embedson 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: ef3de65a0b403021adc635d027d7ab9d8265c854
4
- data.tar.gz: ab1e6d98c11f7b4dc3f03a33f906842384937368
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzBjMmI2NTk4ZWRhMGJkZTZlZWI3MTBjMzI4MzA1ZmU5ODg1MjA5NA==
5
+ data.tar.gz: !binary |-
6
+ MTAyNjhmYmY3OGM4ZTYxYTcxZjVhYzgzY2ZhNzYwNTM0ZGFhYjAxYQ==
5
7
  SHA512:
6
- metadata.gz: b7f1ae0b831c508775371d3667b85b3a7ac9dfa738a9641df620f2789e1128dbf9ab1c5fb82d725ba5bcb40753d73b8eba039fdd4b8eef0ac79dc827ca5b6da6
7
- data.tar.gz: c0895ec2264f217f1228f4fb6fe0a014e193f481224d3d44552a2552b595d76f4d7270e5965b7aaaa8599a189f23ad9736a97adfb6577db7708a436e1041525a
8
+ metadata.gz: !binary |-
9
+ NjEyYTE2ZmJiZGE2ODA5Njc3Y2U4ZDJiYmM4ODU5MGJjNDNlYzZjYzA4Mzhk
10
+ ZDJkYjllZWJhMWFlNjNiOGY5MzI5OTFiODlhMTNkYWQ2MjgzYTYyOGFhMTE3
11
+ YTBjY2FlMDBkNWExYzU0MzcyYjgyZGEwOWQzMzIxYjI2ZDQ5NGQ=
12
+ data.tar.gz: !binary |-
13
+ NjZhZDliMWE4YWMyZmIxNDk4YTE2ZDQ5NTVmYzZhMmFiMDZkOWE3MGQ4MWU1
14
+ YjFiOTFkNmVlZDc1Mzc3YTljODA5OWZjZGI5NTVkNDAyNjAxNzQ1M2NiYWVl
15
+ OThjODgzZjQ2YjY5YzM0MzM4NzlmMjVjYmJjNzg1ZTk2ZTgxMmI=
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2014 sufleR
1
+ Copyright (c) 2014 Szymon Frącczak
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -184,3 +184,6 @@ This will **not** work!
184
184
  4. Push to the branch (`git push origin my-new-feature`)
185
185
  5. Create a new Pull Request
186
186
 
187
+ ## License
188
+
189
+ Copyright (c) 2014 Szymon Frącczak. See LICENSE.txt for further details.
data/TODO.md ADDED
@@ -0,0 +1,5 @@
1
+ ## ToDo list
2
+
3
+ - support for inheritance of embedded class
4
+ - support for dirty tracking
5
+ - never enough tests
@@ -1,14 +1,8 @@
1
1
  module Embedson
2
2
  class ClassTypeError < TypeError
3
- attr_reader :wrong_name, :correct_name
4
3
 
5
4
  def initialize(wrong_name, correct_name)
6
- @wrong_name, @correct_name = wrong_name, correct_name
7
- super(build_message)
8
- end
9
-
10
- def build_message
11
- "wrong argument type #{wrong_name} (expected #{correct_name})"
5
+ super("Wrong argument type #{wrong_name} (expected #{correct_name})")
12
6
  end
13
7
  end
14
8
 
@@ -18,4 +12,11 @@ module Embedson
18
12
  super("Cannot #{action} embedded #{klass_name} without a parent relation.")
19
13
  end
20
14
  end
15
+
16
+ class NoRelationDefinedError < StandardError
17
+
18
+ def initialize(klass_name, inverse_name)
19
+ super("Parent class #{klass_name} has no '#{inverse_name}' method defined or inverse_of option is not set properly.")
20
+ end
21
+ end
21
22
  end
@@ -10,7 +10,7 @@ module Embedson
10
10
 
11
11
  def define
12
12
  methods_embedded.each do |meth|
13
- klass.class_exec builder, &send(meth)
13
+ klass.class_exec builder, &self.method(meth)
14
14
  end
15
15
  end
16
16
 
@@ -20,81 +20,104 @@ module Embedson
20
20
  self.class.private_instance_methods(false).select{ |m| m.to_s.start_with?('embedded_') }
21
21
  end
22
22
 
23
- def embedded_initializer
24
- proc do |builder|
25
- alias_method :orig_initialize, :initialize
23
+ def embedded_alter_initialize(builder)
24
+ klass.send :alias_method, "#{builder.field_name}_initialize".to_sym, :initialize
25
+ klass.send :private, "#{builder.field_name}_initialize"
26
+ end
26
27
 
