mongoid_alize 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. data/config/locales/en.yml +10 -0
  2. data/lib/mongoid/alize/callback.rb +47 -3
  3. data/lib/mongoid/alize/callbacks/from/many.rb +6 -8
  4. data/lib/mongoid/alize/callbacks/from/one.rb +21 -9
  5. data/lib/mongoid/alize/callbacks/to/many_from_many.rb +2 -1
  6. data/lib/mongoid/alize/callbacks/to/many_from_one.rb +1 -1
  7. data/lib/mongoid/alize/callbacks/to/one_from_many.rb +1 -1
  8. data/lib/mongoid/alize/callbacks/to/one_from_one.rb +1 -1
  9. data/lib/mongoid/alize/errors/alize_error.rb +13 -0
  10. data/lib/mongoid/alize/errors/already_defined_field.rb +15 -0
  11. data/lib/mongoid/alize/errors/invalid_field.rb +15 -0
  12. data/lib/mongoid/alize/from_callback.rb +16 -3
  13. data/lib/mongoid/alize/to_callback.rb +29 -5
  14. data/lib/mongoid/alize/to_many_callback.rb +26 -21
  15. data/lib/mongoid/alize/to_one_callback.rb +2 -3
  16. data/lib/mongoid_alize.rb +6 -1
  17. data/spec/app/models/head.rb +4 -0
  18. data/spec/app/models/person.rb +3 -0
  19. data/spec/mongoid/alize/callback_spec.rb +46 -8
  20. data/spec/mongoid/alize/callbacks/from/many_spec.rb +92 -18
  21. data/spec/mongoid/alize/callbacks/from/one_spec.rb +106 -23
  22. data/spec/mongoid/alize/callbacks/to/many_from_many_spec.rb +9 -16
  23. data/spec/mongoid/alize/callbacks/to/many_from_one_spec.rb +8 -15
  24. data/spec/mongoid/alize/callbacks/to/one_from_many_spec.rb +14 -17
  25. data/spec/mongoid/alize/callbacks/to/one_from_one_spec.rb +12 -15
  26. data/spec/mongoid/alize/from_callback_spec.rb +44 -0
  27. data/spec/mongoid/alize/to_callback_spec.rb +74 -0
  28. data/spec/mongoid_alize_spec.rb +152 -16
  29. data/spec/spec_helper.rb +6 -0
  30. metadata +27 -21
@@ -19,4 +19,8 @@ class Head
19
19
 
20
20
  # a relation with no inverse
21
21
  has_many :admirer, :class_name => "Person", :inverse_of => nil
22
+
23
+ def density
24
+ "low"
25
+ end
22
26
  end
@@ -17,5 +17,8 @@ class Person
17
17
  # the only head that is watching
18
18
  belongs_to :seen_by, :class_name => "Head", :inverse_of => :sees
19
19
 
20
+ def location
21
+ "Paris"
22
+ end
20
23
  end
21
24
 
@@ -1,26 +1,64 @@
1
1
  require 'spec_helper'
2
2
 
3
+ class Mongoid::Alize::SpecCallback < Mongoid::Alize::Callback
4
+ def attach
5
+ klass.class_eval do
6
+ def denormalize_spec_person
7
+ end
8
+ end
9
+ end
10
+
11
+ def direction
12
+ "spec"
13
+ end
14
+ end
15
+
3
16
  describe Mongoid::Alize::Callback do
4
17
  def klass
5
- Mongoid::Alize::Callback
18
+ Mongoid::Alize::SpecCallback
6
19
  end
7
20
 
8
21
  def args
9
22
  [Head, :person, [:name, :created_at]]
10
23
  end
11
24
 
12
- def new_unit
25
+ def new_callback
13
26
  klass.new(*args)
14
27
  end
15
28
 
16
29
  describe "initialize" do
17
30
  it "should assign class attributes" do
