Search code examples
ruby-on-railsrubysymbolshash

How are symbols used to identify arguments in ruby methods


I am learning rails and going back to ruby to understand how methods in rails (and ruby really work). When I see method calls like:

validates :first_name, :presence => true

I get confused. How do you write methods in ruby that accept symbols or hashes. The source code for the validates method is confusing too. Could someone please simplify this topic of using symbols as arguments in ruby class and instance methods for me?

UPDATE:

Good one @Dave! But What I was trying out was something like:

def full_name (:first_name, :last_name)
  @first_name = :first_name
  @last_name = :last_name
  p "#{@first_name} #{last_name}"
end

full_name("Breta", "Von Sustern")

Which obviously raises errors. I am trying to understand: Why is passing symbols like this as arguments wrong if symbols are just like any other value?


Solution

  • Symbols and hashes are values like any other, and can be passed like any other value type.

    Recall that ActiveRecord models accept a hash as an argument; it ends up being similar to this (it's not this simple, but it's the same idea in the end):

    class User
      attr_accessor :fname, :lname
    
      def initialize(args)
        @fname = args[:fname] if args[:fname]
        @lname = args[:lname] if args[:lname]
      end
    end
    
    u = User.new(:fname => 'Joe', :lname => 'Hacker')
    

    This takes advantage of not having to put the hash in curly-brackets {} unless you need to disambiguate parameters (and there's a block parsing issue as well when you skip the parens).

    Similarly:

    class TestItOut
      attr_accessor :field_name, :validations
    
      def initialize(field_name, validations)
        @field_name = field_name
        @validations = validations
      end
    
      def show_validations
        puts "Validating field '#{field_name}' with:"
        validations.each do |type, args|
          puts "  validator '#{type}' with args '#{args}'"
        end
      end
    end
    
    t = TestItOut.new(:name, presence: true, length: { min: 2, max: 10 })
    t.show_validations
    

    This outputs:

    Validating field 'name' with:
      validator 'presence' with args 'true'
      validator 'length' with args '{min: 2, max: 10}'
    

    From there you can start to see how things like this work.