mongoid_alize 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. data/config/locales/en.yml +1 -0
  2. data/lib/mongoid/alize/callback.rb +48 -13
  3. data/lib/mongoid/alize/callbacks/from/many.rb +5 -5
  4. data/lib/mongoid/alize/callbacks/from/one.rb +14 -31
  5. data/lib/mongoid/alize/errors/invalid_configuration.rb +16 -0
  6. data/lib/mongoid/alize/from_callback.rb +4 -2
  7. data/lib/mongoid/alize/instance_helpers.rb +25 -0
  8. data/lib/mongoid/alize/macros.rb +65 -42
  9. data/lib/mongoid/alize/to_callback.rb +104 -9
  10. data/lib/mongoid_alize.rb +3 -7
  11. data/spec/app/models/head.rb +17 -1
  12. data/spec/app/models/mock_object.rb +6 -0
  13. data/spec/app/models/person.rb +16 -0
  14. data/spec/helpers/macros_helper.rb +11 -7
  15. data/spec/mongoid/alize/callback_spec.rb +62 -6
  16. data/spec/mongoid/alize/callbacks/from/one_spec.rb +41 -43
  17. data/spec/mongoid/alize/callbacks/to/many_from_many_spec.rb +23 -17
  18. data/spec/mongoid/alize/callbacks/to/many_from_one_spec.rb +158 -60
  19. data/spec/mongoid/alize/callbacks/to/one_from_many_spec.rb +110 -42
  20. data/spec/mongoid/alize/callbacks/to/one_from_one_spec.rb +167 -47
  21. data/spec/mongoid/alize/instance_helpers_spec.rb +36 -0
  22. data/spec/mongoid/alize/macros_spec.rb +90 -17
  23. data/spec/mongoid/alize/to_callback_spec.rb +87 -18
  24. data/spec/mongoid_alize_spec.rb +245 -26
  25. data/spec/spec_helper.rb +3 -1
  26. metadata +6 -8
  27. data/lib/mongoid/alize/callbacks/to/many_from_many.rb +0 -16
  28. data/lib/mongoid/alize/callbacks/to/many_from_one.rb +0 -15
  29. data/lib/mongoid/alize/callbacks/to/one_from_many.rb +0 -15
  30. data/lib/mongoid/alize/callbacks/to/one_from_one.rb +0 -15
  31. data/lib/mongoid/alize/to_many_callback.rb +0 -50
  32. data/lib/mongoid/alize/to_one_callback.rb +0 -43
@@ -1,23 +1,18 @@
1
1
  require 'mongoid/alize/errors/alize_error'
2
2
  require 'mongoid/alize/errors/invalid_field'
3
3
  require 'mongoid/alize/errors/already_defined_field'
4
+ require 'mongoid/alize/errors/invalid_configuration'
4
5
 
5
6
  require 'mongoid/alize/callback'
6
7
 
7
8
  require 'mongoid/alize/from_callback.rb'
8
9
  require 'mongoid/alize/to_callback.rb'
9
- require 'mongoid/alize/to_one_callback.rb'
10
- require 'mongoid/alize/to_many_callback.rb'
11
10
 
12
11
  require 'mongoid/alize/callbacks/from/one.rb'
13
12
  require 'mongoid/alize/callbacks/from/many.rb'
14
13
 
15
- require 'mongoid/alize/callbacks/to/one_from_one.rb'
16
- require 'mongoid/alize/callbacks/to/one_from_many.rb'
17
- require 'mongoid/alize/callbacks/to/many_from_one.rb'
18
- require 'mongoid/alize/callbacks/to/many_from_many.rb'
19
-
20
14
  require 'mongoid/alize/macros'
15
+ require 'mongoid/alize/instance_helpers'
21
16
 
22
17
  I18n.load_path << File.join(File.dirname(__FILE__), "..", "config", "locales", "en.yml")
23
18
 
@@ -27,6 +22,7 @@ module Mongoid
27
22
 
28
23
  included do
29
24
  extend Mongoid::Alize::Macros
25
+ include Mongoid::Alize::InstanceHelpers
30
26
  end
31
27
  end
32
28
  end
@@ -8,7 +8,7 @@ class Head
8
8
  # to whom it's attached
9
9
  belongs_to :person
10
10
 
11
- # in who's possession it is
11
+ # in whose possession it is
12
12
  belongs_to :captor, :class_name => "Person", :inverse_of => :heads
13
13
 
14
14
  # who'd otherwise like to possess it
@@ -20,7 +20,23 @@ class Head
20
20
  # a relation with no inverse
21
21
  has_many :admirer, :class_name => "Person", :inverse_of => nil
