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
@@ -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