mm_dirtier 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,117 +1,98 @@
1
- = Observables
1
+ = mm-dirtier
2
2
 
3
- Observables implements observable arrays and hashes by way of the ActiveModel::Notifier, the same mechanism
4
- underlying the instrumentation API in Rails3. Observables collections broadcast detailed change information for
5
- any state-modifying operations, including specific elements added or removed, when that information is practically
6
- obtainable.
3
+ mm-dirtier is a MongoMapper plugin that extends dirty tracking to the entire object graph. The default dirty tracking implementation in MongoMapper will only track dirty attributes for top level documents, and does not detect changes to hash or array keys. mm-dirtier, which currently only works with the Rails3 branch of MongoMapper, will record changes throughout your entire document hierarchy, including in place modifications to embedded array and hash keys.
7
4
 
8
- == Installation
5
+ == Installation
9
6
 
10
- Observables is available as a RubyGem:
7
+ mm-dirtier is available as a RubyGem:
11
8
 
12
9
  gem install mm_dirtier
13
10
 
14
- == Observing the Observables
11
+ To activate the plugin, add 'mm_dirtier' to your gemfile and
15
12
 
16
- === Example:
13
+ include 'mm_dirtier'
17
14
 
18
- #Getting an observable array
15
+ when your app is initialized. mm-dirtier will take care of plugging itself into MongoMapper::Document and MongoMapper::EmbeddedDocument.
19
16
 
20
- ary = [1,2,3] #or, ary = Observables::Array.new([1,2,3]), or, hsh = Observables::Hash
21
- ary.observable? # => false
22
- ary.can_be_observable? # => true
23
- ary.make_observable => #<Class:#<Array:0x56a84a8>>
17
+ == Usage
24
18
 
25
- #Setting up a subscription
19
+ mm-dirtier was created to serve a single purpose: to enable MongoMapper to automatically persist changes to documents via MongoDB's atomic opererators.
20
+ Whether or not the deep dirty tracking mm-dirtier provides has any use beyond that, I have no idea. For that reason I will simply provide a basic sample of plugin usage in this readme. If anyone conceives of a use for this plugin beyond document persistence and wants to know more, add an issue to the tracker stating as much and I'll expand the documentation.
26
21
 
27
- subscription = ary.subscribe do |change_type,args|
28
- puts "Change type: #{change_type}"
29
- puts "Trigger: #{args.trigger}"
30
- puts "Changes: #{args.changes.inspect}"
31
- puts "---"
32
- end
33
-
34
- #Do stuff
35
-
36
- ary << 3
37
- # Change type: before_added
38
- # Trigger: <<
39
- # Changes: {:added=>[3]}
40
- # ---
41
- # Change type: after_added
42
- # Trigger: <<
43
- # Changes: {:added=>[3]}
44
- # => [1,2,3,3]
22
+ === Example:
45
23
 
46
- #Clean up
24
+ class Procedure
25
+ include MongoMapper::EmbeddedDocument
47
26
 
48
- ary.unsubscribe(subscription)
49
- ary << 4 # => [1,2,3,3,4]
27
+ key :name, String
28
+ end
50
29
 
51
- #Only listen to after_xxx
30
+ class Diagnosis
31
+ include MongoMapper::Document
52
32
 
53
- subscription = ary.subscribe(/after/) do |change_type,_|
54
- puts "Change type:#{change_type}"
33
+ key :name, String
55
34
  end
56
35
 
57
- ary.concat([9,10,11])
36
+ class Patient
37
+ include MongoMapper::Document
58
38
 
59
- # Change type: after_added, changes: {:added=>[9,10,11]}
60
- # => [1,2,3,3,4,9,10,11]
39
+ key :name, String
40
+ key :nicknames, Array
41
+ key :address, Hash
61
42
 
62
- ary.replace([3,2,1])
43
+ one :procedure
44
+ many :procedures
45
+ many :diagnosis_ids, Array
46
+ many :diagnoses, :in => :diagnosis_ids
47
+ end
63
48
 
64
- # Change type: after_modified, changed: {:added=>[3,2,1], :removed=>[1,2,3,3,4,9,10,11]}
65
- # => [3,2,1]
49
+ p = Patient.create :name=>"Nathan Stults"
50
+ p.changed? => false
51
+ p.nicknames << "Col. Easy Andy"
52
+ p.changed? => true
53
+ p.nicknames_changed? => true
54
+ p.nicknames_change => [[],["Col. Easy Andy"]]
66
55
 
67
- #Hashes work too
56
+ p.address["city"] = "Santa Rosa"
57
+ p.address_changed? => true
58
+ p.address_change => [{},{"city"=>"Santa Rosa"}]
68
59
 
69
- hsh = {:a=>:b}
70
- hsh.can_be_observable? # => true
71
- hsh.make_observable
72
- hsh.subscribe { |type,args| ... }
60
+ p.procedure.build :name => "Labotomy"
61
+ p.procedure_changed? => true
62
+ p.procedure_change => [nil,<Class ...>]
63
+ p.procedure.changed? => true
64
+ p.procedure.name_changed? => true
73
65
 
74
- == Special case: ownership
66
+ p.procedures.build(:name=>"Labotomy")
67
+ p.procedures_changed? => true
68
+ p.procedures_change? => [[],[<Class...]]
69
+ p.procedures[0].changed? => true
70
+ p.procedures[0].name_changed? => true
71
+ p.procedures[0].name_change => [nil,"Labotomy"]
75
72
 