22
22
 
23
+ # a polymorphic one-to-one relation
24
+ belongs_to :nearest, :polymorphic => true
25
+
26
+ # a polymorphic one-to-many relation
27
+ has_many :below_people, :class_name => "Person", :as => :above
28
+
23
29
  def density
24
30
  "low"
25
31
  end
32
+
33
+ # example of one way to handling attribute selection
34
+ # for polymorphic associations or generally using the proc fields option
35
+ def alize_fields(inverse)
36
+ if inverse.is_a?(Person)
37
+ [:name, :location]
38
+ else
39
+ [:id]
40
+ end
41
+ end
26
42
  end
@@ -0,0 +1,6 @@
1
+ # explicit mock object class due to this issue - https://github.com/btakita/rr/issues/44
2
+ class MockObject
3
+ def to_ary
4
+ nil
5
+ end
6
+ end
@@ -17,8 +17,24 @@ 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
+ # a polymorphic one-to-one relation
21
+ has_one :nearest_head, :class_name => "Head", :as => :nearest
22
+
23
+ # a polymorphic one-to-many relation
24
+ belongs_to :above, :polymorphic => true
25
+
20
26
  def location
21
27
  "Paris"
22
28
  end
29
+
30
+ # example of one way to handling attribute selection
31
+ # for polymorphic associations or generally using the proc fields option
32
+ def alize_fields(inverse)
33
+ if inverse.is_a?(Head)
34
+ [:size]
35
+ else
36
+ [:id]
37
+ end
38
+ end
23
39
  end
24
40
 
@@ -9,15 +9,15 @@ module MacrosHelper
9
9
  end
10
10
 
11
11
  def tns
12
- Mongoid::Alize::Callbacks::To
12
+ Mongoid::Alize::ToCallback
13
13
  end
14
14
 
15
15
  def it_should_set_callbacks(klass, inverse_klass, relation, inverse_relation, fns, tns)
16
16
  fields = [:fake]
17
17
 
18
18
  it "should use #{fns} to pull" do
19
- obj_mock = Object.new
20
- obj_stub = Object.new
19
+ obj_mock = MockObject.new
20
+ obj_stub = MockObject.new
21
21
 
22
22
  stub(tns).new { obj_stub }
23
23
  stub(obj_stub).attach
@@ -25,12 +25,14 @@ module MacrosHelper
25
25
 
26
26
  mock(fns).new(klass, relation, fields) { obj_mock }
27
27
  mock(obj_mock).attach
28
- klass.send(:alize, relation, *fields)
28
+ klass.send(:alize_from, relation, *fields)
29
+
30
+ klass.alize_from_callbacks.should == [obj_mock]
29
31
  end
30
32
 
31
33
  it "should use #{tns} to push" do
32
- obj_stub = Object.new
33
- obj_mock = Object.new
34
+ obj_stub = MockObject.new
35
+ obj_mock = MockObject.new
34
36
 
35
37
  stub(fns).new { obj_stub }
36
38
  stub(obj_stub).attach
@@ -38,7 +40,9 @@ module MacrosHelper
38
40
 
39
41
  mock(tns).new(inverse_klass, inverse_relation, fields) { obj_mock }
40
42
  mock(obj_mock).attach
41
- klass.send(:alize, relation, *fields)
43
+ inverse_klass.send(:alize_to, inverse_relation, *fields)
44
+
45
+ inverse_klass.alize_to_callbacks.should == [obj_mock]
42
46
  end
43
47
  end
44
48
  end
@@ -33,24 +33,32 @@ describe Mongoid::Alize::Callback do
33
33
  callback.relation.should == :person
34
34
  callback.inverse_klass = Person
35
35
  callback.inverse_relation = :head
36
+ callback.inverse_metadata.should == Person.relations["head"]
36
37
  callback.fields.should == [:name, :created_at]
37
38
  end
38
39
 
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
40
+ it "should not set inverses for the child in a polymorphic association" do
41
+ callback = klass.new(Head, :nearest, [:size])
42
+ callback.inverse_klass.should be_nil
43
+ callback.inverse_relation.should be_nil
44
+ end
45
+
46
+ it "should set inverses for the parent in a polymorphic association" do
47
+ callback = klass.new(Person, :nearest_head, [:size])
48
+ callback.inverse_klass.should == Head
49
+ callback.inverse_relation.should == :nearest
43
50
  end
44
51
  end
45
52
 
46
- describe "with callback " do
53
+ describe "with callback" do
47
54
  before do
48
55
  @callback = new_callback
49
56
  end
50
57
 
51
58
  describe "#alias_callback" do
