Skip to content

Commit

Permalink
refactored properties into their own classes, belongs_to property wor…
Browse files Browse the repository at this point in the history
…king, changed generated json to look much nicer
  • Loading branch information
Alexander Lang committed Oct 5, 2008
1 parent d5d59e1 commit 45cd49a
Show file tree
Hide file tree
Showing 19 changed files with 291 additions and 82 deletions.
Empty file.
28 changes: 14 additions & 14 deletions lib/couch_potatoe/persistence.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require 'couchrest'
require 'validatable'
require File.dirname(__FILE__) + '/persistence/inline_collection'
require File.dirname(__FILE__) + '/persistence/lazy_collection'
require File.dirname(__FILE__) + '/persistence/external_collection'
require File.dirname(__FILE__) + '/persistence/properties'
require File.dirname(__FILE__) + '/persistence/callbacks'
require File.dirname(__FILE__) + '/persistence/json'
Expand Down Expand Up @@ -50,13 +50,15 @@ def destroy
def reload
raise(UnsavedRecordError.new) unless _id
reloaded = self.class.find _id
self.class.properties.each do |name|
self.send "#{name}=", reloaded.send(name)
self.class.properties.each do |property|
json = {}
property.serialize(json, reloaded)
property.build self, json
end
end

def new_record?
_rev.nil?
_id.nil?
end

def to_param
Expand All @@ -68,7 +70,7 @@ def [](name)
end

def ==(other)
other.class == self.class && self.class.properties.map{|name| self.send(name)} == self.class.properties.map{|name| other.send(name)}
other.class == self.class && self.class.property_names.map{|name| self.send(name)} == self.class.property_names.map{|name| other.send(name)}
end

private
Expand All @@ -81,9 +83,9 @@ def create_record
run_callbacks :before_create
self.created_at = Time.now
self.updated_at = Time.now
record = self.class.db.save(self)
self._id = record['id']
self._rev = record['rev']
document = self.class.db.save(self)
self._id = document['id']
self._rev = document['rev']
save_dependent_objects
run_callbacks :after_save
run_callbacks :after_create
Expand All @@ -106,24 +108,22 @@ def update_record
end

def save_dependent_objects
self.class.properties.each do |name|
property = self.send name
property.owner_id = self._id if property.respond_to?(:owner_id=)
property.save if property.respond_to?(:save)
self.class.properties.each do |property|
property.save(self)
end
end

module ClassMethods

def create!(attributes)
def create!(attributes = {})
instance = self.new attributes
instance.save!
instance
end

def find(id)
begin
db.get(id)
self.json_create db.get(id)
rescue(RestClient::ResourceNotFound)
nil
end
Expand Down
39 changes: 39 additions & 0 deletions lib/couch_potatoe/persistence/belongs_to_property.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module CouchPotatoe
module Persistence
class BelongsToProperty
attr_accessor :name

def initialize(owner_clazz, name)
self.name = name
accessors = <<-ACCESSORS
def #{name}
@#{name} || @#{name}_id ? #{item_class_name}.find(@#{name}_id) : nil
end
def #{name}=(value)
@#{name} = value
@#{name}_id = value.id
end
ACCESSORS
owner_clazz.class_eval accessors
owner_clazz.send :attr_accessor, "#{name}_id"
end

def save(object)

end

def build(object, json)
object.send "#{name}_id=", json["#{name}_id"]
end

def serialize(json, object)
json["#{name}_id"] = object.send("#{name}_id") if object.send("#{name}_id")
end

def item_class_name
@name.to_s.singularize.capitalize
end
end
end
end
41 changes: 41 additions & 0 deletions lib/couch_potatoe/persistence/external_collection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require File.dirname(__FILE__) + '/collection'
require File.dirname(__FILE__) + '/finder'

module CouchPotatoe
module Persistence
class ExternalCollection < Collection

attr_accessor :item_ids, :owner_id

def initialize(item_class, owner_id_attribute_name)
super item_class
@items = nil
@owner_id_attribute_name = owner_id_attribute_name
end

def build(attributes)
item = @item_class.new(attributes)
self.<< item
item.position = self.size if item.respond_to?(:position=)
item.send "#{@owner_id_attribute_name}=", owner_id
item
end

def items
if @items.nil?
@items = Finder.new.find @item_class, @owner_id_attribute_name => owner_id
end
@items
end

def save
items.each do |item|
item.send "#{@owner_id_attribute_name}=", owner_id
item.save
end
end

end
end
end

45 changes: 45 additions & 0 deletions lib/couch_potatoe/persistence/external_has_many_property.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module CouchPotatoe
module Persistence
class ExternalHasManyProperty
attr_accessor :name
def initialize(owner_clazz, name)
@name, @owner_clazz = name, owner_clazz
getter = <<-ACCESORS
def #{name}
@#{name} ||= CouchPotatoe::Persistence::ExternalCollection.new(#{item_class_name}, :#{owner_clazz.name.underscore}_id)
end
def #{name}=(items)
items.each do |item|
#{name} << item
end
end
ACCESORS
owner_clazz.class_eval getter
end

def save(object)
object.send(name).each do |item|
item.send("#{@owner_clazz.name.underscore}_id=", object.id)
item.save
end
end

def build(object, json)
collection = ExternalCollection.new(item_class_name.constantize, "#{@owner_clazz.name.underscore}_id")
collection.owner_id = object.id
object.send "#{name}=", collection
end

