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
@@ -0,0 +1,10 @@
1
+ en:
2
+ mongoid:
3
+ errors:
4
+ messages:
5
+ alize:
6
+ invalid_field:
7
+ "%{name} does not exist on the %{inverse_klass} model."
8
+ already_defined_field:
9
+ "%{name} is already defined on the %{klass} model."
10
+
@@ -6,6 +6,7 @@ module Mongoid
6
6
 
7
7
  attr_accessor :klass
8
8
  attr_accessor :relation
9
+ attr_accessor :reflect
9
10
 
10
11
  attr_accessor :inverse_klass
11
12
  attr_accessor :inverse_relation
@@ -15,9 +16,12 @@ module Mongoid
15
16
  self.relation = _relation
16
17
  self.fields = _fields
17
18
 
18
- reflect = _klass.relations[_relation.to_s]
19
- self.inverse_klass = reflect.klass
20
- self.inverse_relation = reflect.inverse
19
+ self.reflect = _klass.relations[_relation.to_s]
20
+ self.inverse_klass = self.reflect.klass
21
+ self.inverse_relation = self.reflect.inverse
22
+
23
+ self.klass.send(:attr_accessor, :force_denormalization)
24
+ self.inverse_klass.send(:attr_accessor, :force_denormalization)
21
25
  end
22
26
 
23
27
  def attach
@@ -26,9 +30,49 @@ module Mongoid
26
30
 
27
31
  protected
28
32
 
33
+ def callback_attached?(callback_type, callback_name)
34
+ !!klass.send(:"_#{callback_type}_callbacks").
35
+ map(&:raw_filter).include?(callback_name)
36
+ end
37
+
38
+ def callback_defined?(callback_name)
39
+ klass.method_defined?(callback_name)
40
+ end
41
+
42
+ def alias_callback
43
+ unless callback_defined?(aliased_callback_name)
44
+ klass.send(:alias_method, aliased_callback_name, callback_name)
45
+ end
46
+ end
47
+
48
+ def callback_name
49
+ "_#{aliased_callback_name}"
50
+ end
51
+
52
+ def aliased_callback_name
53
+ "denormalize_#{direction}_#{relation}"
54
+ end
55
+
29
56
  def joined_fields
30
57
  (fields + [:_id]).map {|f| "'#{f}'" }.join(", ")
31
58
  end
59
+
60
+ def joined_field_values(source)
61
+ <<-RUBY
62
+ [#{joined_fields}].inject({}) { |hash, name|
63
+ hash[name] = #{source}.send(name)
64
+ hash
65
+ }
66
+ RUBY
67
+ end
68
+
69
+ def force_param
70
+ "(force=false)"
71
+ end
72
+
73
+ def force_check
74
+ "force || self.force_denormalization"
75
+ end
32
76
  end
33
77
  end
34
78
  end
@@ -8,22 +8,20 @@ module Mongoid
8
8
 
9
9
  def define_callback
10
10
  klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
11
- def #{callback_name}
11
+ def #{callback_name}#{force_param}
12
12
  self.#{prefixed_name} = self.#{relation}.map do |model|
