StoreComplex
¶ ↑
Stores complex data that includes Arrays and Hashes (possibly nested) in an attribute inside hstore field. The most typical usage scenario is storing arrays in hstore, but it can handle more complex cases.
Installation¶ ↑
Add this line to your application's Gemfile:
gem 'store_complex'
And then execute:
$ bundle
Or install it yourself as:
$ gem install store_complex
Usage¶ ↑
Reminder: What Rails already has¶ ↑
ActiveRecord in Rails 4 already provides methods for dealing with individual attributes inside PostgresSQL hstore and json fields.
Let's consider an example. There is a database table (and a corresponding ActiveRecord model) that describes some blog authors. Among other data, it has an hstore field called properties
that captures various optional bits of information about an author: email, facebook account, personal blog address, etc.
In Rails 4 you can get convenient access to the individual attributes inside properties
using {store_accessor
}:
class Author < ActiveRecord::Base store_accessor :properties, :email, :facebook, :blog end author = Author.new(name:'Uber Guru') author.properties # => nil author.email = 'somebody@example.org' # will store this email in properties author.properties # => {"email"=>"somebody@example.org"} author.save! author = Author.find_by_name('Uber Guru') author.properties # => {"email"=>"somebody@example.org"} author.email # => 'somebody@example.org'
But what if we want to let tha authors specify more than a single email. OK, simple, the email
property will now be an array. Not, so fast…
author = Author.new(name:'Uber Guru') author.properties # => nil author.email = ['somebody@example.org','somebody@example.com'] author.properties # => {"email"=>["somebody@example.org", "somebody@example.com"]} author.save! author = Author.find_by_name('Uber Guru') author.properties # => {"email"=>"[\"somebody@example.org\", \"somebody@example.com\"]"} author.email # => "[\"somebody@example.org\", \"somebody@example.com\"]" # Oh-oh! :(
And instead of an array we got back a string representation of that array.
So, can we do something about it? Yes, meet store_complex
…
How to use store_complex
¶ ↑
In your model, use store_complex
in place of store_accessor
. That's it!
class Author < ActiveRecord::Base store_complex :properties, :email end author = Author.new(name:'Uber Guru') author.properties # => nil author.email = ['somebody@example.org','somebody@example.com'] author.properties # => {"email"=>["somebody@example.org", "somebody@example.com"]} author.save! author = Author.find_by_name('Uber Guru') author.properties # => {"email"=>"[\"somebody@example.org\", \"somebody@example.com\"]"} author.email # => ["somebody@example.org","somebody@example.com"] # Success! :)
What store_complex
does¶ ↑
store_complex
allows to store arrays and hashes in hstore attributes. Those arrays and hashes can contain as their values or keys (for hashes):
-
strings, numbers, booleans, nils - will be stored 'as is';
-
other arrays and hashes - yes, nesting is possible;
-
symbols - will be converted to strings;
-
other data types if they can be converted to strings using
String(value)
- and yes, they will be converted to strings.
One important note: If you store something but array or hash, it will be wrapped into an array. store_complex
is not for simple data types, use 'store_accessor' for that. The only exception is nil
, which deletes the attribute from hstore. And if there is no attribute in hstore, the store_complex
accessor will return an empty array: []
.
Example:
class Author < ActiveRecord::Base store_complex :properties, :email end author = Author.new(name:'Uber Guru') author.properties # => nil author.email # => [] author.email = 'somebody@example.org' # author.email # => ["somebody@example.org"] author.email = nil # author.email # => []
Another awesome feature of store_complex
is that it tracks not only assignments to the “complex” attribute, but also all operations on the hash or array (inlcuding those nested within!) that change the object (all those sort!
and delete
calls). Thanks to observable_object gem (and me as its author ;-) ) for that awesome behavior.
To get you excited, here is an example below:
class Author < ActiveRecord::Base store_complex :properties, :email end author = Author.new(name:'Uber Guru') author.properties # => nil author.email # => [] author.email = { "somebody@example.org" => "personal", "somebody@example.com" => "work", "somebody@business.nowhere" => "work" } author.email.delete_if { |k,v| v == 'work' } # delete all work emails author.save! author = Author.find_by_name('Uber Guru') author.email # => {"somebody@example.org"=>"personal"} # Perfect!
Versioning¶ ↑
Semantic versioning (semver.org/spec/v2.0.0.html) is used.
For a version number MAJOR.MINOR.PATCH, unless MAJOR is 0:
-
MAJOR version is incremented when incompatible API changes are made,
-
MINOR version is incremented when functionality is added in a backwards-compatible manner,
-
PATCH version is incremented when backwards-compatible bug fixes are made.
Major version “zero” (0.y.z) is for initial development. Anything may change at any time. The public API should not be considered stable.
Dependencies¶ ↑
-
Ruby >= 2.1
-
Rails >= 4.0
-
observable_object
Contributing¶ ↑
-
Fork it ( github.com/moonfly/store_complex/fork )
-
Create your feature branch (
git checkout -b my-new-feature
) -
Commit your changes (
git commit -am 'Add some feature'
) -
Push to the branch (
git push origin my-new-feature
) -
Create a new Pull Request