mongoid_alize 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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