def serialize(json, object)
nil
end

private

def item_class_name
@name.to_s.singularize.capitalize
end
end
end
end
5 changes: 3 additions & 2 deletions lib/couch_potatoe/persistence/finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,19 @@ def create_view(params)
"_id" => "_design/#{params[:design_document]}",
:views => {
params[:view] => {
:map => "function(doc){emit(doc.data['#{params[:search_field]}'], doc);}"
:map => "function(doc){if(doc.ruby_class == '#{params[:class]}') {emit(doc['#{params[:search_field]}'], doc)}}"
}
}
})
end

def query_view(params)
db.view(params[:view_url], :key => params[:search_value])['rows'].map{|doc| doc['value']}
db.view(params[:view_url], :key => params[:search_value])['rows'].map{|doc| doc['value']}.map{|json| params[:class].json_create json}
end

def view_parameters(clazz, options)
{
:class => clazz,
:design_document => clazz.name.underscore,
:search_field => options.keys.first,
:search_value => options.values.first,
Expand Down
18 changes: 1 addition & 17 deletions lib/couch_potatoe/persistence/inline_collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,7 @@ module Persistence
class InlineCollection < Collection

def to_json(*args)
{
'json_class' => self.class.name,
'data' => {
'item_class' => @item_class.name,
'items' => @items
}

}.to_json(*args)
end

def self.json_create(json)
collection = self.new json['data']['item_class'].constantize
json['data']['items'].each do |item|
item.position = collection.size if item.respond_to?(:position=)
collection << item
end
collection
@items.to_json(*args)
end

end
Expand Down
34 changes: 34 additions & 0 deletions lib/couch_potatoe/persistence/inline_has_many_property.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
class InlineHasManyProperty
attr_accessor :name

def initialize(owner_clazz, name)
@name = name
getter = <<-GETTER
def #{name}
@#{name} ||= CouchPotatoe::Persistence::InlineCollection.new(#{item_class_name})
end
GETTER
owner_clazz.class_eval getter
end

def build(object, json)
json[name.to_s].each do |item|
item.delete 'ruby_class'
object.send("#{name}").build item
end
end

def save(object)

end

def serialize(json, object)
json[name.to_s] = object.send(name)
end

private

def item_class_name
@name.to_s.singularize.capitalize
end
end
30 changes: 16 additions & 14 deletions lib/couch_potatoe/persistence/json.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,10 @@ def self.included(base)
end

def to_json(*args)
{
'json_class' => self.class.name,
'data' => (self.class.properties + [:created_at, :updated_at]).inject({}) do |props, name|
props[name] = self.send(name) if self.send(name)
props
end
}.merge(id_and_rev_json).to_json(*args)
(self.class.properties).inject({}) do |props, property|
property.serialize(props, self)
props
end.merge('ruby_class' => self.class.name).merge(id_and_rev_json).merge(timestamps_json).to_json(*args)
end

private
Expand All @@ -24,18 +21,23 @@ def id_and_rev_json
end
end

def timestamps_json
[:created_at, :updated_at].inject({}) do |hash, key|
hash[key] = self.send(key).to_s unless self.send(key).nil?
hash
end
end

module ClassMethods
def json_create(json)
instance = self.new
properties.each do |name|
item = json['data'][name.to_s]
item.owner_id = json['_id'] if item.respond_to?('owner_id=')
instance.send "#{name}=", item
end
instance.created_at = json['data']['created_at'] if json['data']['created_at']
instance.updated_at = json['data']['updated_at'] if json['data']['updated_at']
instance.created_at = Time.parse(json['created_at'])
instance.updated_at = Time.parse(json['updated_at'])
instance._id = json['_id']
instance._rev = json['_rev']
properties.each do |property|
property.build(instance, json)
end
instance
end
end
Expand Down
37 changes: 15 additions & 22 deletions lib/couch_potatoe/persistence/properties.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
require File.dirname(__FILE__) + '/simple_property'
require File.dirname(__FILE__) + '/belongs_to_property'
require File.dirname(__FILE__) + '/inline_has_many_property'
require File.dirname(__FILE__) + '/external_has_many_property'

module CouchPotatoe
module Persistence
module Properties
Expand All @@ -12,38 +17,26 @@ def self.properties
end

module ClassMethods
def property_names
properties.map(&:name)
end

def property(name, options = {})
properties << name
attr_accessor name
properties << (options[:class] || SimpleProperty).new(self, name)
end

def belongs_to(name, options = {})
property name
property "#{name}_id"
def belongs_to(name)
property name, :class => BelongsToProperty
end

def has_many(name, options = {})
property name
getter = <<-GETTER
def #{name}
@#{name} ||= #{collection_code(name.to_s.singularize.camelize, options[:stored])}
end
GETTER
self.class_eval getter, 'persistence.rb', 154
end

private

def collection_code(item_class, storage)
if storage == :separately
"CouchPotatoe::Persistence::LazyCollection.new(#{item_class}, :#{self.name.split('::').last.underscore}_id)"
if(options[:stored] == :inline)
property name, :class => InlineHasManyProperty
else
"CouchPotatoe::Persistence::InlineCollection.new(#{item_class})"
property name, :class => ExternalHasManyProperty
end
end
end
end


end
end
Loading

0 comments on commit 45cd49a

Please sign in to comment.