Reputation: 311
Suppose I had the following code in my routes.rb file.
get '/dashboard', to: 'dashboard#index', :as => 'dashboard'
I know what's going on (a public url being mapped to a specific controller/view file which will process and return the request). That is not what I am confused about.
As a Ruby beginner, I'm confused about what Ruby code convention this is. It doesn't look like the correct way to declare a variable or call a method? What exactly is going on and is this some sort of shorthand form to call a method?
get("/dashboard", "dashboard#index", "dashboard")
Upvotes: 2
Views: 302
Reputation: 369438
get '/dashboard', to: 'dashboard#index', :as => 'dashboard'
This is just a normal message send (what you call a "method call", but note that messages and methods are different things in Ruby, and the metaphor of sending a message is really important).
In Ruby, you are allowed to leave out the parentheses around an argument list. So, this is the same as this:
get('/dashboard', to: 'dashboard#index', :as => 'dashboard')
You are also allowed to leave out the receiver of the message send, in which case it is implied to be self
, so this is the same as this:
self.get('/dashboard', to: 'dashboard#index', :as => 'dashboard')
If the last argument to a message send is a Hash
literal, you are allowed to leave out the curly braces, so this is the same as this:
self.get('/dashboard', { to: 'dashboard#index', :as => 'dashboard' })
If the key in a Hash
literal is a Symbol
, you are allowed to write key: value
instead of :key => value
, so this is the same as this:
self.get('/dashboard', { :to => 'dashboard#index', :as => 'dashboard' })
Now, doesn't that look just like a boring old message send?
Most of these rules are there to allow you to write code that looks more natural. For example, things that would be keywords in other languages, are just methods in Ruby, like attr_reader
, attr_writer
, attr_accessor
, require
, private
, public
, protected
, etc. It would look weird, if you were required to use parentheses and receivers there:
self.protected()
def foo() end
# instead of
protected
def foo() end
# or
self.private(def foo() end)
# instead of
private def foo() end
The rule that you can leave out the curly braces around a trailing Hash
literal was introduced, so that you could "fake" keyword arguments:
def route(path, options = {}) end
route(:index, :from => :here, :to => :there)
Even more so with the new-style symbol keys:
route(:index, from: :here, to: :there)
Note that as of Ruby 2.0, Ruby also has proper keyword arguments, which for backwards-compatibility look exactly like a trailing Hash
:
def route(path, from:, to:, with: nil) end
route(:index, from: :here, to: :there) # this line same as above
Upvotes: 2
Reputation: 20263
You're very close.
Yes, get
is a method that is being called. In ruby, you can omit the parentheses. So,
get '/dashboard', to: 'dashboard#index', :as => 'dashboard'
Is the same as:
get("/dashboard", to: "dashboard#index", :as => "dashboard")
Where to:
and :as
are named parameters which are received by get
as a hash
.
I think this method is defined in action_dispatch/routing/mapper.rb
:
module ActionDispatch
module Routing
class Mapper
module HttpHelpers
# Define a route that only recognizes HTTP GET.
# For supported arguments, see match[rdoc-ref:Base#match]
#
# get 'bacon', to: 'food#bacon'
def get(*args, &block)
map_method(:get, args, &block)
end
...
end
end
end
end
So, you can see that to: "dashboard#index", :as => "dashboard"
are received by *args
and the splat operator
allows for a variable length set of arguments.
Upvotes: 2
Reputation: 54882
This is a common way to allow options to be passed to a method. Like the following:
def do_something(subject, options = {})
subject.perform_something
subject.perform_caching if options[:cache]
subject.send_report unless options[:skip_reporting]
# etc.
end
So you can call:
something(some_object)
something(some_object, cache: true)
something(some_object, skip_reporting: true, cache: true)
You can even set up defaults, like this:
def do_something_else(subject, options = {})
options = { cache: true, skip_reporting: false }.merge(options)
subject.perform_something
subject.perform_caching if options[:cache]
subject.send_report unless options[:skip_reporting]
end
So that by default if no cache
options if given, it will be true
.
Upvotes: 3