18
- unit = new_unit
19
- unit.klass.should == Head
20
- unit.relation.should == :person
21
- unit.inverse_klass = Person
22
- unit.inverse_relation = :head
23
- unit.fields.should == [:name, :created_at]
31
+ callback = new_callback
32
+ callback.klass.should == Head
33
+ callback.relation.should == :person
34
+ callback.inverse_klass = Person
35
+ callback.inverse_relation = :head
36
+ callback.fields.should == [:name, :created_at]
37
+ end
38
+
39
+ it "should add force_denormalization attribute to klass and inverse klass" do
40
+ callback = new_callback
41
+ Head.new.force_denormalization = true
42
+ Person.new.force_denormalization = true
43
+ end
44
+ end
45
+
46
+ describe "with callback " do
47
+ before do
48
+ @callback = new_callback
49
+ end
50
+
51
+ describe "#alias_callback" do
52
+ it "should alias the callback on the klass" do
53
+ mock(@callback.klass).alias_method("denormalize_spec_person", "_denormalize_spec_person")
54
+ @callback.send(:alias_callback)
55
+ end
56
+
57
+ it "should not alias the callback if it's already set" do
58
+ @callback.send(:attach)
59
+ dont_allow(@callback.klass).alias_method
60
+ @callback.send(:alias_callback)
61
+ end
24
62
  end
25
63
  end
26
64
  end
@@ -6,47 +6,121 @@ describe Mongoid::Alize::Callbacks::From::Many do
6
6
  end
7
7
 
8
8
  def args
9
- [Head, :wanted_by, [:name, :created_at]]
9
+ [Head, :wanted_by, [:name, :location, :created_at]]
10
10
  end
11
11
 
12
- def new_unit
12
+ def new_callback
13
13
  klass.new(*args)
14
14
  end
15
15
 
16
16
  describe "#define_fields" do
17
17
  it "should define an Array called {relation}_fields" do
18
- unit = new_unit
19
- unit.send(:define_fields)
18
+ callback = new_callback
19
+ callback.send(:define_fields)
20
20
  Head.fields["wanted_by_fields"].type.should == Array
21
21
  end
22
+
23
+ it "should default the field to empty" do
24
+ callback = new_callback
25
+ callback.send(:define_fields)
26
+ Head.new.wanted_by_fields.should == []
27
+ end
28
+
29
+ it "should raise an already defined field error if the field already exists" do
30
+ Head.class_eval do
31
+ field :wanted_by_fields
32
+ end
33
+ callback = new_callback
34
+ expect {
35
+ callback.send(:define_fields)
36
+ }.to raise_error(Mongoid::Alize::Errors::AlreadyDefinedField,
37
+ "wanted_by_fields is already defined on the Head model.")
38
+ end
22
39
  end
23
40
 
24
41
  describe "the defined callback" do
25
42
  def run_callback
26
- @head.send(callback_name)
43
+ @head.send(:_denormalize_from_wanted_by)
44
+ end
45
+
46
+ def bob_fields
47
+ { "_id" => @person.id,
48
+ "name"=> "Bob",
49
+ "location" => "Paris",
50
+ "created_at"=> @now.to_s(:utc) }
51
+ end
52
+
53
+ before do
54
+ now = Time.now
55
+ stub(Time).now { now }
56
+ @head = Head.create
57
+ @person = Person.create(:name => "Bob", :created_at => @now = Time.now)
58
+
59
+ @head.relations["wanted_by"].should be_stores_foreign_key
27
60
  end
28
61
 
29
- def callback_name
30
- "denormalize_from_wanted_by"
62
+ describe "valid fields" do
63
+ before do
64
+ @callback = new_callback
65
+ @callback.send(:define_fields)
66
+ @callback.send(:define_callback)
67
+ end
68
+
69
+ it "should set fields from a changed relation" do
70
+ @head.wanted_by_ids << @person.id
71
+ @head.should be_wanted_by_ids_changed
72
+ run_callback
73
+ @head.wanted_by_fields.should == [bob_fields]
74
+ end
75
+
76
+ it "should still set fields there are no changes" do
77
+ @head.should_not be_wanted_by_ids_changed
78
+ mock.proxy(@head).wanted_by
79
+ run_callback
80
+ end
81
+ end
82
+
83
+ describe "with a field that doesn't exist" do
84
+ before do
85
+ @callback = klass.new(Head, :wanted_by, [:notreal])
86
+ @callback.send(:define_fields)
87
+ @callback.send(:define_callback)
88
+ end
89
+
90
+ it "should raise a no method error" do
91
+ @head.wanted_by_ids << @person.id
92
+ @head.wanted_by_fields.should be_nil
93
+ expect {
94
+ run_callback
95
+ }.to raise_error NoMethodError
96
+ end
97
+ end
98
+ end
99
+
100
+ describe "in a one to many case" do
101
+ def run_callback
102
+ @head.send(:_denormalize_from_sees)
31
103
  end
32
104
 
33
105
  before do