27
- define_method("initialize") do |*args|
28
- attrs = args[0]
29
- attrs ||= {}
30
- public_send("#{builder.field_name}=", attrs.fetch(builder.field_name, nil))
31
- orig_initialize(*args)
32
- end
28
+ def embedded_initializer(builder)
29
+ klass.send :define_method, "initialize" do |*args|
30
+ attrs = args[0] || {}
31
+ val = attrs.delete(builder.field_name)
32
+
33
+ send("#{builder.field_name}_initialize", *args)
34
+ public_send("#{builder.field_name}=", val) if val.present?
33
35
  end
34
36
  end
35
37
 
36
- def embedded_writer
37
- proc do |builder|
38
- define_method("#{builder.field_name}=") do |arg|
39
- verify_arg_klass(arg)
38
+ def embedded_writer(builder)
39
+ klass.send :define_method, "#{builder.field_name}=" do |arg|
40
+ send("#{builder.field_name}_verify_arg_klass", arg)
40
41
 
41
- instance_variable_set(builder.instance_var_name, arg)
42
- parent = public_send(builder.field_name)
42
+ instance_variable_set(builder.instance_var_name, arg)
43
43
 
44
- send_self_to_related(parent)
45
- end
44
+ send("#{builder.field_name}_send_to_related", self)
46
45
  end
47
46
  end
48
47
 
49
- def embedded_reader
50
- proc do |builder|
51
- define_method(builder.field_name) do
52
- instance_variable_get(builder.instance_var_name)
53
- end
48
+ def embedded_reader(builder)
49
+ klass.send :define_method, builder.field_name do
50
+ instance_variable_get(builder.instance_var_name)
54
51
  end
55
52
  end
56
53
 
57
- def embedded_destroy
58
- proc do |builder|
59
- define_method('destroy') do
60
- parent = public_send(builder.field_name)
61
- return false unless parent.present?
62
- parent.public_send(builder.inverse_set, nil)
63
- parent.save!
64
- end
54
+ def embedded_destroy(builder)
55
+ klass.send :define_method, 'destroy' do
56
+ call_in_transaction_for_all_embedding('save!', nil)
57
+ end
58
+ end
59
+
60
+ def embedded_save(builder)
61
+ klass.send :define_method, 'save' do
62
+ call_in_transaction_for_all_embedding('save', self)
65
63
  end
66
64
  end
67
65
 
68
- def embedded_save
69
- proc do |builder|
70
- define_method('save') do
71
- parent = public_send(builder.field_name)
72
- return false unless parent.present?
73
- parent.save
66
+ def embedded_save!(builder)
67
+ klass.send :define_method, 'save!' do
68
+ raise NoParentError.new('save!', self.class.name) unless any_embedding_present?
69
+ call_in_transaction_for_all_embedding('save!', self)
70
+ end
71
+ end
72
+
73
+ def embedded_changed(builder)
74
+ klass.send :define_method, 'embedson_model_changed!' do
75
+ raise NoParentError.new('register change', self.class.name) unless any_embedding_present?
76
+
77
+ self.class.embedson_relations.each do |relation|
78
+ send("#{relation}_send_to_related", self) if public_send(relation).present?
74
79
  end
80
+ true
75
81
  end
76
82
  end
77
83
 
78
- def embedded_save!
79
- proc do |builder|
80
- define_method('save!') do
81
- parent = public_send(builder.field_name)
82
- raise NoParentError.new('save', self.class.name) unless parent.present?
83
- parent.save!
84
+ def embedded_send_to_related(builder)
85
+ klass.send :define_method, "#{builder.field_name}_send_to_related" do |arg|
86
+ parent = public_send(builder.field_name)
87
+ return if parent.nil?
88
+ unless parent.respond_to?(builder.inverse_set)
89
+ raise NoRelationDefinedError.new(parent.class, builder.inverse_set)
84
90
  end
91
+ parent.public_send(builder.inverse_set, arg)
85
92
  end
93
+ klass.send :private, "#{builder.field_name}_send_to_related"
86
94
  end
87
95
 
88
- def embedded_changed
89
- proc do |builder|
90
- define_method('embedson_model_changed!') do
91
- parent = public_send(builder.field_name)
92
- raise NoParentError.new('register change', self.class.name) unless parent.present?
93
- parent.public_send(builder.inverse_set, self)
94
- true
96
+ def embedded_call_in_transaction_for_all_embedding(builder)
97
+ return if klass.methods.include? :call_in_transaction_for_all_embedding
98
+ klass.send :define_method, :call_in_transaction_for_all_embedding do |method, object|
99
+ results = []
100
+ ActiveRecord::Base.transaction do
101
+ self.class.embedson_relations.each do |field_name|
102
+ next if public_send(field_name).nil?
103
+ send("#{field_name}_send_to_related", object)
104
+ save_res = public_send(field_name).send(method)
105
+ results << save_res
106
+ raise ActiveRecord::Rollback unless save_res
107
+ end
95
108
  end