52
- it "should alias the callback on the klass" do
59
+ it "should alias the callback on the klass and make it public" do
53
60
  mock(@callback.klass).alias_method("denormalize_spec_person", "_denormalize_spec_person")
61
+ mock(@callback.klass).public("denormalize_spec_person")
54
62
  @callback.send(:alias_callback)
55
63
  end
56
64
 
@@ -61,5 +69,53 @@ describe Mongoid::Alize::Callback do
61
69
  end
62
70
  end
63
71
  end
72
+
73
+ describe "name helpers" do
74
+ before do
75
+ @callback = new_callback
76
+ end
77
+
78
+ it "should have a callback name" do
79
+ @callback.callback_name.should == "_denormalize_spec_person"
80
+ end
81
+
82
+ it "should have aliased callback name" do
83
+ @callback.aliased_callback_name.should == "denormalize_spec_person"
84
+ end
85
+
86
+ it "should add _fields to the callback name" do
87
+ @callback.fields_method_name.should == "_denormalize_spec_person_fields"
88
+ end
89
+ end
90
+
91
+ describe "define fields method" do
92
+ def define_fields_method
93
+ @callback.send(:define_fields_method)
94
+ end
95
+
96
+ describe "when fields is an array" do
97
+ before do
98
+ @callback = new_callback
99
+ end
100
+
101
+ it "should return the fields w/ to_s applied" do
102
+ define_fields_method
103
+ @head = Head.new
104
+ @head.send("_denormalize_spec_person_fields", nil).should == ["name", "created_at"]
105
+ end
106
+ end
107
+
108
+ describe "when fields is a proc" do
109
+ before do
110
+ @callback = klass.new(Head, :person, lambda { |inverse| [:name, :created_at] })
111
+ end
112
+
113
+ it "should return the fields w/ to_s applied" do
114
+ define_fields_method
115
+ @head = Head.new
116
+ @head.send("_denormalize_spec_person_fields", Person.new).should == ["name", "created_at"]
117
+ end
118
+ end
119
+ end
64
120
  end
65
121
 
@@ -14,39 +14,29 @@ describe Mongoid::Alize::Callbacks::From::One do
14
14
  end
15
15
 
16
16
  describe "#define_fields" do
17
- it "should add properly typed, prefixed fields from the relation" do
18
- callback = klass.new(Head, :person, [:name, :created_at])
19
- callback.send(:define_fields)
20
- Head.fields["person_name"].type.should == String
21
- Head.fields["person_created_at"].type.should == Time
22
- end
23
-
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
17
+ describe "with an array of fields" do
18
+ it "should add a field generated from %{relation}_fields" do
19
+ callback = new_callback
20
+ callback.send(:define_fields)
21
+ Head.fields["person_fields"].type.should == Hash
33
22
  end
34
- callback = klass.new(Head, :person, [:name])
35
- dont_allow(Head).field
36
- callback.send(:define_fields)
37
- end
38
23
 
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
24
+ it "should default the field to nil" do
25
+ callback = new_callback
26
+ callback.send(:define_fields)
27
+ Head.new.person_fields.should be_nil
28
+ end
44
29
 
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
30
+ it "should raise an already defined field error if the field already exists" do
31
+ Head.class_eval do
32
+ field :person_fields
33
+ end
34
+ callback = new_callback
35
+ expect {
36
+ callback.send(:define_fields)
37
+ }.to raise_error(Mongoid::Alize::Errors::AlreadyDefinedField,
38
+ "person_fields is already defined on the Head model.")
39
+ end
50
40
  end
51
41
  end
52
42
 
@@ -55,37 +45,43 @@ describe Mongoid::Alize::Callbacks::From::One do
55
45
  @head.send(:_denormalize_from_person, force)
56
46
  end
57
47
 
58
- before do
48
+ def person_fields
49
+ { "name"=> "Bob",
50
+ "location" => "Paris",
51
+ "created_at"=> @now.to_s(:utc) }
52
+ end
53
+
54
+ def create_models
59
55
  @head = Head.create
60
56
  @person = Person.create(:name => @name = "Bob",
61
57
  :created_at => @now = Time.now)
58
+ end
62
59
 
60
+ before do
63
61
  @callback = new_callback
64
62
  @callback.send(:define_fields)
63
+ create_models
65
64
  @callback.send(:define_callback)
66
-
67
- @head.relations["person"].should be_stores_foreign_key
68
65
  end
69
66
 
70
67
  it "should set fields from a changed relation" do
71
68
  @head.person = @person
72
69
  @head.should be_person_id_changed
73
70
  run_callback
74
- @head.person_name.should == @name
75
- @head.person_created_at.to_i.should == @now.to_i
76
- @head.person_location.should == "Paris"
71
+ @head.person_fields.should == person_fields
77
72
  end