34
- @head = Head.create(
35
- :wanted_by => [@person = Person.create(:name => "Bob",
36
- :created_at => @now = Time.now)])
106
+ now = Time.now
107
+ stub(Time).now { now }
108
+ @head = Head.create
109
+ @person = Person.create(:name => "Bob")
37
110
 
38
- @unit = new_unit
39
- @unit.send(:define_fields)
40
- @unit.send(:define_callback)
111
+ @callback = klass.new(Head, :sees, [:name])
112
+ @callback.send(:define_fields)
113
+ @callback.send(:define_callback)
114
+
115
+ @head.relations["sees"].should_not be_stores_foreign_key
41
116
  end
42
117
 
43
- it "should pull the fields from the relation" do
44
- @head.wanted_by_fields.should be_nil
118
+ it "should field from a changed relation" do
119
+ @head.sees << @person
45
120
  run_callback
46
- @head.wanted_by_fields.should == [{
121
+ @head.sees_fields.should == [{
47
122
  "_id" => @person.id,
48
- "name"=> "Bob",
49
- "created_at"=> @now.to_s(:utc)
123
+ "name" => "Bob"
50
124
  }]
51
125
  end
52
126
  end
@@ -6,55 +6,138 @@ describe Mongoid::Alize::Callbacks::From::One do
6
6
  end
7
7
 
8
8
  def args
9
- [Head, :person, [:name, :created_at]]
9
+ [Head, :person, [:name, :location, :created_at]]
10
10
  end
11
11
 
12
- def new_unit
12
+ def new_callback
13
13
  klass.new(*args)
14
14
  end
15
15
 
16
16
  describe "#define_fields" do
17
17
  it "should add properly typed, prefixed fields from the relation" do
18
- unit = new_unit
19
- unit.send(:define_fields)
18
+ callback = klass.new(Head, :person, [:name, :created_at])
19
+ callback.send(:define_fields)
20
20
  Head.fields["person_name"].type.should == String
21
21
  Head.fields["person_created_at"].type.should == Time
22
22
  end
23
- end
24
23
 
25
- describe "the defined callback" do
26
- def run_callback
27
- @head.send(callback_name)
24
+ it "should define as a string field that's not defined" do
25
+ callback = klass.new(Head, :person, [:location])
26
+ callback.send(:define_fields)
27
+ Head.fields["person_location"].type.should == String
28
+ end
29
+
30
+ it "should not add the field if the field already exists" do
31
+ Head.class_eval do
32
+ field :person_name
33
+ end
34
+ callback = klass.new(Head, :person, [:name])
35
+ dont_allow(Head).field
36
+ callback.send(:define_fields)
28
37
  end
29
38
 
30
- def callback_name
31
- "denormalize_from_person"
39
+ it "should not add the field if it already exists b/c of another relation" do
40
+ callback = klass.new(Head, :person, [:id])
41
+ dont_allow(Head).field
42
+ callback.send(:define_fields)
43
+ end
44
+
45
+ it "should allow the id and type of the inverse to be denormalized without an extra _" do
46
+ callback = klass.new(Person, :head, [:id, :type])
47
+ callback.send(:define_fields)
48
+ Person.fields["head_id"].type.should == BSON::ObjectId
49
+ Person.fields["head_type"].type.should == String
50
+ end
51
+ end
52
+
53
+ describe "the defined callback" do
54
+ def run_callback(force=false)
55
+ @head.send(:_denormalize_from_person, force)
32
56
  end
33
57
 
34
58
  before do
35
- @head = Head.create(
36
- :person => @person = Person.create(:name => @name = "Bob",
37
- :created_at => @now = Time.now))
38
- @unit = new_unit
39
- @unit.send(:define_fields)
40
- @unit.send(:define_callback)
59
+ @head = Head.create
60
+ @person = Person.create(:name => @name = "Bob",
61
+ :created_at => @now = Time.now)
62
+
63
+ @callback = new_callback
64
+ @callback.send(:define_fields)
65
+ @callback.send(:define_callback)
66
+
67
+ @head.relations["person"].should be_stores_foreign_key
41
68
  end
42
69
 
43
- it "should set pull fields from the relation" do
44
- @head.person_name.should be_nil
45
- @head.person_created_at.should be_nil
70
+ it "should set fields from a changed relation" do
71
+ @head.person = @person
72
+ @head.should be_person_id_changed
46
73
  run_callback
47
74
  @head.person_name.should == @name
48
75
  @head.person_created_at.to_i.should == @now.to_i
76
+ @head.person_location.should == "Paris"
49
77
  end
50
78
 
51
- it "should assign nil values if the relation is nil" do
52
- @head.person_name = "not nil"
53
- @head.person_created_at = Time.now
79
+ it "should assign nil values if the changed relation is nil" do
80
+ @head.person = @person
81
+ @head.save!
54
82
  @head.person = nil
83
+ @head.should be_person_id_changed
55
84
  run_callback
56
85
  @head.person_name.should be_nil
57
- @head.person_created_at.should be_nil
86
+ end
87
+
88
+ it "should not run if the relation has not changed" do
89
+ @head.should_not be_person_id_changed
90
+ dont_allow(@head).person
91
+ run_callback
92
+ end
93
+
94
+ it "should still run if the relation has not changed but force is passed" do
95
+ @head.should_not be_person_id_changed
96
+ mock.proxy(@head).person
97
+ run_callback(true)
98
+ end
99
+
100
+ it "should still run if the relation has not changed but force_denormalization is set on the class" do
101
+ @head.should_not be_person_id_changed
102
+ @head.force_denormalization = true
103
+ mock.proxy(@head).person
104
+ run_callback
105
+ end
106
+ end
107
+
108
+ describe "the defined callback when denormalizing on the has_one side" do
109
+ def run_callback
110
+ @person.send(:_denormalize_from_head)
111
+ end
112
+
113
+ before do
114
+ @person = Person.create
115
+ @head = Head.create(:size => 5)
116
+
117
+ @callback = klass.new(Person, :head, [:size])
118
+ @callback.send(:define_fields)
119
+ @callback.send(:define_callback)
120
+
121
+ @person.relations["head"].should_not be_stores_foreign_key
122
+ end
123
+
124
+ it "should set values from a changed relation" do
125
+ @person.head = @head
126
+ run_callback
127
+ @person.head_size.should == 5
128
+ end
129
+
130
+ it "should set values from a a nil relation" do
131
+ @person.head = @head
132
+ @person.save!
133
+ @person.head = nil
134
+ run_callback
135
+ @person.head_size.should be_nil
136
+ end
137
+
138
+ it "should run even if the relation has not changed" do
139
+ mock.proxy(@person).head
140
+ run_callback
58
141
  end
59
142
  end
60
143
  end
@@ -6,16 +6,17 @@ describe Mongoid::Alize::Callbacks::To::ManyFromMany do
6
6
  end
7
7
 
8
8
  def args
9
- [Person, :wants, [:name, :created_at]]
9
+ [Person, :wants, [:name, :location, :created_at]]
10
10
  end
11
11
 
12
- def new_unit
12
+ def new_callback
13
13
  klass.new(*args)
14
14
  end
15
15
 
16
16
  def wanted_by_fields
17
17
  { "_id" => @person.id,
18
- "name"=> "Bob",
18
+ "name" => "Bob",
19
+ "location" => "Paris",
19
20
  "created_at"=> @now.to_s(:utc) }
20
21
  end
21
22
 
@@ -33,20 +34,16 @@ describe Mongoid::Alize::Callbacks::To::ManyFromMany do
33
34
  :created_at => @now = Time.now)])
