This gem simulates named-parameters in Ruby. It's a complement to the common Ruby idiom of using `Hash` args to emulate the use of named parameters. It does this by extending the language with a `has_named_parameters` clause that allows a class to declare the parameters that are acceptable to a method. The `has_named_parameters` dictates how the presence of these parameters are enforced and raises an `ArgumentError` when a method invocation is made that violates the rules for those parameters. See: the [Named Parameter](http://en.wikipedia.org/wiki/named_parameter) article from Wikipedia for more information. Get It ------ You know you want it: gem install named-parameters Use It ------ Make it available everywhere: require 'named-parameters' But if you want to be selective, do: require 'named-parameters/module' Then include the `NamedParameters` module into your class: class YourClass include NamedParameters end Either way, you would now be able to use the **`has_named_parameters`** clause as needed: class YourClass has_named_parameters :your_method, :require => :param def your_method options puts options.inspect end end So when you invoke `your_method`, its parameter requirements will now be enforced: obj = YourClass.new obj.your_method :param => 'Et tu, Babe?' # will spit out: 'Et tu, Babe?' obj.your_method # will raise an ArgumentError because the required :param was not specified Abuse It -------- Declare required parameters: has_named_parameters :send_mail, :required => :to has_named_parameters :send_mail, :required => [ :to, :subject ] Declare optional parameters: has_named_parameters :send_mail, :optional => :subject has_named_parameters :send_mail, :optional => [ :subject, :bcc, :from ] Declare one of a set of parameters as required (ie: require one and only one from a list): has_named_parameters :send_mail, :oneof => [ :signature, :alias ] Declare default values for optional parameters: has_named_parameters :send_mail, :optional => [ :subject, :bcc, { :from => 'yourself@example.org' } ] has_named_parameters :send_mail, :optional => [ :subject, :bcc, [ :from, 'yourself@example.org' ] ] You can also declare default values for `:required` and `:oneof` parameters, but really, that's just silly. With `has_named_parameters`, you can mix-and-match parameter requirements: has_named_parameters :send_mail, :required => [ :to, :subject, ], :oneof => [ :signature, :alias ], :optional => [ :subject, :bcc, [ :from, 'yourself@example.org' ] ] And is applicable to both class and instance methods: require 'named-parameters' class Mailer has_named_parameters :send_mail, :required => [ :to, :subject, ], :oneof => [ :signature, :alias ], :optional => [ :subject, :bcc, [ :from, 'yourself@example.org' ] ] def send_mail options # ... do send mail stuff here ... end has_named_parameters :archive, :optional => [ :method => 'zip' ] def self.archive options = { } # ... do mail archiving stuff here ... end end Shortcuts --------- In addition to the `has_named_parameters` method, `NamedParameters` also comes with two convenience methods for applying a parameter spec for constructors: Use the **`requires`** clause to declare what parameters a class expects when it is instantiated: class GoogleStorage requires [ :'access-key', :'secret-key' ] def initialize options # ... do googly stuff here ... end end Use the **`recognizes`** clause to specify optional parameters for constructors: class GoogleStorage recognizes [ :'group-email', :'apps-domain' ] def initialize options # ... do googly stuff here ... end end You may also specify default values for parameters when using these clauses: class GoogleStorage requires [ :'access-key', :'secret-key' ] recognizes [ [ :'group-email', 'group@example.org' ], [ :'apps-domain', 'example.org' ] ] def initialize options # ... do googly stuff here ... end end Permissive Mode --------------- When a method is declared with `has_named_parameters` that method will only accept keys that were listed as `:required`, `:optional`, or `:oneof` - passing any other key to the `Hash` arg will raise an `ArgumentError` on method call: has_named_parameters :exec, :required => :w, :optional => [ :x, :y ] def exec opts # ... end # the following will raise an ArgumentError since :z was not declared exec :w => 1, :x => 2, :y => 3, :z => 4 But sometimes you need to be able to pass additional keys and you don't know what those keys are. Setting the optional `mode` parameter for `has_named_parameters` to `:permissive` will relax this restriction: has_named_parameters :exec, { :required => :w, :optional => [ :x, :y ] }, :permissive def exec opts # ... end # the following will no longer raise an ArgumentError exec :w => 1, :x => 2, :y => 3, :z => 4 The `:required` and `:oneof` parameters will still be expected: # the following will still raise an ArgumentError since :w is required exec :x => 2, :y => 3, :z => 4 For clarity you should skip the `:optional` parameters list altogether when using the `:permissive` mode. The `requires` and `recognizes` clause for constructors will also accept a `mode` setting: requires [ :x ], :permissive recognizes [ :y, :z ], :permissive And just like the `:optional` parameter list in the `has_named_parameters` clause, when `:permissive` mode is used, it's clearer to omit the `recognizes` clause altogether. How It Works ------------ When the `has_named_parameters` is declared in a class, it instruments the class so that when the method in the declaration is invoked, a validation is performed on the last `Hash` argument that was received by the method. It expects that the last argument is the the `Hash` args representing the named parameters when a method is invoked. If no `Hash` args was supplied then it creates one. So you can mix-and-match argument types in a method, and still declare that it `has_named_parameters`: has_named_parameters :request, :required => :key, :optional => [ :etc, 'howl' ] def request path, options "path: #{path}, options: #{options.inspect}" end # invocation: request "/xxx", :key => '0925' # result: # => path: /xxx, options: {:key => '0925', :etc => 'howl'} Gotchas ------- It has no idea if the last argument really is the last argument. So be careful when you have something similar to the following: has_named_parameters :request, :optional => :key def request path = "/xxx", options = { } "path: #{path}, options: #{options.inspect}" end # invocation: request :key => '0925' # expecting: # => path: /xxx, options: {:key => '0925'} # but actual result is: # => path: {:accesskey => '0925'}, options: {} For the above case, it might be better to refactor: has_named_parameters :request, :optional => [ :key, [ :path, "/xxx" ] ] def request options = { } "path: #{options.delete :path}, options: #{options.inspect}" end # invocation: request :key => '0925' # result: # => path: /xxx, options: {:key => '0925'} # invocation: request # result: # => path: /xxx, options: {} Dependencies ------------ Development: * `yard >= 0` * `rspec >= 1.2.9` Download -------- You can download this project in either [zip](http://github.com/jurisgalang/named-parameters/zipball/master) or [tar](http://github.com/jurisgalang/named-parameters/tarball/master") formats. You can also clone the project with [Git](http://git-scm.com) by running: git clone git://github.com/jurisgalang/named-parameters Note on Patches/Pull Requests ----------------------------- * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. Releases -------- Please read the `RELEASENOTES` file for the details of each release. In general: patch releases will not include breaking changes while releases that bumps the major or minor components of the version string may or may not include breaking changes. Author ------ [Juris Galang](http://github.com/jurisgalang/) License ------- Dual licensed under the MIT or GPL Version 2 licenses. See the MIT-LICENSE and GPL-LICENSE files for details. Copyright (c) 2010 Juris Galang. All Rights Reserved.