When you use attr_accessor :venue, you're essentially doing this.
def venue
  @venue
end 
def venue=(value)
  @venue = value
end 
When you do attr_writer :venue, you're doing
def venue=(val)
  @venue = val 
end 
When you do attr_reader :venue, you're doing
def venue
  @venue 
end
Note that an @instance_variable is nil by default, or if it's uninitialized. To see this in action, type irb in your terminal and press enter. Then, type a single @, and spam a bunch of characters after it. @ausdhyf934234092348 will be nil. 
When you're inside Ticket#initialize, you're acting as an instance of the object, thus self will return the current instance. self.class will return the class, and give you access to any class-level methods.
class Ticket
  VENUE_SIZES = [:small, :large]
  def self.venue_sizes
    VENUE_SIZES
  end 
  def initialize(venue, date)
    # ... 
    self.venue_sizes # NoMethodError
    self.class.venue_sizes # [:small, :large]
  end 
end
Going up the chain, if you're acting as the class and not an instance of a class, calling self.class — or self.class.class if you're acting as an instance, will return Class
attr_accessor is simply syntactical sugar; it was made because of the mundane chore of having to write those getter/setter methods over and over again. There are cooler problems to solve, like writing our own nadnerb_accessor in Ruby (though attr_accessor is implemented in C):
class Ticket
  def self.nadnerb_accessor(*names)
    names.each do |name|
      define_method(name) do
        instance_variable_get(:"@#{name}")
      end
      define_method("#{name}=") do |value|
        instance_variable_set(:"@#{name}", value)
      end
    end
  end
  nadnerb_accessor :venue, :price
  def initialize(venue, price)
    self.venue = venue
    self.price = price
  end
  def inspect
    "#<Ticket @venue=\"#{@venue}\" @price=\"#{@price}\">"
  end
end
ticket = Ticket.new("StackOverflow", "$5")
puts ticket.inspect