README.md in toystore-0.9.0 vs README.md in toystore-0.10.0
- old
+ new
@@ -1,9 +1,220 @@
-# Toystore
+# Toystore [![Build Status](https://secure.travis-ci.org/jnunemaker/toystore.png)](http://travis-ci.org/jnunemaker/toystore)
An object mapper for any [adapter](https://github.com/jnunemaker/adapter) that can read, write, delete, and clear data.
-See [examples/](https://github.com/jnunemaker/toystore/tree/master/examples) for potential direction.
+## Examples
+
+The project comes with two main includes that you can use -- Toy::Object and Toy::Store.
+
+**Toy::Object** comes with all the goods you need for plain old ruby objects -- attributes, dirty attribute tracking, equality, inheritance, serialization, cloning, logging and pretty inspecting.
+
+**Toy::Store** includes Toy::Object and adds persistence through adapters, mass assignment, querying, callbacks, validations and a few simple associations (lists and references).
+
+### Toy::Object
+
+First, join me in a whirlwind tour of Toy::Object.
+
+```ruby
+class Person
+ include Toy::Object
+
+ attribute :name, String
+ attribute :age, Integer
+end
+
+# Pretty class inspecting
+puts Person.inspect
+
+john = Person.new(:name => 'John', :age => 30)
+steve = Person.new(:name => 'Steve', :age => 31)
+
+# Pretty inspecting
+puts john.inspect
+
+# Attribute dirty tracking
+john.name = 'NEW NAME!'
+puts john.changes.inspect # {"name"=>["John", "NEW NAME!"], "age"=>[nil, 30]}
+puts john.name_changed?.inspect # true
+
+# Equality goodies
+puts john.eql?(john) # true
+puts john.eql?(steve) # false
+puts john == john # true
+puts john == steve # false
+
+# Cloning
+puts john.clone.inspect
+
+# Inheritance
+class AwesomePerson < Person
+end
+
+puts Person.attributes.keys.sort.inspect # ["age", "id", "name"]
+puts AwesomePerson.attributes.keys.sort.inspect # ["age", "id", "name", "type"]
+
+# Serialization
+puts john.to_json
+puts john.to_xml
+```
+
+Ok, that was definitely awesome. Please continue on your personal journey to a blown mind (very similar to a beautiful mind).
+
+### Toy::Store
+
+Toy::Store is a unique bird that builds on top of Toy::Object. Below is a quick sample of what it can do.
+
+```ruby
+class Person
+ include Toy::Store
+
+ attribute :name, String
+ attribute :age, Integer, :default => 0
+end
+
+# Persistence
+john = Person.create(:name => 'John', :age => 30)
+pp john
+pp john.persisted?
+
+# Mass Assignment Security
+Person.attribute :role, String, :default => 'guest'
+Person.attr_accessible :name, :age
+
+person = Person.new(:name => 'Hacker', :age => 13, :role => 'admin')
+pp person.role # "guest"
+
+# Querying
+pp Person.get(john.id)
+pp Person.get_multi(john.id)
+pp Person.get('NOT HERE') # nil
+pp Person.get_or_new('NOT HERE') # new person with id of 'NOT HERE'
+
+begin
+ Person.get!('NOT HERE')
+rescue Toy::NotFound
+ puts "Could not find person with id of 'NOT HERE'"
+end
+
+# Reloading
+pp john.reload
+
+# Callbacks
+class Person
+ def add_fifty_to_age
+ self.age += 50
+ end
+end
+
+class Person
+ before_create :add_fifty_to_age
+end
+
+pp Person.create(:age => 10).age # 60
+
+# Validations
+class Person
+ validates_presence_of :name
+end
+
+person = Person.new
+pp person.valid? # false
+pp person.errors[:name] # ["can't be blank"]
+
+# Lists (array key stored as attribute)
+class Skill
+ include Toy::Store
+
+ attribute :name, String
+ attribute :truth, Boolean
+end
+
+class Person
+ list :skills, Skill
+end
+
+john.skills = [Skill.create(:name => 'Programming', :truth => true)]
+john.skills << Skill.create(:name => 'Mechanic', :truth => false)
+
+pp john.skills.map(&:id) == john.skill_ids # true
+
+# References (think foreign keyish)
+class Person
+ reference :mom, Person
+end
+
+mom = Person.create(:name => 'Mum')
+john.mom = mom
+john.save
+pp john.reload.mom_id == mom.id # true
+
+# Identity Map
+Toy::IdentityMap.use do
+ frank = Person.create(:name => 'Frank')
+
+ pp Person.get(frank.id).equal?(frank) # true
+ pp Person.get(frank.id).object_id == frank.object_id # true
+end
+
+# Or you can turn it on globally
+Toy::IdentityMap.enabled = true
+frank = Person.create(:name => 'Frank')
+
+pp Person.get(frank.id).equal?(frank) # true
+pp Person.get(frank.id).object_id == frank.object_id # true
+
+# All persistence runs through an adapter.
+# All of the above examples used the default in-memory adapter.
+# Looks something like this:
+Person.adapter :memory, {}
+
+puts "Adapter: #{Person.adapter.inspect}"
+
+# You can make a new adapter to your awesome new/old data store
+# Always use #key_for, #encode, and #decode. Feel free to override
+# them if you like, but always use them. Default encode/decode is
+# most likely marshaling, but you can use anything.
+Adapter.define(:append_only_array) do
+ def read(key)
+ if (record = client.reverse.detect { |row| row[0] == key_for(key) })
+ decode(record)
+ end
+ end
+
+ def write(key, value)
+ key = key_for(key)
+ value = encode(value)
+ client << [key, value]
+ value
+ end
+
+ def delete(key)
+ key = key_for(key)
+ client.delete_if { |row| row[0] == key }
+ end
+
+ def clear
+ client.clear
+ end
+end
+
+client = []
+Person.adapter :append_only_array, client
+
+pp "Client: #{Person.adapter.client.equal?(client)}"
+
+person = Person.create(:name => 'Phil', :age => 55)
+person.age = 56
+person.save
+
+pp client
+
+pp Person.get(person.id) # Phil with age 56
+```
+
+If that doesn't excite you, nothing will. At this point, you are probably wishing for more.
+
+Luckily, there is an entire directory full of [examples](https://github.com/jnunemaker/toystore/tree/master/examples) and I created a few power user guides, which I will kindly link next.
## ToyStore Power User Guides
* [Wiki Home](https://github.com/jnunemaker/toystore/wiki)
* [Identity](https://github.com/jnunemaker/toystore/wiki/Identity)