109
+ !results.size.zero? && results.all?
96
110
  end
97
111
  end
112
+
113
+ def embedded_any_present?(builder)
114
+ return if klass.methods.include? :any_ebedding_present?
115
+ klass.send :define_method, :any_embedding_present? do
116
+ self.class.embedson_relations.any?{ |r| public_send(r).present? }
117
+ end
118
+
119
+ klass.send :private, :any_embedding_present?
120
+ end
98
121
  end
99
122
  end
100
123
  end
@@ -10,58 +10,63 @@ module Embedson
10
10
 
11
11
  def define
12
12
  methods_embeds.each do |meth|
13
- klass.class_exec builder, &send(meth)
13
+ klass.class_exec builder, &self.method(meth)
14
14
  end
15
15
  end
16
16
 
17
17
  private
18
18
 
19
19
  def methods_embeds
20
- [:writer, :reader, :related_model, :build_related_model]
20
+ [:writer, :reader, :related_model, :build_related_model, :send_to_related]
21
21
  end
22
22
 
23
- def writer
24
- proc do |builder|
25
- define_method("#{builder.field_name}=") do |arg|
26
- verify_arg_klass(arg)
27
- send_self_to_related(arg)
23
+ def writer(builder)
24
+ klass.send :define_method, "#{builder.field_name}=" do |arg|
25
+ send("#{builder.field_name}_verify_arg_klass", arg)
26
+ send("#{builder.field_name}_send_to_related", arg)
28
27
 
29
- instance_variable_set(builder.instance_var_name, arg)
30
- write_attribute(builder.column_name, arg.nil? ? arg : arg.to_h)
28
+ instance_variable_set(builder.instance_var_name, arg)
29
+ val = arg.nil? ? arg : arg.to_h.stringify_keys
30
+ unless val == read_attribute(builder.column_name)
31
+ write_attribute(builder.column_name, val)
31
32
  end
32
33
  end
33
34
  end
34
35
 
35
- def reader
36
- proc do |builder|
37
- define_method(builder.field_name) do
38
- return if read_attribute(builder.column_name).nil?
36
+ def reader(builder)
37
+ klass.send :define_method, builder.field_name do
38
+ return if read_attribute(builder.column_name).nil?
39
39
 
40
- build_related_model if instance_variable_get(builder.instance_var_name).nil?
41
- instance_variable_get(builder.instance_var_name)
42
- end
40
+ send("#{builder.field_name}_build_related_model") if instance_variable_get(builder.instance_var_name).nil?
41
+ instance_variable_get(builder.instance_var_name)
43
42
  end
44
43
  end
45
44
 
46
- def related_model
47
- proc do |builder|
48
- private
45
+ def related_model(builder)
46
+ klass.send :define_method, "#{builder.field_name}_related_model" do
47
+ builder.related_klass_name.constantize.new(read_attribute(builder.column_name))
48
+ end
49
+ klass.send :private, "#{builder.field_name}_related_model"
50
+ end
49
51
 
50
- define_method('related_model') do
51
- builder.related_klass_name.constantize.new(read_attribute(builder.column_name))
52
+ def build_related_model(builder)
53
+ klass.send :define_method, "#{builder.field_name}_build_related_model" do
54
+ related_model = send("#{builder.field_name}_related_model")
55
+ instance_variable_set(builder.instance_var_name, related_model)
56
+ if related_model.respond_to?(builder.inverse_set)
57
+ related_model.public_send(builder.inverse_set, self)
52
58
  end
53
59
  end
60
+ klass.send :private, "#{builder.field_name}_build_related_model"
54
61
  end
55
62
 
56
- def build_related_model
57
- proc do |builder|
58
- private
59
-
60
- define_method('build_related_model') do
61
- instance_variable_set(builder.instance_var_name, related_model)
62
- related_model.public_send(builder.inverse_set, self) if related_model.respond_to?(builder.inverse_set)
63
+ def send_to_related(builder)
64
+ klass.send :define_method, "#{builder.field_name}_send_to_related" do |arg|
65
+ if arg.respond_to?(builder.inverse_set) && arg.public_send(builder.inverse_get) != self
66
+ arg.public_send(builder.inverse_set, self)
63
67
  end
64
68
  end
69
+ klass.send :private, "#{builder.field_name}_send_to_related"
65
70
  end
66
71
  end
67
72
  end
@@ -26,7 +26,7 @@ module Embedson
26
26
  end
27
27
 
28
28
  def related_klass_name
29
- @related_klass_name ||= (options.fetch(:class_name, nil) || field_name).to_s.classify
29
+ @related_klass_name ||= (options.fetch(:class_name, nil) || field_name).to_s.camelize
30
30
  end
31
31
 
32
32
  def instance_var_name
