Variableobjects for class and instance variables
Ruby already has Method objects, why not Variable objects as well?
Why?
Some methods already exist for interacting with class and instance variables:
Module#class_variable_defined?Module#class_variable_getModule#class_variable_setObject#instance_variable_defined?Object#instance_variable_getObject#instance_variable_set
But notice that these all share a common prefix - instance_variable_ or class_variable_.
This feels a little smelly, let's try to DRY it up with some Variable objects!
Installation
gem install variables
Requirements
Ruby 1.8.7+
Usage
Let's experiment with a simple User class.
class User
def initialize(name)
@name = name
end
endObjects can have any number of instance variables.
user = User.new('Bob') #=> #<User:0x007f8f6a84aa98>
user.instance_variable_get('@name') #=> "Bob"Similar to Object#method, the instance_variable method returns a Variable object.
name = user.instance_variable(:name) #=> #<InstanceVariable: #<User>@name>But unlike Object#method, this method does not require a variable to actually be defined.
undefined = user.instance_variable(:undefined) #=> #<InstanceVariable: #<User>@undefined>We can check if a variable is defined by using the defined? method.
name.defined? #=> true
undefined.defined? #=> falseOnce we have a Variable object, we can get its value.
name.get #=> "Bob"
undefined.get #=> nilSimilar to Hash#fetch, the fetch method raises an exception if the variable is undefined.
name.fetch #=> "Bob"
undefined.fetch #=> Variables::UndefinedVariable - undefined variable "undefined"The fetch method optionally accepts a default value to return if the variable is undefined.
name.fetch(:default) #=> "Bob"
undefined.fetch(:default) #=> :defaultDefault values can also be defined with a block which is yielded with the Variable name.
name.fetch { |name| "#{name}-default" } #=> "Bob"
undefined.fetch { |name| "#{name}-default" } #=> "@undefined-default"The Object#instance_variable_fetch method allows us to fetch a variable's value by name.
name.fetch #=> "Bob"
user.instance_variable_fetch(:name) #=> "Bob"We can update a Variable value by using the set method.
name.set('Steve') #=> "Steve"
user.instance_variable_get('@name') #=> "Steve"The replace method is similar to set, but it returns the old value instead of the new value.
name.replace('Bob') #=> "Steve"
user.instance_variable_get('@name') #=> "Bob"We can even temporarily replace a value for the duration of a block.
user.instance_variable_get('@name') #=> "Bob"
name.replace('Steve') do
user.instance_variable_get('@name') #=> "Steve"
end
user.instance_variable_get('@name') #=> "Bob"Note that when using the block form of replace, the last expression of the block is returned.
name.replace('Steve') { 1 + 1 } #=> 2The Object#instance_variable_replace method allows us to replace a variable's value by name.
user.instance_variable_get('@name') #=> "Bob"
user.instance_variable_replace(:name, 'Steve') do
user.instance_variable_get('@name') #=> "Steve"
end
user.instance_variable_get('@name') #=> "Bob"The instance_variable_replace method also accepts a hash of variables to replace.
user.instance_variable_get('@name') #=> "Bob"
user.instance_variable_get('@test') #=> nil
user.instance_variable_replace(name: 'Steve', test: 'example') do
user.instance_variable_get('@name') #=> "Steve"
user.instance_variable_get('@test') #=> "example"
end
user.instance_variable_get('@name') #=> "Bob"
user.instance_variable_get('@test') #=> nilEverything that we can do with instance variables can be done with class variables as well!
example = User.class_variable(:example) #=> #<ClassVariable: User@@name>
example.defined? #=> false
example.set('testing') #=> "testing"
User.class_variable_get('@@example') #=> "testing"API
Module#class_variableModule#class_variable_fetchModule#class_variable_replaceObject#instance_variableObject#instance_variable_fetchObject#instance_variable_replaceVariable#defined?Variable#fetchVariable#getVariable#nameVariable#ownerVariable#replaceVariable#set
Testing
bundle exec rspec
Contributing
- 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.
License
MIT - Copyright © 2015 Sean Huber
