| |   |
| 285 | 285 | |
| 286 | 286 | slots.keys.sort.each do |k| |
| 287 | 287 | if %w(version previous_version).member?(k) && v = self[k] |
| 288 | | s << "#{k}: #{v.gsub(/^(0)+/,'')[0,4]}..., " |
| 288 | s << "#{k}: #{v[0,4]}..., " |
| 289 | 289 | else |
| 290 | 290 | s << "#{k}: #{self[k].inspect}, " |
| 291 | 291 | end |
| … | … | |
| 344 | 344 | collect_meta_modules(store, raw_slots['meta']).each do |meta_module| |
| 345 | 345 | unless doc.is_a? meta_module |
| 346 | 346 | doc.extend(meta_module) |
| 347 | | |
| 348 | 347 | meta_module.setup_callbacks(doc) rescue nil |
| 349 | 348 | end |
| 350 | 349 | end |
| … | … | |
| 640 | 640 | initialize_slots slots |
| 641 | 641 | |
| 642 | 642 | self[:uuid] = Util.random_uuid unless self[:uuid] |
| 643 | | generate_new_version! unless self[:version] |
| 643 | self[:version] ||= NIL_UUID |
| 644 | 644 | end |
| 645 | 645 | end |
| 646 | 646 | |
| … | … | |
| 685 | 685 | if m = store.find($1, $2) |
| 686 | 686 | mod = Module.find_by_nsurl(m[:nsurl]) |
| 687 | 687 | mod = nil if mod == Module |
| 688 | | meta_names << (mod ? mod.name : "") + "::" + m[:name] |
| 688 | if (mod.nil? && Object.constants.include?(m[:name])) || (mod && mod.constants.include?(m[:name])) |
| 689 | meta_names << (mod ? mod.name : "") + "::" + m[:name] |
| 690 | else |
| 691 | meta_names << Meta.resolve_uuid_name(m[:nsurl],m[:name]) |
| 692 | end |
| 689 | 693 | end |
| 690 | 694 | when DOCREF |
| 691 | 695 | if m = store.find($1) |
| 692 | 696 | mod = Module.find_by_nsurl(m[:nsurl]) |
| 693 | 697 | mod = nil if mod == Module |
| 694 | | meta_names << (mod ? mod.name : "") + "::" + m[:name] |
| 698 | if (mod.nil? && Object.constants.include?(m[:name])) || (mod && mod.constants.include?(m[:name])) |
| 699 | meta_names << (mod ? mod.name : "") + "::" + m[:name] |
| 700 | else |
| 701 | meta_names << Meta.resolve_uuid_name(m[:nsurl],m[:name]) |
| 702 | end |
| 695 | 703 | end |
| 696 | 704 | when Array |
| 697 | 705 | meta_names = meta.map { |m| collect_meta_modules(store, m) }.flatten |
| toggle raw diff |
--- a/lib/strokedb/document.rb
+++ b/lib/strokedb/document.rb
@@ -285,7 +285,7 @@ module StrokeDB
slots.keys.sort.each do |k|
if %w(version previous_version).member?(k) && v = self[k]
- s << "#{k}: #{v.gsub(/^(0)+/,'')[0,4]}..., "
+ s << "#{k}: #{v[0,4]}..., "
else
s << "#{k}: #{self[k].inspect}, "
end
@@ -344,7 +344,6 @@ module StrokeDB
collect_meta_modules(store, raw_slots['meta']).each do |meta_module|
unless doc.is_a? meta_module
doc.extend(meta_module)
-
meta_module.setup_callbacks(doc) rescue nil
end
end
@@ -641,7 +640,7 @@ module StrokeDB
initialize_slots slots
self[:uuid] = Util.random_uuid unless self[:uuid]
- generate_new_version! unless self[:version]
+ self[:version] ||= NIL_UUID
end
end
@@ -686,13 +685,21 @@ module StrokeDB
if m = store.find($1, $2)
mod = Module.find_by_nsurl(m[:nsurl])
mod = nil if mod == Module
- meta_names << (mod ? mod.name : "") + "::" + m[:name]
+ if (mod.nil? && Object.constants.include?(m[:name])) || (mod && mod.constants.include?(m[:name]))
+ meta_names << (mod ? mod.name : "") + "::" + m[:name]
+ else
+ meta_names << Meta.resolve_uuid_name(m[:nsurl],m[:name])
+ end
end
when DOCREF
if m = store.find($1)
mod = Module.find_by_nsurl(m[:nsurl])
mod = nil if mod == Module
- meta_names << (mod ? mod.name : "") + "::" + m[:name]
+ if (mod.nil? && Object.constants.include?(m[:name])) || (mod && mod.constants.include?(m[:name]))
+ meta_names << (mod ? mod.name : "") + "::" + m[:name]
+ else
+ meta_names << Meta.resolve_uuid_name(m[:nsurl],m[:name])
+ end
end
when Array
meta_names = meta.map { |m| collect_meta_modules(store, m) }.flatten |
| |   |
| 1 | 1 | module StrokeDB |
| 2 | |
| 3 | META_CACHE = {} |
| 4 | |
| 2 | 5 | # Meta is basically a type. Imagine the following document: |
| 3 | 6 | # |
| 4 | 7 | # some_apple: |
| … | … | |
| 23 | 23 | # |
| 24 | 24 | # Document class will be extended by modules Fruit and Product. |
| 25 | 25 | module Meta |
| 26 | | |
| 26 | |
| 27 | 27 | class << self |
| 28 | |
| 29 | def resolve_uuid_name(nsurl,name) |
| 30 | "meta:#{nsurl}##{name}" |
| 31 | end |
| 32 | |
| 33 | def make_uuid_from_fullname(full_name) |
| 34 | StrokeDB::Util.sha1_uuid(full_name) |
| 35 | end |
| 36 | |
| 37 | def make_uuid(nsurl, name) |
| 38 | StrokeDB::Util.sha1_uuid("meta:#{nsurl}##{name}") |
| 39 | end |
| 40 | |
| 28 | 41 | def new(*args, &block) |
| 29 | 42 | mod = Module.new |
| 30 | 43 | args = args.unshift(nil) if args.empty? || args.first.is_a?(Hash) |
| … | … | |
| 60 | 60 | initialize_coercions |
| 61 | 61 | initialize_virtualizations |
| 62 | 62 | end |
| 63 | | if meta_name = extract_meta_name(*args) |
| 64 | | Object.const_set(meta_name, mod) |
| 63 | if name = args.last.stringify_keys['name'] |
| 64 | META_CACHE[make_uuid(args.last.stringify_keys['nsurl'],args.last.stringify_keys['name'])] = mod |
| 65 | mod.instance_eval %{ |
| 66 | def name |
| 67 | '#{name}' |
| 68 | end |
| 69 | } |
| 65 | 70 | end |
| 66 | 71 | mod |
| 67 | 72 | end |
| … | … | |
| 86 | 86 | @uuid ||= ::Util.sha1_uuid("meta:#{StrokeDB.nsurl}##{Meta.name.demodulize}") |
| 87 | 87 | end |
| 88 | 88 | |
| 89 | | def extract_meta_name(*args) |
| 90 | | if args.first.is_a?(Hash) |
| 91 | | args.first[:name] |
| 92 | | else |
| 93 | | args[1][:name] unless args.empty? |
| 94 | | end |
| 95 | | end |
| 96 | | |
| 97 | 89 | end |
| 98 | 90 | |
| 91 | def implements(another_meta) |
| 92 | values = @args.select{|a| a.is_a?(Hash) }.first |
| 93 | values.merge!(another_meta.document.to_raw.delete_if {|k,v| ['name','uuid','version','previous_version','meta'].member?(k) }) |
| 94 | include(another_meta) |
| 95 | self |
| 96 | end |
| 97 | |
| 99 | 98 | def +(meta) |
| 100 | 99 | if is_a?(Module) && meta.is_a?(Module) |
| 101 | 100 | new_meta = Module.new |
| … | … | |
| 254 | 254 | metadocs.size > 1 ? metadocs.inject { |a, b| a + b }.make_immutable! : metadocs.first |
| 255 | 255 | end |
| 256 | 256 | |
| 257 | | private |
| 258 | | |
| 259 | 257 | def make_document(store=nil) |
| 260 | 258 | raise NoDefaultStoreError.new unless store ||= StrokeDB.default_store |
| 261 | 259 | @meta_initialization_procs.each {|proc| proc.call }.clear |
| … | … | |
| 261 | 261 | values = @args.clone.select{|a| a.is_a?(Hash) }.first |
| 262 | 262 | values[:meta] = Meta.document(store) |
| 263 | 263 | values[:name] ||= name.demodulize |
| 264 | |
| 265 | raise ArgumentError, "meta can't be nameless" if values[:name].blank? |
| 266 | |
| 264 | 267 | values[:nsurl] ||= name.modulize.empty? ? Module.nsurl : name.modulize.constantize.nsurl |
| 265 | | values[:uuid] ||= ::Util.sha1_uuid("meta:#{values[:nsurl]}##{values[:name]}") if values[:name] |
| 268 | values[:uuid] ||= Meta.make_uuid(values[:nsurl],values[:name]) |
| 269 | |
| 266 | 270 | |
| 267 | 271 | if meta_doc = find_meta_doc(values, store) |
| 268 | 272 | values[:version] = meta_doc.version |
| toggle raw diff |
--- a/lib/strokedb/document/meta.rb
+++ b/lib/strokedb/document/meta.rb
@@ -1,4 +1,7 @@
module StrokeDB
+
+ META_CACHE = {}
+
# Meta is basically a type. Imagine the following document:
#
# some_apple:
@@ -20,8 +23,21 @@ module StrokeDB
#
# Document class will be extended by modules Fruit and Product.
module Meta
-
+
class << self
+
+ def resolve_uuid_name(nsurl,name)
+ "meta:#{nsurl}##{name}"
+ end
+
+ def make_uuid_from_fullname(full_name)
+ StrokeDB::Util.sha1_uuid(full_name)
+ end
+
+ def make_uuid(nsurl, name)
+ StrokeDB::Util.sha1_uuid("meta:#{nsurl}##{name}")
+ end
+
def new(*args, &block)
mod = Module.new
args = args.unshift(nil) if args.empty? || args.first.is_a?(Hash)
@@ -44,8 +60,13 @@ module StrokeDB
initialize_coercions
initialize_virtualizations
end
- if meta_name = extract_meta_name(*args)
- Object.const_set(meta_name, mod)
+ if name = args.last.stringify_keys['name']
+ META_CACHE[make_uuid(args.last.stringify_keys['nsurl'],args.last.stringify_keys['name'])] = mod
+ mod.instance_eval %{
+ def name
+ '#{name}'
+ end
+ }
end
mod
end
@@ -65,16 +86,15 @@ module StrokeDB
@uuid ||= ::Util.sha1_uuid("meta:#{StrokeDB.nsurl}##{Meta.name.demodulize}")
end
- def extract_meta_name(*args)
- if args.first.is_a?(Hash)
- args.first[:name]
- else
- args[1][:name] unless args.empty?
- end
- end
-
end
+ def implements(another_meta)
+ values = @args.select{|a| a.is_a?(Hash) }.first
+ values.merge!(another_meta.document.to_raw.delete_if {|k,v| ['name','uuid','version','previous_version','meta'].member?(k) })
+ include(another_meta)
+ self
+ end
+
def +(meta)
if is_a?(Module) && meta.is_a?(Module)
new_meta = Module.new
@@ -234,8 +254,6 @@ module StrokeDB
metadocs.size > 1 ? metadocs.inject { |a, b| a + b }.make_immutable! : metadocs.first
end
- private
-
def make_document(store=nil)
raise NoDefaultStoreError.new unless store ||= StrokeDB.default_store
@meta_initialization_procs.each {|proc| proc.call }.clear
@@ -243,8 +261,12 @@ module StrokeDB
values = @args.clone.select{|a| a.is_a?(Hash) }.first
values[:meta] = Meta.document(store)
values[:name] ||= name.demodulize
+
+ raise ArgumentError, "meta can't be nameless" if values[:name].blank?
+
values[:nsurl] ||= name.modulize.empty? ? Module.nsurl : name.modulize.constantize.nsurl
- values[:uuid] ||= ::Util.sha1_uuid("meta:#{values[:nsurl]}##{values[:name]}") if values[:name]
+ values[:uuid] ||= Meta.make_uuid(values[:nsurl],values[:name])
+
if meta_doc = find_meta_doc(values, store)
values[:version] = meta_doc.version |
| |   |
| 22 | 22 | existing_chain = {} |
| 23 | 23 | docs.group_by {|doc| doc.uuid}.each_pair do |uuid, versions| |
| 24 | 24 | doc = find(uuid) |
| 25 | | existing_chain[uuid] = doc.versions.all_versions if doc |
| 25 | existing_chain[uuid] = doc.versions.all_versions.map {|v| [v, doc.versions[v].to_json ]} if doc |
| 26 | 26 | end |
| 27 | 27 | case _timestamp |
| 28 | 28 | when Numeric |
| … | … | |
| 31 | 31 | @timestamp = LTS.new(_timestamp.counter, timestamp.uuid) |
| 32 | 32 | else |
| 33 | 33 | end |
| 34 | | docs.each {|doc| save!(doc) unless include?(doc.uuid, doc.version)} |
| 34 | @txn = Transaction.new(:store => self) |
| 35 | @txn.execute do |txn| |
| 36 | docs.each {|doc| save!(doc) } |
| 35 | 37 | docs.group_by {|doc| doc.uuid}.each_pair do |uuid, versions| |
| 36 | | incoming_chain = find(uuid, versions.last.version).versions.all_versions |
| 38 | incoming_chain = find(uuid, versions.last.version).versions.all_versions.map {|v| [v, find(uuid,v).to_json ]} |
| 37 | 39 | if existing_chain[uuid].nil? or existing_chain[uuid].empty? # It is a new document |
| 38 | 40 | added_doc = find(uuid, versions.last.version) |
| 39 | 41 | save_as_head!(added_doc) |
| … | … | |
| 45 | 45 | sync = sync_chains(incoming_chain.reverse, existing_chain[uuid].reverse) |
| 46 | 46 | rescue NonMatchingChains |
| 47 | 47 | # raise NonMatchingDocumentCondition.new(uuid) # that will definitely leave garbage in the store (FIXME?) |
| 48 | txn.rollback! |
| 48 | 49 | non_matching_doc = find(uuid) |
| 49 | 50 | report.non_matching_documents << non_matching_doc |
| 50 | 51 | next |
| … | … | |
| 54 | 54 | case resolution |
| 55 | 55 | when :up_to_date |
| 56 | 56 | # nothing to do |
| 57 | txn.commit! |
| 57 | 58 | when :merge |
| 58 | | report.conflicts << SynchronizationConflict.create!(self, :document => find(uuid), :rev1 => sync[1], :rev2 => sync[2]) |
| 59 | report.conflicts << SynchronizationConflict.create!(self, :document => find(uuid), :rev1 => sync[1].map{|e| e[0]}.reverse, :rev2 => sync[2].map{|e| e[0]}.reverse) |
| 60 | txn.commit! |
| 59 | 61 | when :fast_forward |
| 60 | | fast_forwarded_doc = find(uuid, sync[1].last) |
| 62 | fast_forwarded_doc = find(uuid, sync[1].last.first) |
| 61 | 63 | save_as_head!(fast_forwarded_doc) |
| 62 | 64 | report.fast_forwarded_documents << fast_forwarded_doc |
| 65 | txn.commit! |
| 63 | 66 | else |
| 67 | txn.rollback! |
| 64 | 68 | raise "Invalid sync resolution #{resolution}" |
| 65 | 69 | end |
| 66 | 70 | end |
| 67 | 71 | end |
| 72 | end |
| 68 | 73 | report.conflicts.each do |conflict| |
| 69 | 74 | if resolution_strategy = conflict.document.meta[:resolution_strategy] |
| 70 | 75 | conflict.metas << resolution_strategy |
| toggle raw diff |
--- a/lib/strokedb/sync/store_sync.rb
+++ b/lib/strokedb/sync/store_sync.rb
@@ -22,7 +22,7 @@ module StrokeDB
existing_chain = {}
docs.group_by {|doc| doc.uuid}.each_pair do |uuid, versions|
doc = find(uuid)
- existing_chain[uuid] = doc.versions.all_versions if doc
+ existing_chain[uuid] = doc.versions.all_versions.map {|v| [v, doc.versions[v].to_json ]} if doc
end
case _timestamp
when Numeric
@@ -31,9 +31,11 @@ module StrokeDB
@timestamp = LTS.new(_timestamp.counter, timestamp.uuid)
else
end
- docs.each {|doc| save!(doc) unless include?(doc.uuid, doc.version)}
+ @txn = Transaction.new(:store => self)
+ @txn.execute do |txn|
+ docs.each {|doc| save!(doc) }
docs.group_by {|doc| doc.uuid}.each_pair do |uuid, versions|
- incoming_chain = find(uuid, versions.last.version).versions.all_versions
+ incoming_chain = find(uuid, versions.last.version).versions.all_versions.map {|v| [v, find(uuid,v).to_json ]}
if existing_chain[uuid].nil? or existing_chain[uuid].empty? # It is a new document
added_doc = find(uuid, versions.last.version)
save_as_head!(added_doc)
@@ -43,6 +45,7 @@ module StrokeDB
sync = sync_chains(incoming_chain.reverse, existing_chain[uuid].reverse)
rescue NonMatchingChains
# raise NonMatchingDocumentCondition.new(uuid) # that will definitely leave garbage in the store (FIXME?)
+ txn.rollback!
non_matching_doc = find(uuid)
report.non_matching_documents << non_matching_doc
next
@@ -51,17 +54,22 @@ module StrokeDB
case resolution
when :up_to_date
# nothing to do
+ txn.commit!
when :merge
- report.conflicts << SynchronizationConflict.create!(self, :document => find(uuid), :rev1 => sync[1], :rev2 => sync[2])
+ report.conflicts << SynchronizationConflict.create!(self, :document => find(uuid), :rev1 => sync[1].map{|e| e[0]}.reverse, :rev2 => sync[2].map{|e| e[0]}.reverse)
+ txn.commit!
when :fast_forward
- fast_forwarded_doc = find(uuid, sync[1].last)
+ fast_forwarded_doc = find(uuid, sync[1].last.first)
save_as_head!(fast_forwarded_doc)
report.fast_forwarded_documents << fast_forwarded_doc
+ txn.commit!
else
+ txn.rollback!
raise "Invalid sync resolution #{resolution}"
end
end
end
+ end
report.conflicts.each do |conflict|
if resolution_strategy = conflict.document.meta[:resolution_strategy]
conflict.metas << resolution_strategy |
| |   |
| 1 | 1 | require File.dirname(__FILE__) + '/spec_helper' |
| 2 | 2 | |
| 3 | 3 | describe "Playlist.has_many :songs association" do |
| 4 | | |
| 4 | |
| 5 | 5 | before(:each) do |
| 6 | 6 | setup_default_store |
| 7 | 7 | setup_index |
| … | … | |
| 12 | 12 | end |
| 13 | 13 | Song = Meta.new |
| 14 | 14 | end |
| 15 | | |
| 15 | |
| 16 | 16 | it "should convert :songs to Song and Playlist to playlist to compute foreign reference slot name" do |
| 17 | 17 | playlist = Playlist.create! |
| 18 | 18 | song = Song.create!(:playlist => playlist) |
| … | … | |
| 24 | 24 | song = Song.create! |
| 25 | 25 | playlist.songs.should be_empty |
| 26 | 26 | end |
| 27 | | |
| 27 | |
| 28 | 28 | it "should have association owner defined" do |
| 29 | 29 | playlist = Playlist.create! |
| 30 | 30 | song = Song.create! |
| 31 | 31 | playlist.songs.association_owner.should == playlist |
| 32 | 32 | end |
| 33 | | |
| 33 | |
| 34 | 34 | it "should work well with multiple metas" do |
| 35 | 35 | Object.send!(:remove_const,'RockPlaylist') if defined?(RockPlaylist) |
| 36 | 36 | RockPlaylist = Meta.new do |
| … | … | |
| 44 | 44 | playlist.songs.should == [rock_song] |
| 45 | 45 | playlist.rock_songs.should == [rock_song] |
| 46 | 46 | end |
| 47 | | |
| 47 | |
| 48 | 48 | it "should fetch head versions of associated documents if association owner is a head" do |
| 49 | 49 | playlist = Playlist.create! |
| 50 | 50 | playlist.should be_head |
| … | … | |
| 60 | 60 | end |
| 61 | 61 | |
| 62 | 62 | it "should fetch specific versions of associated documents if association owner is a not a head" do |
| 63 | | pending |
| 64 | | playlist = Playlist.create! |
| 65 | | song = Song.create!(:playlist => playlist) |
| 66 | | playlist.name = "My playlist" |
| 67 | | playlist.save! |
| 68 | | playlist = playlist.versions.previous |
| 69 | | playlist.should_not be_head |
| 70 | | song.name = "My song" |
| 71 | | song.save! |
| 72 | | song = song.versions.previous |
| 73 | | playlist.songs.should == [song] |
| 74 | | playlist.songs.each do |s| |
| 75 | | s.should_not be_head |
| 76 | | s.should be_a_kind_of(VersionedDocument) |
| 77 | | s.should_not have_slot(:name) |
| 63 | pending do |
| 64 | playlist = Playlist.create! |
| 65 | song = Song.create!(:playlist => playlist) |
| 66 | playlist.name = "My playlist" |
| 67 | playlist.save! |
| 68 | playlist = playlist.versions.previous |
| 69 | playlist.should_not be_head |
| 70 | song.name = "My song" |
| 71 | song.save! |
| 72 | song = song.versions.previous |
| 73 | playlist.songs.should == [song] |
| 74 | playlist.songs.each do |s| |
| 75 | s.should_not be_head |
| 76 | s.should be_a_kind_of(VersionedDocument) |
| 77 | s.should_not have_slot(:name) |
| 78 | end |
| 78 | 79 | end |
| 79 | 80 | end |
| 80 | 81 | |
| … | … | |
| 95 | 95 | end |
| 96 | 96 | |
| 97 | 97 | it "should fetch head versions of associated documents if association owner wasn't saved when associated doc were created and now it is not a head" do |
| 98 | | pending |
| 99 | | playlist = Playlist.new |
| 100 | | song = Song.create!(:playlist => playlist) |
| 101 | | song.name = "My song" |
| 102 | | song.save! |
| 103 | | playlist.save! |
| 104 | | playlist.name = "My playlist" |
| 105 | | playlist.save! |
| 106 | | playlist = playlist.versions.previous |
| 107 | | playlist.songs.should == [song] |
| 108 | | playlist.songs.each do |s| |
| 109 | | s.should be_head |
| 110 | | s.should_not be_a_kind_of(VersionedDocument) |
| 111 | | s.should have_slot(:name) |
| 98 | pending do |
| 99 | playlist = Playlist.new |
| 100 | song = Song.create!(:playlist => playlist) |
| 101 | song.name = "My song" |
| 102 | song.save! |
| 103 | playlist.save! |
| 104 | playlist.name = "My playlist" |
| 105 | playlist.save! |
| 106 | playlist = playlist.versions.previous |
| 107 | playlist.songs.should == [song] |
| 108 | playlist.songs.each do |s| |
| 109 | s.should be_head |
| 110 | s.should_not be_a_kind_of(VersionedDocument) |
| 111 | s.should have_slot(:name) |
| 112 | end |
| 112 | 113 | end |
| 113 | 114 | end |
| 114 | | |
| 115 | |
| 115 | 116 | it "should be able to filter associated documents" do |
| 116 | 117 | playlist = Playlist.create! |
| 117 | 118 | rock_song = Song.create!(:playlist => playlist, :genre => 'Rock') |
| … | … | |
| 120 | 120 | playlist.songs.find(:genre => 'Rock').should == [rock_song] |
| 121 | 121 | playlist.songs.find(:genre => 'Pop').should == [pop_song] |
| 122 | 122 | end |
| 123 | | |
| 123 | |
| 124 | 124 | it "should be able to instantiate new document with #new" do |
| 125 | 125 | playlist = Playlist.create! |
| 126 | 126 | song = playlist.songs.new(:name => 'My song') |
| … | … | |
| 153 | 153 | song.should_not be_new |
| 154 | 154 | playlist.songs.should == [song] |
| 155 | 155 | end |
| 156 | | |
| 156 | |
| 157 | 157 | end |
| 158 | 158 | |
| 159 | 159 | describe "Namespace::Playlist.has_many :songs association" do |
| 160 | | |
| 160 | |
| 161 | 161 | before(:each) do |
| 162 | 162 | setup_default_store |
| 163 | 163 | setup_index |
| … | … | |
| 170 | 170 | end |
| 171 | 171 | Namespace::Song = Meta.new |
| 172 | 172 | end |
| 173 | | |
| 173 | |
| 174 | 174 | it "should convert :songs to Song and Playlist to playlist to compute foreign reference slot name" do |
| 175 | 175 | playlist = Namespace::Playlist.create! |
| 176 | 176 | song = Namespace::Song.create!(:playlist => playlist) |
| 177 | 177 | playlist.songs.should == [song] |
| 178 | 178 | end |
| 179 | | |
| 180 | | |
| 179 | |
| 180 | |
| 181 | 181 | end |
| 182 | 182 | |
| 183 | 183 | describe "Playlist.has_many :rock_songs, :through => :songs, :conditions => { :genre => 'Rock' } association" do |
| 184 | | |
| 184 | |
| 185 | 185 | before(:each) do |
| 186 | 186 | setup_default_store |
| 187 | 187 | setup_index |
| … | … | |
| 192 | 192 | end |
| 193 | 193 | Song = Meta.new |
| 194 | 194 | end |
| 195 | | |
| 195 | |
| 196 | 196 | it "should convert :songs to Song and Playlist to playlist to compute foreign reference slot name" do |
| 197 | 197 | playlist = Playlist.create! |
| 198 | 198 | rock_song = Song.create!(:playlist => playlist, :genre => 'Rock') |
| 199 | 199 | pop_song = Song.create!(:playlist => playlist, :genre => 'Pop') |
| 200 | 200 | playlist.rock_songs.should == [rock_song] |
| 201 | 201 | end |
| 202 | | |
| 202 | |
| 203 | 203 | end |
| 204 | 204 | |
| 205 | 205 | describe "Playlist.has_many :songs, :foreign_reference => :belongs_to_playlist association" do |
| 206 | | |
| 206 | |
| 207 | 207 | before(:each) do |
| 208 | 208 | setup_default_store |
| 209 | 209 | setup_index |
| … | … | |
| 214 | 214 | end |
| 215 | 215 | Song = Meta.new |
| 216 | 216 | end |
| 217 | | |
| 217 | |
| 218 | 218 | it "should convert :songs to Song and use foreign_reference to compute foreign reference slot name" do |
| 219 | 219 | playlist = Playlist.create! |
| 220 | 220 | song = Song.create!(:belongs_to_playlist => playlist) |
| 221 | 221 | playlist.songs.should == [song] |
| 222 | 222 | end |
| 223 | | |
| 223 | |
| 224 | 224 | end |
| 225 | 225 | |
| 226 | 226 | describe "Playlist.has_many :all_songs, :through => :songs association" do |
| 227 | | |
| 227 | |
| 228 | 228 | before(:each) do |
| 229 | 229 | setup_default_store |
| 230 | 230 | setup_index |
| … | … | |
| 236 | 236 | end |
| 237 | 237 | Song = Meta.new |
| 238 | 238 | end |
| 239 | | |
| 239 | |
| 240 | 240 | it "should use through's :songs to find out Song and convert Playlist to playlist" do |
| 241 | 241 | playlist = Playlist.create! |
| 242 | 242 | song = Song.create!(:playlist => playlist) |
| 243 | 243 | playlist.all_songs.should == [song] |
| 244 | 244 | end |
| 245 | | |
| 245 | |
| 246 | 246 | end |
| 247 | 247 | |
| 248 | 248 | describe "Playlist.has_many :authors, :through => [:songs,:authors] association" do |
| 249 | | |
| 249 | |
| 250 | 250 | before(:each) do |
| 251 | 251 | setup_default_store |
| 252 | 252 | setup_index |
| … | … | |
| 258 | 258 | end |
| 259 | 259 | Song = Meta.new |
| 260 | 260 | end |
| 261 | | |
| 261 | |
| 262 | 262 | it "should use through's :songs to find out Song and convert Playlist to playlist" do |
| 263 | 263 | playlist = Playlist.create! |
| 264 | 264 | song = Song.create!(:playlist => playlist, :author => "John Doe") |
| … | … | |
| 270 | 270 | song = Song.create!(:playlist => playlist) |
| 271 | 271 | playlist.authors.should be_empty |
| 272 | 272 | end |
| 273 | | |
| 273 | |
| 274 | 274 | end |
| 275 | 275 | |
| 276 | 276 | describe "Playlist.has_many :songs, :extend => MyExt association" do |
| … | … | |
| 287 | 287 | end |
| 288 | 288 | Song = Meta.new |
| 289 | 289 | end |
| 290 | | |
| 290 | |
| 291 | 291 | it "should extend result with MyExt" do |
| 292 | 292 | playlist = Playlist.create! |
| 293 | 293 | playlist.songs.should be_a_kind_of(MyExt) |
| 294 | 294 | end |
| 295 | | |
| 295 | |
| 296 | 296 | end |
| 297 | 297 | |
| 298 | 298 | describe "Playlist.has_many :songs do .. end association" do |
| … | … | |
| 309 | 309 | end |
| 310 | 310 | Song = Meta.new |
| 311 | 311 | end |
| 312 | | |
| 312 | |
| 313 | 313 | it "should extend result with given block" do |
| 314 | 314 | playlist = Playlist.create! |
| 315 | 315 | playlist.songs.should be_a_kind_of(Playlist::HasManySongs) |
| 316 | 316 | playlist.songs.should respond_to(:some_method) |
| 317 | 317 | end |
| 318 | | |
| 318 | |
| 319 | 319 | end |
| toggle raw diff |
--- a/spec/lib/strokedb/document/associations_spec.rb
+++ b/spec/lib/strokedb/document/associations_spec.rb
@@ -1,7 +1,7 @@
require File.dirname(__FILE__) + '/spec_helper'
describe "Playlist.has_many :songs association" do
-
+
before(:each) do
setup_default_store
setup_index
@@ -12,7 +12,7 @@ describe "Playlist.has_many :songs association" do
end
Song = Meta.new
end
-
+
it "should convert :songs to Song and Playlist to playlist to compute foreign reference slot name" do
playlist = Playlist.create!
song = Song.create!(:playlist => playlist)
@@ -24,13 +24,13 @@ describe "Playlist.has_many :songs association" do
song = Song.create!
playlist.songs.should be_empty
end
-
+
it "should have association owner defined" do
playlist = Playlist.create!
song = Song.create!
playlist.songs.association_owner.should == playlist
end
-
+
it "should work well with multiple metas" do
Object.send!(:remove_const,'RockPlaylist') if defined?(RockPlaylist)
RockPlaylist = Meta.new do
@@ -44,7 +44,7 @@ describe "Playlist.has_many :songs association" do
playlist.songs.should == [rock_song]
playlist.rock_songs.should == [rock_song]
end
-
+
it "should fetch head versions of associated documents if association owner is a head" do
playlist = Playlist.create!
playlist.should be_head
@@ -60,21 +60,22 @@ describe "Playlist.has_many :songs association" do
end
it "should fetch specific versions of associated documents if association owner is a not a head" do
- pending
- playlist = Playlist.create!
- song = Song.create!(:playlist => playlist)
- playlist.name = "My playlist"
- playlist.save!
- playlist = playlist.versions.previous
- playlist.should_not be_head
- song.name = "My song"
- song.save!
- song = song.versions.previous
- playlist.songs.should == [song]
- playlist.songs.each do |s|
- s.should_not be_head
- s.should be_a_kind_of(VersionedDocument)
- s.should_not have_slot(:name)
+ pending do
+ playlist = Playlist.create!
+ song = Song.create!(:playlist => playlist)
+ playlist.name = "My playlist"
+ playlist.save!
+ playlist = playlist.versions.previous
+ playlist.should_not be_head
+ song.name = "My song"
+ song.save!
+ song = song.versions.previous
+ playlist.songs.should == [song]
+ playlist.songs.each do |s|
+ s.should_not be_head
+ s.should be_a_kind_of(VersionedDocument)
+ s.should_not have_slot(:name)
+ end
end
end
@@ -94,23 +95,24 @@ describe "Playlist.has_many :songs association" do
end
it "should fetch head versions of associated documents if association owner wasn't saved when associated doc were created and now it is not a head" do
- pending
- playlist = Playlist.new
- song = Song.create!(:playlist => playlist)
- song.name = "My song"
- song.save!
- playlist.save!
- playlist.name = "My playlist"
- playlist.save!
- playlist = playlist.versions.previous
- playlist.songs.should == [song]
- playlist.songs.each do |s|
- s.should be_head
- s.should_not be_a_kind_of(VersionedDocument)
- s.should have_slot(:name)
+ pending do
+ playlist = Playlist.new
+ song = Song.create!(:playlist => playlist)
+ song.name = "My song"
+ song.save!
+ playlist.save!
+ playlist.name = "My playlist"
+ playlist.save!
+ playlist = playlist.versions.previous
+ playlist.songs.should == [song]
+ playlist.songs.each do |s|
+ s.should be_head
+ s.should_not be_a_kind_of(VersionedDocument)
+ s.should have_slot(:name)
+ end
end
end
-
+
it "should be able to filter associated documents" do
playlist = Playlist.create!
rock_song = Song.create!(:playlist => playlist, :genre => 'Rock')
@@ -118,7 +120,7 @@ describe "Playlist.has_many :songs association" do
playlist.songs.find(:genre => 'Rock').should == [rock_song]
playlist.songs.find(:genre => 'Pop').should == [pop_song]
end
-
+
it "should be able to instantiate new document with #new" do
playlist = Playlist.create!
song = playlist.songs.new(:name => 'My song')
@@ -151,11 +153,11 @@ describe "Playlist.has_many :songs association" do
song.should_not be_new
playlist.songs.should == [song]
end
-
+
end
describe "Namespace::Playlist.has_many :songs association" do
-
+
before(:each) do
setup_default_store
setup_index
@@ -168,18 +170,18 @@ describe "Namespace::Playlist.has_many :songs association" do
end
Namespace::Song = Meta.new
end
-
+
it "should convert :songs to Song and Playlist to playlist to compute foreign reference slot name" do
playlist = Namespace::Playlist.create!
song = Namespace::Song.create!(:playlist => playlist)
playlist.songs.should == [song]
end
-
-
+
+
end
describe "Playlist.has_many :rock_songs, :through => :songs, :conditions => { :genre => 'Rock' } association" do
-
+
before(:each) do
setup_default_store
setup_index
@@ -190,18 +192,18 @@ describe "Playlist.has_many :rock_songs, :through => :songs, :conditions => { :g
end
Song = Meta.new
end
-
+
it "should convert :songs to Song and Playlist to playlist to compute foreign reference slot name" do
playlist = Playlist.create!
rock_song = Song.create!(:playlist => playlist, :genre => 'Rock')
pop_song = Song.create!(:playlist => playlist, :genre => 'Pop')
playlist.rock_songs.should == [rock_song]
end
-
+
end
describe "Playlist.has_many :songs, :foreign_reference => :belongs_to_playlist association" do
-
+
before(:each) do
setup_default_store
setup_index
@@ -212,17 +214,17 @@ describe "Playlist.has_many :songs, :foreign_reference => :belongs_to_playlist a
end
Song = Meta.new
end
-
+
it "should convert :songs to Song and use foreign_reference to compute foreign reference slot name" do
playlist = Playlist.create!
song = Song.create!(:belongs_to_playlist => playlist)
playlist.songs.should == [song]
end
-
+
end
describe "Playlist.has_many :all_songs, :through => :songs association" do
-
+
before(:each) do
setup_default_store
setup_index
@@ -234,17 +236,17 @@ describe "Playlist.has_many :all_songs, :through => :songs association" do
end
Song = Meta.new
end
-
+
it "should use through's :songs to find out Song and convert Playlist to playlist" do
playlist = Playlist.create!
song = Song.create!(:playlist => playlist)
playlist.all_songs.should == [song]
end
-
+
end
describe "Playlist.has_many :authors, :through => [:songs,:authors] association" do
-
+
before(:each) do
setup_default_store
setup_index
@@ -256,7 +258,7 @@ describe "Playlist.has_many :authors, :through => [:songs,:authors] association"
end
Song = Meta.new
end
-
+
it "should use through's :songs to find out Song and convert Playlist to playlist" do
playlist = Playlist.create!
song = Song.create!(:playlist => playlist, :author => "John Doe")
@@ -268,7 +270,7 @@ describe "Playlist.has_many :authors, :through => [:songs,:authors] association"
song = Song.create!(:playlist => playlist)
playlist.authors.should be_empty
end
-
+
end
describe "Playlist.has_many :songs, :extend => MyExt association" do
@@ -285,12 +287,12 @@ describe "Playlist.has_many :songs, :extend => MyExt association" do
end
Song = Meta.new
end
-
+
it "should extend result with MyExt" do
playlist = Playlist.create!
playlist.songs.should be_a_kind_of(MyExt)
end
-
+
end
describe "Playlist.has_many :songs do .. end association" do
@@ -307,11 +309,11 @@ describe "Playlist.has_many :songs do .. end association" do
end
Song = Meta.new
end
-
+
it "should extend result with given block" do
playlist = Playlist.create!
playlist.songs.should be_a_kind_of(Playlist::HasManySongs)
playlist.songs.should respond_to(:some_method)
end
-
+
end |
| |   |
| 1 | describe "Meta module with on_initialization callback" do |
| 2 | |
| 3 | before(:each) do |
| 4 | setup_default_store |
| 5 | setup_index |
| 6 | |
| 7 | Object.send!(:remove_const,'SomeName') if defined?(SomeName) |
| 8 | SomeName = Meta.new do |
| 9 | on_initialization do |obj| |
| 10 | Kernel.send!(:on_initialization_called,obj.new?) |
| 11 | end |
| 12 | end |
| 13 | end |
| 14 | |
| 15 | it "should receive this callback on meta instantiation" do |
| 16 | Kernel.should_receive(:on_initialization_called).with(true) |
| 17 | doc = SomeName.new |
| 18 | end |
| 19 | |
| 20 | it "should be a sole meta receiving this callback when adding metas dynamically" do |
| 21 | Object.send!(:remove_const,'SomeOtherName') if defined?(SomeOtherName) |
| 22 | SomeOtherName = Meta.new do |
| 23 | on_initialization do |obj| |
| 24 | Kernel.send!(:other_on_initialization_called,obj.new?) |
| 25 | end |
| 26 | end |
| 27 | Kernel.should_receive(:other_on_initialization_called).with(true).once |
| 28 | doc = SomeOtherName.new |
| 29 | Kernel.should_receive(:on_initialization_called).with(true).once |
| 30 | doc.metas << SomeName |
| 31 | end |
| 32 | |
| 33 | end |
| 34 | |
| 35 | |
| 36 | describe "Meta module with on_load callback" do |
| 37 | |
| 38 | before(:each) do |
| 39 | setup_default_store |
| 40 | setup_index |
| 41 | Object.send!(:remove_const,'SomeName') if defined?(SomeName) |
| 42 | SomeName = Meta.new do |
| 43 | on_load do |obj| |
| 44 | Kernel.send!(:on_load_called,obj.new?) |
| 45 | end |
| 46 | end |
| 47 | end |
| 48 | |
| 49 | it "should not receive this callback on meta instantiation" do |
| 50 | Kernel.should_not_receive(:on_load_called) |
| 51 | doc = SomeName.new |
| 52 | end |
| 53 | |
| 54 | it "should receive this callback on document load" do |
| 55 | doc = SomeName.create! |
| 56 | Kernel.should_receive(:on_load_called).with(false) |
| 57 | d = SomeName.find(doc.uuid) |
| 58 | end |
| 59 | |
| 60 | |
| 61 | end |
| 62 | |
| 63 | describe "Meta module with before_save callback" do |
| 64 | |
| 65 | before(:each) do |
| 66 | setup_default_store |
| 67 | setup_index |
| 68 | |
| 69 | Object.send!(:remove_const,'SomeName') if defined?(SomeName) |
| 70 | SomeName = Meta.new do |
| 71 | before_save do |obj| |
| 72 | Kernel.send!(:before_save_called,obj.new?) |
| 73 | end |
| 74 | end |
| 75 | end |
| 76 | |
| 77 | it "should initiate callback on Document#save! (before actually saving it)" do |
| 78 | s = SomeName.new |
| 79 | Kernel.should_receive(:before_save_called).with(true) |
| 80 | s.save! |
| 81 | end |
| 82 | |
| 83 | end |
| 84 | |
| 85 | describe "Meta module with after_save callback" do |
| 86 | |
| 87 | before(:each) do |
| 88 | setup_default_store |
| 89 | setup_index |
| 90 | |
| 91 | Object.send!(:remove_const,'SomeName') if defined?(SomeName) |
| 92 | SomeName = Meta.new do |
| 93 | after_save do |obj| |
| 94 | Kernel.send!(:after_save_called,obj.new?) |
| 95 | end< |