Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/jnunemaker/toystore
Object mapper for anything that can read, write and delete data
https://github.com/jnunemaker/toystore
Last synced: about 2 months ago
JSON representation
Object mapper for anything that can read, write and delete data
- Host: GitHub
- URL: https://github.com/jnunemaker/toystore
- Owner: jnunemaker
- License: bsd-3-clause
- Fork: true (zerohistory/toystore)
- Created: 2011-11-16T16:54:19.000Z (almost 13 years ago)
- Default Branch: master
- Last Pushed: 2013-04-27T14:22:35.000Z (over 11 years ago)
- Last Synced: 2024-05-11T23:21:20.524Z (5 months ago)
- Language: Ruby
- Homepage: http://jnunemaker.github.com/toystore/
- Size: 1.94 MB
- Stars: 126
- Watchers: 11
- Forks: 12
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: Changelog.md
- License: LICENSE
Awesome Lists containing this project
README
# Toystore
An object mapper for any [adapter](https://github.com/jnunemaker/adapter) that can read, write, delete, and clear data.
## 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 identity, persistence and querying through adapters, mass assignment, 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::Objectattribute :name, String
attribute :age, Integer
end# Pretty class inspecting
pp Personjohn = Person.new(:name => 'John', :age => 30)
steve = Person.new(:name => 'Steve', :age => 31)# Pretty inspecting
pp john# Attribute dirty tracking
john.name = 'NEW NAME!'
pp john.changes # {"name"=>["John", "NEW NAME!"], "age"=>[nil, 30]}
pp john.name_changed? # true# Equality goodies
pp john.eql?(john) # true
pp john.eql?(steve) # false
pp john == john # true
pp john == steve # false# Cloning
pp john.clone# Inheritance
class AwesomePerson < Person
endpp Person.attributes.keys.sort # ["age", "name"]
pp AwesomePerson.attributes.keys.sort # ["age", "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::Storeattribute :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, :ageperson = Person.new(:name => 'Hacker', :age => 13, :role => 'admin')
pp person.role # "guest"# Querying
pp Person.read(john.id)
pp Person.read_multiple([john.id])
pp Person.read('NOT HERE') # nilbegin
Person.read!('NOT HERE')
rescue Toy::NotFound
puts "Could not find person with id of 'NOT HERE'"
end# Reloading
pp john.reload# Callbacks
class Person
before_create :add_fifty_to_agedef add_fifty_to_age
self.age += 50
end
endpp Person.create(:age => 10).age # 60
# Validations
class Person
validates_presence_of :name
endperson = Person.new
pp person.valid? # false
pp person.errors[:name] # ["can't be blank"]# Lists (array key stored as attribute)
class Skill
include Toy::Storeattribute :name, String
attribute :truth, Boolean
endclass Person
list :skills, Skill
endjohn.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
endmom = 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.read(frank.id).equal?(frank) # true
pp Person.read(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.read(frank.id).equal?(frank) # true
pp Person.read(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
Adapter.define(:append_only_array) do
def read(key)
if (record = client.reverse.detect { |row| row[0] == key })
record
end
enddef write(key, value)
client << [key, value]
value
enddef delete(key)
client.delete_if { |row| row[0] == key }
enddef clear
client.clear
end
endclient = []
Person.adapter :append_only_array, clientpp "Client: #{Person.adapter.client.equal?(client)}"
person = Person.create(:name => 'Phil', :age => 55)
person.age = 56
person.savepp client
pp Person.read(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.
## Instrumentation
ToyStore comes with a log subscriber and automatic metriks instrumentation. By
default these work with ActiveSupport::Notifications, but only require the
pieces of ActiveSupport that are needed and only do so if you actually attempt
to require the instrumentation files listed below.To use the log subscriber:
```ruby
# Gemfile
gem 'activesupport'# config/initializers/toystore.rb (or wherever you want it)
require 'toy/instrumentation/log_subscriber'
```To use the metriks instrumentation:
```ruby
# Gemfile
gem 'activesupport'
gem 'metriks'# config/initializers/toystore.rb (or wherever you want it)
require 'toy/instrumentation/metriks'
```## ToyStore Power User Guides
* [Wiki Home](https://github.com/jnunemaker/toystore/wiki)
* [Identity](https://github.com/jnunemaker/toystore/wiki/Identity)
* [Types](https://github.com/jnunemaker/toystore/wiki/Types)
* [Exceptions](https://github.com/jnunemaker/toystore/wiki/Exceptions)## Changelog
As of 0.8.3, I started keeping a [changelog](https://github.com/jnunemaker/toystore/blob/master/Changelog.md). All significant updates will be summarized there.
## Compatibility
* Rails 3.0.*, 3.1.*, 3.2.*, Sinatra, etc. No Rails 2 (because it uses Active Model).
* Ruby 1.9.3 only## Mailing List
https://groups.google.com/forum/#!forum/toystoreadapter
## Contributing
* Fork the project.
* Make your feature addition or bug fix in a topic branch.
* Add tests for it. This is important so we don't break it in a future version unintentionally.
* Commit, do not mess with rakefile, version, or changelog. (if you want to have your own version, that is fine, but bump version in a commit by itself so we can ignore when we pull)
* Send a pull request.