34
35
  @person.wants = [@head]
35
36
 
36
- @unit = new_unit
37
+ @callback = new_callback
37
38
  end
38
39
 
39
40
  describe "#define_callback" do
40
41
  before do
41
- @unit.send(:define_callback)
42
+ @callback.send(:define_callback)
42
43
  end
43
44
 
44
45
  def run_callback
45
- @person.send(callback_name)
46
- end
47
-
48
- def callback_name
49
- "denormalize_to_wants"
46
+ @person.send(:_denormalize_to_wants)
50
47
  end
51
48
 
52
49
  it "should push the fields to the relation" do
@@ -71,15 +68,11 @@ describe Mongoid::Alize::Callbacks::To::ManyFromMany do
71
68
 
72
69
  describe "#define_destroy_callback" do
73
70
  before do
74
- @unit.send(:define_destroy_callback)
71
+ @callback.send(:define_destroy_callback)
75
72
  end
76
73
 
77
74
  def run_destroy_callback
78
- @person.send(destroy_callback_name)
79
- end
80
-
81
- def destroy_callback_name
82
- "denormalize_destroy_to_wants"
75
+ @person.send(:_denormalize_destroy_to_wants)
83
76
  end
84
77
 
85
78
  it "should pull first any existing array entries matching the _id" do