@@ -45,36 +45,21 @@ module Embedson
45
45
 
46
46
  def generate_common
47
47
  methods_for_both.each do |meth|
48
- klass.class_exec self, &send(meth)
48
+ klass.class_exec self, &self.method(meth)
49
49
  end
50
50
  end
51
51
 
52
52
  def methods_for_both
53
- [:send_self_to_related, :verify_arg_klass]
53
+ [:verify_arg_klass]
54
54
  end
55
55
 
56
- def verify_arg_klass
57
- proc do |builder|
58
- private
59
-
60
- define_method('verify_arg_klass') do |arg|
61
- unless arg.nil? || arg.is_a?(builder.related_klass_name.constantize)
62
- raise ClassTypeError.new(arg.class.name, builder.related_klass_name)
63
- end
64
- end
65
- end
66
- end
67
-
68
- def send_self_to_related
69
- proc do |builder|
70
- private
71
-
72
- define_method('send_self_to_related') do |arg|
73
- if arg.respond_to?(builder.inverse_set) && arg.public_send(builder.inverse_get) != self
74
- arg.public_send(builder.inverse_set, self)
75
- end
56
+ def verify_arg_klass(builder)
57
+ klass.send(:define_method, "#{field_name}_verify_arg_klass") do |arg|
58
+ unless arg.nil? || arg.is_a?(builder.related_klass_name.constantize)
59
+ raise ClassTypeError.new(arg.class.name, builder.related_klass_name)
76
60
  end
77
61
  end
62
+ klass.send(:private, "#{field_name}_verify_arg_klass")
78
63
  end
79
64
  end
80
65
  end
@@ -1,13 +1,57 @@
1
1
  module Embedson
2
+ # Public: Defines embeds_one and embedded_in methods.
3
+ #
4
+ # Examples
5
+ #
6
+ # class Emb
7
+ # extend Embedson::Model
8
+ # end
2
9
  module Model
3
10
 
11
+ # Public: Creates methods to manage embedded class.
12
+ #
13
+ # name - Name of of relation.
14
+ # options - The Hash options used to define custom column name, class name
15
+ # and field name in embedded class (default: {}):
16
+ # :class_name - Name of class which will be ebedded.
17
+ # :column_name - Name of column where Hash representation will be stored.
18
+ # :inverse_of - Name of field where related class will store current object.
19
+ #
20
+ # Examples
21
+ #
22
+ # embeds_one :virt, class_name: Virt, column_name: :data, inverse_of: :parent
23
+ #
24
+ # embeds_one :virt
25
+ #
26
+ # Returns nothing
4
27
  def embeds_one(name, options = {})
5
28
  MethodBuilder.new(self, name, options).embeds
6
29
  end
7
30
 
31
+ # Public: Creates methods to manage parent class.
32
+ #
33
+ # name - Name of relation where parent object will be stored.
34
+ # options - The hash options used to define custom class name and field name
35
+ # in parent class (default: {}):
36
+ # :class_name - Name of class where current object will be embedded.
37
+ # :inverse_of - Name of field where parent class will keep current object.
38
+ #
39
+ # Examples
40
+ #
41
+ # embedded_in :parent, class_name: Test, inverse_of: :virt
42
+ #
43
+ # embedded_in :parent
44
+ #
45
+ # Returns nothing
8
46
  def embedded_in(name, options = {})
47
+ @embedson_relations ||= []
48
+ @embedson_relations << name
9
49
  MethodBuilder.new(self, name, options).embedded
10
50
  end
51
+
52
+ def self.extended(mod)
53
+ attr_reader :embedson_relations
54
+ end
11
55
  end
12
56
  end
13
57
 
@@ -1,3 +1,3 @@
1
1
  module Embedson
2
- VERSION = "1.0.2"
2
+ VERSION = "1.0.3"
3
3
  end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Embedson::ClassTypeError do
4
+ let(:error) { Embedson::ClassTypeError.new('Wrong', 'Correct') }
5
+
6
+ it 'raises with correct message' do
7
+ expect(error.message).to eq "Wrong argument type Wrong (expected Correct)"
8
+ end
9
+ end
10
+
11
+ describe Embedson::NoParentError do
12
+ let(:error) { Embedson::NoParentError.new('assign', 'Correct') }
13
+
14
+ it 'raises with correct message' do
15
+ expect(error.message).to eq "Cannot assign embedded Correct without a parent relation."
16
+ end
17
+ end
18
+
19
+ describe Embedson::NoParentError do
20
+ let(:error) { Embedson::NoRelationDefinedError.new('Klass', 'embedded=') }
21
+
22
+ it 'raises with correct message' do
23
+ expect(error.message).to eq "Parent class Klass has no 'embedded=' method defined or inverse_of option is not set properly."
24
+ end
25
+ end