Squares [*]
A lightweight ORM backed by any hash-like storage. Hand-crafted from a solid piece of pure aircraft-grade Ruby and drawing distilled awesomeness from atmospheric pollutants, its only dependency is you.
Installation Blah, Blah, Blah
I swear, this part of the README just rolled right out of bundle gem squares
.
Add this line to your application's Gemfile:
gem 'squares'
And then execute:
$ bundle
Or install it yourself as:
$ gem install squares
And yeah, I did enjoy typing bundle gem squares
. It sounds like something to
eat. Now I'm hungry.
Usage
Because you are going to use it.
require 'squares'
Write Models
How come they never write back?
class Person < Squares::Base
properties :real_name, :age
end
You can also provide a default value if you switch to the property
variant:
class Person < Squares::Base
property :eye_color, default: 'brown'
end
Bootstrapping
A funny word for "setup & configure". Bootstrapping. Bootstrapping. See? Funny.
Now you can bootstrap ;)
your model to a hash-like storage object like so:
people_storage = Redis::Namespace.new(
Person.underscore_name,
:redis => $redis_connection )
Person.store = people_storage
Or if you just want to use a plain ole in-memory hash:
tom_sellecks_mustache = {}
Soup.store = tom_sellecks_mustache
Squares actually defaults the store to an empty hash, which means if you're ok
with in-memory, transient storage (e.g. when writing tests, etc.) you don't have
to do any config-- er, bootstrapping ;)
at all!
You can setup a bunch of 'em like this:
[Person, Place, SwampThing].each do |model|
model.store = LevelDB::DB.new("./tmp/#{model.underscore_name}")
end
But it gets even better: the Squares module is an Enumerable
which enumerates all
the model classes (inheritors of Squares::Base
). So you can:
Squares.map &:underscore_name #=> ['person', 'place', 'swamp_thing']
Or better yet:
Squares.each do |model|
model.store = LevelDB::DB.new './tmp/#{model.underscore_name}'
end
Onward To The Fun
Squares does not auto-generate an :id
for each new object --you'll do that
and it will be used as the "key" in the hash storage. In the following example,
we're creating a new Person
and using 'spiderman' as the key:
pete = Person.new('spiderman', real_name: 'Peter Parker', age: 17)
# ^^^ key ^^^ ^^^^^^^^^^^ properties ^^^^^^^^^^^
pete.save
Person.has_key? 'spiderman' #=> true
pete.id #=> 'spiderman'
When we retrieve an object, it returns an instance of that model:
wallcrawler = Person['spiderman']
wallcrawler = Person.find 'spiderman' #=> same, shmame.
wallcrawler.id #=> 'spiderman'
wallcrawler.real_name #=> 'Peter Parker'
wallcrawler.class #=> Person
Of course, for some types of storage, the model object has to be serialized and
de-serialized when it's stored and retrieved. Squares uses Marshal.dump
and
Marshal.restore
to do that. This means that custom marshalling can be added
to your models (see documentation on ruby Marshal).
Even More Fun
You can use the ActiveRecord-esque .where
method with a block to retrieve records
for which the block returns true:
Person.where { |p| p.left_handed == true } #=> all the lefties
The .where
method is actually just an alias of .select
...which means, yeah!
Squares are enumerable, yay!
Person.map(&:name) #=> an array containing all the names
What It Doesn't Do
Much like Wolverine, Squares doesn't do relationships. You'll have to maintain those in your code. If you have an issue with that, leave me an issue, and I'll think about what that might mean.
Squares neither knows nor cares about the type or contents of your model instance's properties. This has consequences.
First, anything you stash had darn well better be marshal-able, or there will be blood on the roller-rink. Or at least errors. Yeah, I've made sure there won't be blood (you're welcome), but watch out for errors. If you run into problems, refer to the documentation on ruby Marshal.
Second, there is no magic-fu for stuff like generating question methods
for boolean properties. For example, it doesn't make a #left_handed?
method
out of your property :left_handed
). But hey, you know what you can
do? Behold:
class Person
property :awesome?, default: true #=> What?! is that a "?"
end
Ok, don't interrupt me, I'm selling here...
you = Person.new
you.awesome? #=> true
Of course, Squares doesn't mind how you use #awesome?
and the corresponding #awesome=
methods:
you.awesome = 'yak hair'
you.awesome? #=> 'yak hair'
or
you.awesome = nil
you.awesome? #=> nil
But hey, who cares, as long as yak hair is truthy?
Contributing
- Fork it ( https://github.com/joelhelbling/squares/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