76
- Observables was created to assist in the implementation of proper dirty tracking for in-place modifications
77
- to embedded collections in ORM's, particularly for documented oriented databases, where
78
- this is a common situation. In this scenario and similar scenarios, observable collections
79
- will only be subscribed to by the object that owns them. However, the parent object
80
- may own any number of child collections. To avoid having to manage myriad subscription
81
- objects, each observable collection can have a single 'observer' - and will manage the
82
- subscription to that observer like so:
73
+ #in_array associations are a little different than embedded associations
74
+ #because what is actually stored on the document is only the array of ids
75
+ #then that is what is tracked for changed.
76
+ p.diagnoses << Diagnosis.create(:name=>"Aspergers Syndrom")
77
+ p.diagnosis_ids_changed? => true
78
+ p.diagnosis_ids_change => [[],[BSON::ObjectId('...')]]
83
79
 
84
- class Owner
85
- def my_array
86
- @my_array
87
- end
80
+ p.save
88
81
 
89
- def my_array=(new_array)
90
- @my_array.clear_observer if @my_array
91
- @my_array = new_array.tap {|a|a.make_observable}
92
- @my_array.set_observer(self, :pattern=>/before/, :callback_method=>:my_array_before_change)
93
- #Acceptable alernatives are:
94
- # @my_array.set_observer { |type,args| ... }
95
- # @my_array.set_observer(self, :pattern=>/before/) { |sender,type,args| ... }
96
- end
82
+ p.changed? => false
83
+ p.procedure.changed? => false
84
+ p.procedures[0].changed? => false
97
85
 
98
- def my_array_before_change(sender,type,args)
99
- #sender = @my_array
100
- #do something interesting, like, say, attribute_will_change!(:my_array)
101
- end
102
86
 
103
- end
104
87
 
105
- == Note on Patches/Pull Requests
88
+ == Note on Patches/Pull Requests
106
89
 
107
- * Fork the project.
108
- * Make your feature addition or bug fix.
109
- * Add tests for it. This is important so I don't break it in a
110
- future version unintentionally.
111
- * Commit, do not mess with rakefile, version, or history.
112
- (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
113
- * Send me a pull request. Bonus points for topic branches.
90
+ * Fork the project.
91
+ * Make your feature addition or bug fix.
92
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
93
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
94
+ * Send me a pull request. Bonus points for topic branches.
114
95
 
115
- == Copyright
96
+ == Copyright
116
97
 
117
- Copyright (c) 2010 Nathan Stults. See LICENSE for details.
98
+ Copyright (c) 1776 Nathan Stults. See LICENSE for details.
@@ -4,12 +4,13 @@ module MmDirtier
4
4
  module Dirtier
5
5
 
6
6
  def self.included(model)
7
+ model.plugin MongoMapper::Plugins::Dirty unless
8
+ model.plugins.include?(MongoMapper::Plugins::Dirty)
7
9
  model.plugin MmDirtier::Plugins::Dirtier
8
10
  end
9
11
 
10
12
  def self.configure(model)
11
- model.plugin MongoMapper::Plugins::Dirty unless
12
- model.plugins.include?(MongoMapper::Plugins::Dirty)
13
+
13
14
  end
14
15
 
15
16
  module InstanceMethods
@@ -1,6 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  module MongoMapper
3
3
  module Dirtier
4
- Version = '0.1.0'
4
+ Version = '0.1.1'
5
5
  end
6
- end
6
+ end
@@ -2,13 +2,17 @@ require 'test_helper'
2
2
 
3
3
  class OneEmbeddedProxyTest < Test::Unit::TestCase
4
4
  def setup
5
- @document = Doc { key :ary, Array }
5
+ @document = Doc("Doc") { key :ary, Array }
6
6
  end
7
7
 
8
8
  context "marking changes on one embedded proxies" do
9
9
  setup do
10
- @child = EDoc { key :name, String }
10
+ @child = EDoc("Child") { key :name, String }
11
+ @child.plugin MongoMapper::Plugins::Dirty
12
+ @child.plugin MmDirtier::Plugins::Dirtier
11
13
  @document.one :child, :class=>@child
14
+ @child.one :grandchild, :class=>@child
15
+
12
16
  end
13
17
 
14
18
  should "not happen if there are none" do
@@ -36,7 +40,7 @@ class OneEmbeddedProxyTest < Test::Unit::TestCase
36
40
  doc.child_changed?.should be_false
37
41
  end
38
42
 
39
- should "detect when a collection is set to nil" do
43
+ should "detect when a child is set to nil" do
40
44
  doc = @document.new
41
45
  c = doc.child.build
42
46
  doc.save!
@@ -64,5 +68,18 @@ class OneEmbeddedProxyTest < Test::Unit::TestCase
64
68
  doc.child.name = "hi there"
65
69
  doc.child_changed?.should be_false
66
70
  end
71
+
72
+ should "detect deletion on a nested embedded one" do
73
+ doc = @document.new
74
+ child = doc.child.build
75
+ grandchild = child.grandchild.build
76
+ doc.save!
77
+ child.name = "tada"
78
+ child.grandchild = nil
79
+ child.grandchild_changed?.should be_true
80
+ change = child.grandchild_change
81
+ change[0].should == grandchild
82
+ change[1].should be_nil
83
+ end
67
84
  end
68
85
  end
data/test/test_helper.rb CHANGED
@@ -29,7 +29,8 @@ class Test::Unit::TestCase
29
29
  end
30
30
 
31
31
  def EDoc(name='Class', &block)
32
- klass = Class.new do
32
+ klass = Class.new
33
+ klass.class_eval do
33
34
  include MongoMapper::EmbeddedDocument
34
35
 
35
36
  if name
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mm_dirtier
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 0
10
- version: 0.1.0
9
+ - 1
10
+ version: 0.1.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nathan Stults
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-16 00:00:00 -07:00
18
+ date: 2010-10-23 00:00:00 -07:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency