`Variable` objects for class and instance variables
Ruby
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
lib
spec
.gitignore
.rspec
.travis.yml
Gemfile
Gemfile.lock
LICENSE
README.md
variables.gemspec

README.md

Sean Huber variables

Build Status Code Climate Coverage Gem Version

Variable objects 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_get
  • Module#class_variable_set
  • Object#instance_variable_defined?
  • Object#instance_variable_get
  • Object#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
end

Objects 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? #=> false

Once we have a Variable object, we can get its value.

name.get      #=> "Bob"
undefined.get #=> nil

Similar 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) #=> :default

Default 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 } #=> 2

The 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') #=> nil

Everything 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

YARD Documentation

  • Module#class_variable
  • Module#class_variable_fetch
  • Module#class_variable_replace
  • Object#instance_variable
  • Object#instance_variable_fetch
  • Object#instance_variable_replace
  • Variable#defined?
  • Variable#fetch
  • Variable#get
  • Variable#name
  • Variable#owner
  • Variable#replace
  • Variable#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