78
73
 
79
- it "should assign nil values if the changed relation is nil" do
74
+ it "should set no fields from a nil relation" do
80
75
  @head.person = @person
81
76
  @head.save!
82
77
  @head.person = nil
83
78
  @head.should be_person_id_changed
84
79
  run_callback
85
- @head.person_name.should be_nil
80
+ @head.person_fields.should == nil
86
81
  end
87
82
 
88
83
  it "should not run if the relation has not changed" do
84
+ @head.relations["person"].should be_stores_foreign_key
89
85
  @head.should_not be_person_id_changed
90
86
  dont_allow(@head).person
91
87
  run_callback
@@ -111,20 +107,22 @@ describe Mongoid::Alize::Callbacks::From::One do
111
107
  end
112
108
 
113
109
  before do
110
+ @callback = klass.new(Person, :head, [:size])
111
+ @callback.send(:define_fields)
112
+
114
113
  @person = Person.create
115
114
  @head = Head.create(:size => 5)
116
115
 
117
- @callback = klass.new(Person, :head, [:size])
118
- @callback.send(:define_fields)
119
116
  @callback.send(:define_callback)
120
-
121
117
  @person.relations["head"].should_not be_stores_foreign_key
122
118
  end
123
119
 
124
120
  it "should set values from a changed relation" do
125
121
  @person.head = @head
126
122
  run_callback
127
- @person.head_size.should == 5
123
+ @person.head_fields.should == {
124
+ "size" => 5
125
+ }
128
126
  end
129
127
 
130
128
  it "should set values from a a nil relation" do
@@ -132,7 +130,7 @@ describe Mongoid::Alize::Callbacks::From::One do
132
130
  @person.save!
133
131
  @person.head = nil
134
132
  run_callback
135
- @person.head_size.should be_nil
133
+ @person.head_fields.should be_nil
136
134
  end
137
135
 
138
136
  it "should run even if the relation has not changed" do
@@ -1,8 +1,8 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Mongoid::Alize::Callbacks::To::ManyFromMany do
3
+ describe Mongoid::Alize::ToCallback do
4
4
  def klass
5
- Mongoid::Alize::Callbacks::To::ManyFromMany
5
+ Mongoid::Alize::ToCallback
6
6
  end
7
7
 
8
8
  def args
@@ -24,30 +24,33 @@ describe Mongoid::Alize::Callbacks::To::ManyFromMany do
24
24
  { "_id" => "SomeObjectId" }
25
25
  end
26
26
 
27
- before do
28
- Head.class_eval do
29
- field :wanted_by_fields, type: Array
30
- end
31
-
27
+ def create_models
32
28
  @head = Head.create(
33
29
  :wanted_by => [@person = Person.create(:name => "Bob",
34
30
  :created_at => @now = Time.now)])
35
31
  @person.wants = [@head]
36
-
37
- @callback = new_callback
38
32
  end
39
33
 
40
- describe "#define_callback" do
41
- before do
42
- @callback.send(:define_callback)
34
+ before do
35
+ Head.class_eval do
36
+ field :wanted_by_fields, type: Array, :default => []
43
37
  end
38
+ end
44
39
 
40
+ describe "#define_callback" do
45
41
  def run_callback
46
42
  @person.send(:_denormalize_to_wants)
47
43
  end
48
44
 
45
+ before do
46
+ @callback = new_callback
47
+ @callback.send(:define_fields)
48
+ create_models
49
+ @callback.send(:define_callback)
50
+ end
51
+
49
52
  it "should push the fields to the relation" do
50
- @head.wanted_by_fields.should be_nil
53
+ @head.wanted_by_fields.should == []
51
54
  run_callback
52
55
  @head.wanted_by_fields.should == [wanted_by_fields]
53
56
  end
@@ -67,14 +70,17 @@ describe Mongoid::Alize::Callbacks::To::ManyFromMany do
67
70
  end
68
71
 
69
72
  describe "#define_destroy_callback" do
70
- before do
71
- @callback.send(:define_destroy_callback)
72
- end
73
-
74
73
  def run_destroy_callback
75
74
  @person.send(:_denormalize_destroy_to_wants)
76
75
  end
77
76
 
77
+ before do
78
+ @callback = new_callback
79
+ @callback.send(:define_fields)
80
+ create_models
81
+ @callback.send(:define_destroy_callback)
82
+ end
83
+
78
84
  it "should pull first any existing array entries matching the _id" do
79
85
  @head.wanted_by_fields = [wanted_by_fields, other_wanted_by]
80
86
  @head.save!