embedson 1.0.2 → 1.0.3
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.
- checksums.yaml +13 -5
- data/LICENSE.txt +1 -1
- data/README.md +3 -0
- data/TODO.md +5 -0
- data/lib/embedson/exceptions.rb +8 -7
- data/lib/embedson/model/embedded_builder.rb +73 -50
- data/lib/embedson/model/embeds_builder.rb +33 -28
- data/lib/embedson/model/method_builder.rb +8 -23
- data/lib/embedson/model.rb +44 -0
- data/lib/embedson/version.rb +1 -1
- data/spec/embedson/exceptions_spec.rb +25 -0
- data/spec/embedson/model/embedded_builder_spec.rb +485 -0
- data/spec/embedson/model_spec.rb +70 -64
- metadata +28 -23
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YzBjMmI2NTk4ZWRhMGJkZTZlZWI3MTBjMzI4MzA1ZmU5ODg1MjA5NA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
MTAyNjhmYmY3OGM4ZTYxYTcxZjVhYzgzY2ZhNzYwNTM0ZGFhYjAxYQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
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
data/README.md
CHANGED
data/TODO.md
ADDED
data/lib/embedson/exceptions.rb
CHANGED
@@ -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
|
-
|
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, &
|
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
|
24
|
-
|
25
|
-
|
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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
42
|
-
parent = public_send(builder.field_name)
|
42
|
+
instance_variable_set(builder.instance_var_name, arg)
|
43
43
|
|
44
|
-
|
45
|
-
end
|
44
|
+
send("#{builder.field_name}_send_to_related", self)
|
46
45
|
end
|
47
46
|
end
|
48
47
|
|
49
|
-
def embedded_reader
|
50
|
-
|
51
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
parent.
|
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
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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, &
|
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
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
30
|
-
|
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
|
-
|
37
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
48
|
-
|
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
|
-
|
51
|
-
|
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
|
57
|
-
|
58
|
-
|
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.
|
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, &
|
48
|
+
klass.class_exec self, &self.method(meth)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
52
|
def methods_for_both
|
53
|
-
[:
|
53
|
+
[:verify_arg_klass]
|
54
54
|
end
|
55
55
|
|
56
|
-
def verify_arg_klass
|
57
|
-
|
58
|
-
|
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
|
data/lib/embedson/model.rb
CHANGED
@@ -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
|
|
data/lib/embedson/version.rb
CHANGED
@@ -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
|