13
- model.attributes.slice(#{joined_fields})
13
+ #{joined_field_values("model")}
14
14
  end
15
15
  end
16
-
17
16
  protected :#{callback_name}
18
17
  CALLBACK
19
18
  end
20
19
 
21
20
  def define_fields
22
- unless !!klass.fields[prefixed_name]
23
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
24
- field :#{prefixed_name}, :type => Array
25
- CALLBACK
26
- end
21
+ ensure_field_not_defined!(prefixed_name, klass)
22
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
23
+ field :#{prefixed_name}, :type => Array, :default => []
24
+ CALLBACK
27
25
  end
28
26
 
29
27
  def prefixed_name
@@ -9,13 +9,18 @@ module Mongoid
9
9
  def define_callback
10
10
  field_sets = ""
11
11
  fields.each do |name|
12
- field_sets << "self.set(:#{prefixed_field_name(name)}, relation && relation.read_attribute(:#{name}))\n"
12
+ field_sets << "self.send(:#{prefixed_field_name(name)}=,
13
+ relation && relation.send(:#{name}))\n"
13
14
  end
14
15
 
15
16
  klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
16
- def #{callback_name}
17
- relation = self.#{relation}
18
- #{field_sets}
17
+ def #{callback_name}#{force_param}
18
+ if #{force_check} ||
19
+ #{!reflect.stores_foreign_key?} ||
20
+ self.#{reflect.key}_changed?
21
+ relation = self.#{relation}
22
+ #{field_sets}
23
+ end
19
24
  true
20
25
  end
21
26
 
@@ -26,9 +31,11 @@ module Mongoid
26
31
  def define_fields
27
32
  fields.each do |name|
28
33
  prefixed_name = prefixed_field_name(name)
29
- klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
30
- field :#{prefixed_name}, :type => #{inverse_field_type(name)}
31
- CALLBACK
34
+ unless field_defined?(prefixed_name, klass)
35
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
36
+ field :#{prefixed_name}, :type => #{inverse_field_type(name)}
37
+ CALLBACK
38
+ end
32
39
  end
33
40
  end
34
41
 
@@ -37,8 +44,13 @@ module Mongoid
37
44
  end
38
45
 
39
46
  def inverse_field_type(name)
40
- field = inverse_klass.fields[name.to_s]
41
- field.options[:type] ? field.type : String
47
+ name = name.to_s
48
+
49
+ name = "_id" if name == "id"
50
+ name = "_type" if name == "type"
51
+
52
+ field = inverse_klass.fields[name]
53
+ (field && field.options[:type]) ? field.type : String
42
54
  end
43
55
  end
44
56
  end
@@ -6,8 +6,9 @@ module Mongoid
6
6
  protected
7
7
 
8
8
  def iterable_relation
9
- "self.#{relation}"
9
+ plain_relation
10
10
  end
11
+
11
12
  end
12
13
  end
13
14
  end
@@ -6,7 +6,7 @@ module Mongoid
6
6
  protected
7
7
 
8
8
  def iterable_relation
9
- "self.#{relation} ? [self.#{relation}] : []"
9
+ surrounded_relation
10
10
  end
11
11
  end
12
12
  end
@@ -6,7 +6,7 @@ module Mongoid
6
6
  protected
7
7
 
8
8
  def iterable_relation
9
- "self.#{relation}"
9
+ plain_relation
10
10
  end
11
11
  end
12
12
  end
@@ -6,7 +6,7 @@ module Mongoid
6
6
  protected
7
7
 
8
8
  def iterable_relation
9
- "self.#{relation} ? [self.#{relation}] : []"
9
+ surrounded_relation
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,13 @@
1
+ module Mongoid
2
+ module Alize
3
+ module Errors
4
+
5
+ class AlizeError < Mongoid::Errors::MongoidError
6
+ def translate(key, data={})
7
+ super("alize.#{key}", data)
8
+ end
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Mongoid
2
+ module Alize
3
+ module Errors
4
+
5
+ class AlreadyDefinedField < AlizeError
6
+ def initialize(name, klass)
7
+ super(
8
+ translate("already_defined_field", { :name => name, :klass => klass })
9
+ )
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Mongoid
2
+ module Alize
3
+ module Errors
4
+
5
+ class InvalidField < AlizeError
6
+ def initialize(name, inverse_klass)
7
+ super(
8
+ translate("invalid_field", { :name => name, :inverse_klass => inverse_klass })
9
+ )
10
+ end
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -6,17 +6,30 @@ module Mongoid
6
6
  define_fields
7
7
 
8
8
  define_callback
9
+ alias_callback
9
10
  set_callback
10
11
  end
11
12
 
12
13
  protected
13
14
 
14
15
  def set_callback
15
- klass.set_callback(:create, :before, callback_name)
16
+ unless callback_attached?("save", aliased_callback_name)
17
+ klass.set_callback(:save, :before, aliased_callback_name)
18
+ end
16
19
  end
17
20
 
18
- def callback_name
19
- "denormalize_from_#{relation}"
21
+ def ensure_field_not_defined!(prefixed_name, klass)
22
+ if field_defined?(prefixed_name, klass)
23
+ raise Mongoid::Alize::Errors::AlreadyDefinedField.new(prefixed_name, klass.name)
24
+ end
25
+ end
26
+
27
+ def field_defined?(prefixed_name, klass)
28
+ !!klass.fields[prefixed_name]
29
+ end
30
+
31
+ def direction
32
+ "from"
20
33
  end
21
34
  end
22
35
  end
@@ -4,28 +4,52 @@ module Mongoid
4
4
 
5
5
  def attach
6
6
  define_callback
7
+ alias_callback
7
8
  set_callback
8
9
 
9
10
  define_destroy_callback
11
+ alias_destroy_callback
10
12
  set_destroy_callback
11
13
  end
12
14
 
13
15
  protected
14
16
 
15
17
  def set_callback
16
- klass.set_callback(:save, :after, callback_name)
18
+ unless callback_attached?("save", aliased_callback_name)
19
+ klass.set_callback(:save, :after, aliased_callback_name)
20
+ end
17
21
  end
18
22
 
19
23
  def set_destroy_callback
20
- klass.set_callback(:destroy, :after, destroy_callback_name)
24
+ unless callback_attached?("destroy", aliased_destroy_callback_name)
25
+ klass.set_callback(:destroy, :after, aliased_destroy_callback_name)
26
+ end
21
27
  end
22
28
 
23
- def callback_name
24
- "denormalize_to_#{relation}"
29
+ def alias_destroy_callback
30
+ unless callback_defined?(aliased_destroy_callback_name)
31
+ klass.send(:alias_method, aliased_destroy_callback_name, destroy_callback_name)
32
+ end
33
+ end
34
+
35
+ def aliased_destroy_callback_name
36
+ "denormalize_destroy_#{direction}_#{relation}"
25
37
  end
26
38
 
27
39
  def destroy_callback_name
28
- "denormalize_destroy_to_#{relation}"
40
+ "_#{aliased_destroy_callback_name}"
41
+ end
42
+
43
+ def plain_relation
44
+ "self.#{relation}"
45
+ end
46
+
47
+ def surrounded_relation
48
+ "self.#{relation} ? [self.#{relation}] : []"
49
+ end
50
+
51
+ def direction
52
+ "to"
29
53
  end
30
54
  end
31
55
  end
@@ -4,36 +4,41 @@ module Mongoid
4
4
  protected
5
5
 
6
6
  def define_callback
7
- _callback(callback_name,
8
- "inverse.push(:#{prefixed_name}, self.attributes.slice(#{joined_fields}))")
7
+ klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
8
+ def #{callback_name}#{force_param}
9
+ data = #{joined_field_values("self")}
10
+ (#{iterable_relation}).each do |inverse|
11
+ #{pull_from_inverse}
12
+ inverse.push(:#{prefixed_name}, data)
13
+ end
14
+ end
15
+ protected :#{callback_name}
16
+ CALLBACK
9
17
  end
10
18
 
11
19
  def define_destroy_callback
12
- _callback(destroy_callback_name)
13
- end
14
-
15
- def _callback(_callback_name, push_content="")
16
20
  klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
17
- def #{_callback_name}
18
-
21
+ def #{destroy_callback_name}
19
22
  (#{iterable_relation}).each do |inverse|
23
+ #{pull_from_inverse}
24
+ end
25
+ end
26
+ protected :#{destroy_callback_name}
27
+ CALLBACK
28
+ end
20
29
 
21
- # this pull works in the DB, but not in memory
22
- # ($pull w/ expression not currently supported by Mongoid)
23
- inverse.pull(:#{prefixed_name}, { "_id" => self.id })
24
-
25
- # manually do the pull in memory for now
26
- if inverse.#{prefixed_name}
27
- inverse.#{prefixed_name}.reject! do |hash|
28
- hash["_id"] == self.id
29
- end
30
- end
30
+ def pull_from_inverse
31
+ <<-CALLBACK
32
+ # this pull works in the DB, but not in memory
33
+ # ($pull w/ expression not currently supported by Mongoid)
34
+ inverse.pull(:#{prefixed_name}, { "_id" => self.id })
31
35
 
32
- #{push_content}
36
+ # manually do the pull in memory for now
37
+ if inverse.#{prefixed_name}
38
+ inverse.#{prefixed_name}.reject! do |hash|
39
+ hash["_id"] == self.id
33
40
  end
34
41
  end
35
-
36
- protected :#{_callback_name}
37
42
  CALLBACK
38
43
  end
39
44
 
@@ -13,7 +13,7 @@ module Mongoid
13
13
 
14
14
  def to_one_field_sets
15
15
  fields.map { |field|
16
- "relation.set(:#{prefixed_field_name(field)}, self.read_attribute(:#{field}))"
16
+ "relation.set(:#{prefixed_field_name(field)}, self.send(:#{field}))"
17
17
  }.join("\n")
18
18
  end
19
19
 
@@ -25,13 +25,12 @@ module Mongoid
25
25
 
26
26
  def _callback(_callback_name, field_sets)
27
27
  klass.class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
28
- def #{_callback_name}
28
+ def #{_callback_name}#{force_param}
29
29
  (#{iterable_relation}).each do |relation|
30
30
  #{field_sets}
31
31
  end
32
32
  true
33
33
  end
34
-
35
34
  protected :#{_callback_name}
36
35
  CALLBACK
37
36
  end
data/lib/mongoid_alize.rb CHANGED
@@ -1,7 +1,10 @@
1
+ require 'mongoid/alize/errors/alize_error'
2
+ require 'mongoid/alize/errors/invalid_field'
3
+ require 'mongoid/alize/errors/already_defined_field'
4
+
1
5
  require 'mongoid/alize/callback'
2
6
 
3
7
  require 'mongoid/alize/from_callback.rb'
4
-
5
8
  require 'mongoid/alize/to_callback.rb'
6
9
  require 'mongoid/alize/to_one_callback.rb'
7
10
  require 'mongoid/alize/to_many_callback.rb'
@@ -16,6 +19,8 @@ require 'mongoid/alize/callbacks/to/many_from_many.rb'
16
19
 
17
20
  require 'mongoid/alize/macros'
18
21
 
22
+ I18n.load_path << File.join(File.dirname(__FILE__), "..", "config", "locales", "en.yml")
23
+
19
24
  module Mongoid
20
25
  module Alize
21
26
  extend ActiveSupport::Concern