ar_doc_store 1.0.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ar_doc_store.gemspec +3 -3
- data/lib/ar_doc_store.rb +30 -28
- data/lib/ar_doc_store/attributes/array.rb +25 -0
- data/lib/ar_doc_store/attributes/base.rb +72 -0
- data/lib/ar_doc_store/attributes/boolean.rb +17 -0
- data/lib/ar_doc_store/attributes/callback_support.rb +21 -0
- data/lib/ar_doc_store/attributes/date.rb +21 -0
- data/lib/ar_doc_store/attributes/datetime.rb +21 -0
- data/lib/ar_doc_store/attributes/decimal.rb +17 -0
- data/lib/ar_doc_store/attributes/embeds_base.rb +35 -0
- data/lib/ar_doc_store/attributes/embeds_many.rb +143 -0
- data/lib/ar_doc_store/{attribute_types/embeds_one_attribute.rb → attributes/embeds_one.rb} +25 -30
- data/lib/ar_doc_store/{attribute_types/enumeration_attribute.rb → attributes/enumeration.rb} +2 -6
- data/lib/ar_doc_store/{attribute_types/float_attribute.rb → attributes/float.rb} +5 -8
- data/lib/ar_doc_store/{attribute_types/integer_attribute.rb → attributes/integer.rb} +5 -8
- data/lib/ar_doc_store/attributes/string.rb +13 -0
- data/lib/ar_doc_store/embeddable_model.rb +51 -75
- data/lib/ar_doc_store/embedded_collection.rb +14 -0
- data/lib/ar_doc_store/embedding.rb +0 -2
- data/lib/ar_doc_store/model.rb +18 -8
- data/lib/ar_doc_store/storage.rb +4 -127
- data/lib/ar_doc_store/types/embeds_many.rb +52 -0
- data/lib/ar_doc_store/types/embeds_one.rb +42 -0
- data/lib/ar_doc_store/version.rb +1 -1
- data/test/{attribute_types → attributes}/array_attribute_test.rb +5 -1
- data/test/{attribute_types → attributes}/boolean_attribute_test.rb +7 -1
- data/test/attributes/date_attribute_test.rb +52 -0
- data/test/attributes/datetime_attribute_test.rb +54 -0
- data/test/attributes/decimal_attribute_test.rb +38 -0
- data/test/attributes/embeds_many_attribute_test.rb +78 -0
- data/test/attributes/embeds_one_attribute_test.rb +80 -0
- data/test/{attribute_types → attributes}/enumeration_attribute_test.rb +5 -0
- data/test/{attribute_types → attributes}/float_attribute_test.rb +6 -1
- data/test/attributes/integer_attribute_test.rb +63 -0
- data/test/{attribute_types → attributes}/string_attribute_test.rb +6 -0
- data/test/test_helper.rb +22 -94
- metadata +57 -35
- data/lib/ar_doc_store/attribute_types/array_attribute.rb +0 -30
- data/lib/ar_doc_store/attribute_types/base_attribute.rb +0 -61
- data/lib/ar_doc_store/attribute_types/boolean_attribute.rb +0 -25
- data/lib/ar_doc_store/attribute_types/datetime_attribute.rb +0 -17
- data/lib/ar_doc_store/attribute_types/embeds_many_attribute.rb +0 -165
- data/lib/ar_doc_store/attribute_types/string_attribute.rb +0 -17
- data/lib/ar_doc_store/attribute_types/uuid_attribute.rb +0 -30
- data/test/attribute_types/datetime_attribute_test.rb +0 -35
- data/test/attribute_types/integer_attribute_test.rb +0 -33
- data/test/originals/dirty_attributes_test.rb +0 -35
- data/test/originals/embedded_model_attribute_test.rb +0 -23
- data/test/originals/embedding_test.rb +0 -82
@@ -1,25 +0,0 @@
|
|
1
|
-
module ArDocStore
|
2
|
-
module AttributeTypes
|
3
|
-
|
4
|
-
class BooleanAttribute < BaseAttribute
|
5
|
-
def build
|
6
|
-
key = attribute.to_sym
|
7
|
-
model.class_eval do
|
8
|
-
store_accessor json_column, key
|
9
|
-
define_method "#{key}?".to_sym, -> { public_send(key) == true }
|
10
|
-
define_method "#{key}=".to_sym, -> (value) {
|
11
|
-
res = ArDocStore.convert_boolean(value)
|
12
|
-
write_store_attribute(json_column, key, res)
|
13
|
-
}
|
14
|
-
add_ransacker(key, 'bool')
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def type
|
19
|
-
:boolean
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
@@ -1,165 +0,0 @@
|
|
1
|
-
module ArDocStore
|
2
|
-
|
3
|
-
class EmbeddedCollection < Array
|
4
|
-
attr_accessor :parent
|
5
|
-
end
|
6
|
-
|
7
|
-
module AttributeTypes
|
8
|
-
|
9
|
-
class EmbedsManyAttribute < BaseAttribute
|
10
|
-
|
11
|
-
def build
|
12
|
-
assn_name = attribute.to_sym
|
13
|
-
class_name = options[:class_name] || attribute.to_s.classify
|
14
|
-
model.store_accessor model.json_column, assn_name
|
15
|
-
create_reader_for assn_name, class_name
|
16
|
-
create_writer_for assn_name, class_name
|
17
|
-
create_build_method_for assn_name, class_name
|
18
|
-
create_ensure_method_for assn_name
|
19
|
-
create_embeds_many_attributes_method(class_name, assn_name)
|
20
|
-
create_embeds_many_validation(assn_name)
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def add_method(method, block)
|
26
|
-
model.class_eval do
|
27
|
-
define_method method, block
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def create_reader_for(assn_name, class_name)
|
32
|
-
add_method assn_name.to_sym, -> {
|
33
|
-
ivar = "@#{assn_name}"
|
34
|
-
instance_variable_get(ivar) || begin
|
35
|
-
my_class_name = class_name.constantize
|
36
|
-
items = read_store_attribute(json_column, assn_name)
|
37
|
-
if items.is_a?(Array) || items.is_a?(ArDocStore::EmbeddedCollection)
|
38
|
-
items = ArDocStore::EmbeddedCollection.new items.map { |item| item.is_a?(my_class_name) ? item : my_class_name.build(item) }
|
39
|
-
else
|
40
|
-
items ||= ArDocStore::EmbeddedCollection.new
|
41
|
-
end
|
42
|
-
instance_variable_set ivar, (items)
|
43
|
-
items.parent = self
|
44
|
-
items.map {|item| item.parent = self }
|
45
|
-
items
|
46
|
-
end
|
47
|
-
}
|
48
|
-
end
|
49
|
-
def create_writer_for(assn_name, class_name)
|
50
|
-
add_method "#{assn_name}=".to_sym, -> (values) {
|
51
|
-
if values && values.respond_to?(:map)
|
52
|
-
items = ArDocStore::EmbeddedCollection.new values.map { |item|
|
53
|
-
my_class_name = class_name.constantize
|
54
|
-
item = item.is_a?(my_class_name) ? item : my_class_name.new(item)
|
55
|
-
item.id
|
56
|
-
item.parent = self
|
57
|
-
item
|
58
|
-
}
|
59
|
-
else
|
60
|
-
items = []
|
61
|
-
end
|
62
|
-
items.parent = self
|
63
|
-
instance_variable_set "@#{assn_name}", write_store_attribute(json_column, assn_name, items)
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
def create_build_method_for(assn_name, class_name)
|
68
|
-
add_method "build_#{assn_name.to_s.singularize}", -> (attributes=nil) {
|
69
|
-
assns = self.public_send assn_name
|
70
|
-
item = class_name.constantize.build attributes
|
71
|
-
item.parent = self
|
72
|
-
assns << item
|
73
|
-
public_send "#{assn_name}=", assns
|
74
|
-
item
|
75
|
-
}
|
76
|
-
end
|
77
|
-
|
78
|
-
def create_ensure_method_for(assn_name)
|
79
|
-
method = -> { public_send "build_#{assn_name.to_s.singularize}" if self.public_send(assn_name).blank? }
|
80
|
-
add_method "ensure_#{assn_name.to_s.singularize}", method
|
81
|
-
add_method "ensure_#{assn_name}", method
|
82
|
-
end
|
83
|
-
|
84
|
-
def create_embeds_many_attributes_method(class_name, assn_name)
|
85
|
-
add_method "#{assn_name}_attributes=", -> (values) {
|
86
|
-
return if values.blank?
|
87
|
-
# if it's a single item then wrap it in an array but how to tell?
|
88
|
-
|
89
|
-
if values.respond_to?(:each)
|
90
|
-
if values.respond_to?(:values)
|
91
|
-
values = values.values
|
92
|
-
end
|
93
|
-
else
|
94
|
-
values = [values]
|
95
|
-
end
|
96
|
-
models = public_send assn_name
|
97
|
-
public_send "#{assn_name}=", AssignEmbedsManyAttributes.new(self, class_name, assn_name, models, values).models
|
98
|
-
}
|
99
|
-
end
|
100
|
-
|
101
|
-
def create_embeds_many_validation(assn_name)
|
102
|
-
model.class_eval do
|
103
|
-
validate_method = "validate_embedded_record_for_#{assn_name}".to_sym
|
104
|
-
define_method validate_method, -> { validate_embeds_many assn_name }
|
105
|
-
validate validate_method
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
class AssignEmbedsManyAttributes
|
113
|
-
attr_reader :models, :assn_name, :parent, :class_name
|
114
|
-
def initialize(parent, class_name, assn_name, models, values)
|
115
|
-
@parent, @class_name, @assn_name, @models, @values = parent, class_name, assn_name, models, values
|
116
|
-
values.each { |value|
|
117
|
-
value = value.symbolize_keys
|
118
|
-
if value.key?(:id)
|
119
|
-
process_existing_model(value)
|
120
|
-
else
|
121
|
-
next if values.all?(&:nil?)
|
122
|
-
add(value)
|
123
|
-
end
|
124
|
-
}
|
125
|
-
end
|
126
|
-
|
127
|
-
private
|
128
|
-
|
129
|
-
attr_writer :models, :values
|
130
|
-
attr_reader :values, :assn_name
|
131
|
-
|
132
|
-
def process_existing_model(value)
|
133
|
-
return false unless value.key?(:id)
|
134
|
-
model = find_model_by_value(value)
|
135
|
-
model && destroy_or_update(model, value) or add(value)
|
136
|
-
end
|
137
|
-
|
138
|
-
def destroy_or_update(model, value)
|
139
|
-
destroy(model, value) or update_attributes(model, value)
|
140
|
-
end
|
141
|
-
|
142
|
-
def add(value)
|
143
|
-
models << class_name.constantize.new(value)
|
144
|
-
end
|
145
|
-
|
146
|
-
def destroy(model, value)
|
147
|
-
wants_to_die?(value) && models.delete(model)
|
148
|
-
end
|
149
|
-
|
150
|
-
def update_attributes(model, value)
|
151
|
-
model.attributes = value
|
152
|
-
end
|
153
|
-
|
154
|
-
def wants_to_die?(value)
|
155
|
-
value.key?(:_destroy) && ArDocStore.convert_boolean(value[:_destroy])
|
156
|
-
end
|
157
|
-
|
158
|
-
def find_model_by_value(value)
|
159
|
-
models.detect { |item| item.id == value[:id] }
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
|
164
|
-
end
|
165
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
|
3
|
-
module ArDocStore
|
4
|
-
module AttributeTypes
|
5
|
-
|
6
|
-
class UuidAttribute < BaseAttribute
|
7
|
-
def build
|
8
|
-
key = attribute.to_sym
|
9
|
-
model.class_eval do
|
10
|
-
store_accessor json_column, key
|
11
|
-
define_method key, -> {
|
12
|
-
value = read_store_attribute(json_column, key)
|
13
|
-
unless value
|
14
|
-
value = SecureRandom.uuid
|
15
|
-
write_store_attribute json_column, key, value
|
16
|
-
end
|
17
|
-
value
|
18
|
-
}
|
19
|
-
define_method "#{key}=".to_sym, -> (value) {
|
20
|
-
write_store_attribute(json_column, key, value)
|
21
|
-
}
|
22
|
-
add_ransacker(key, 'text')
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require_relative './../test_helper'
|
2
|
-
|
3
|
-
class DatetimeAttributeTest < MiniTest::Test
|
4
|
-
|
5
|
-
def test_attribute_on_model_init
|
6
|
-
approved_at = Time.new(1984, 3, 6)
|
7
|
-
po = PurchaseOrder.new approved_at: approved_at
|
8
|
-
assert approved_at == po.approved_at
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_attribute_on_existing_model
|
12
|
-
approved_at = Time.new(1984, 3, 6)
|
13
|
-
po = PurchaseOrder.new
|
14
|
-
po.approved_at = approved_at
|
15
|
-
assert approved_at == po.approved_at
|
16
|
-
assert po.approved_at_changed?
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_question_mark_method
|
20
|
-
approved_at = Time.new(1984, 3, 6)
|
21
|
-
po = PurchaseOrder.new approved_at: approved_at
|
22
|
-
assert_equal true, po.approved_at?
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_conversion
|
26
|
-
approved_at = Time.new(1984, 3, 6)
|
27
|
-
po = PurchaseOrder.new approved_at: approved_at.to_s
|
28
|
-
assert_kind_of Time, po.approved_at
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_no_op
|
32
|
-
po = PurchaseOrder.new
|
33
|
-
assert_nil po.approved_at
|
34
|
-
end
|
35
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
require_relative './../test_helper'
|
2
|
-
|
3
|
-
class IntegerAttributeTest < MiniTest::Test
|
4
|
-
|
5
|
-
def test_string_attribute_on_model_init
|
6
|
-
b = Building.new stories: 5
|
7
|
-
assert_equal 5, b.stories
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_string_attribute_on_existing_model
|
11
|
-
b = Building.new
|
12
|
-
b.stories = 5
|
13
|
-
assert_equal 5, b.stories
|
14
|
-
assert b.stories_changed?
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_question_mark_method
|
18
|
-
b = Building.new stories: 5
|
19
|
-
assert_equal true, b.stories?
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_type_conversion_on_init
|
23
|
-
b = Building.new stories: '5'
|
24
|
-
assert_equal 5, b.stories
|
25
|
-
end
|
26
|
-
|
27
|
-
def test_type_conversion_on_existing
|
28
|
-
b = Building.new
|
29
|
-
b.stories = '5'
|
30
|
-
assert_equal 5, b.stories
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require_relative './../test_helper'
|
2
|
-
|
3
|
-
class DirtyAttributeTest < MiniTest::Test
|
4
|
-
|
5
|
-
def test_on_model
|
6
|
-
b = Building.new name: 'Foo!'
|
7
|
-
# This used to work but started failing. AR behavior is to make it true.
|
8
|
-
# send :clear_changes_information not working yields undefined method.
|
9
|
-
# assert !b.name_changed?
|
10
|
-
b.name = 'Bar.'
|
11
|
-
assert_equal 'Bar.', b.name
|
12
|
-
assert b.name_changed?
|
13
|
-
# Somehow this worked at one point, but should only work when the record is loaded via instantiate:
|
14
|
-
# assert_equal 'Foo!', b.name_was
|
15
|
-
end
|
16
|
-
|
17
|
-
#This test fails here but passes elsewhere.
|
18
|
-
def test_on_embedded_model
|
19
|
-
b = Building.new
|
20
|
-
r = b.build_restroom restroom_type: 'dirty'
|
21
|
-
assert !r.restroom_type_changed?
|
22
|
-
r.restroom_type = 'nasty'
|
23
|
-
assert r.restroom_type_changed?
|
24
|
-
assert_equal 'dirty', r.restroom_type_was
|
25
|
-
assert_equal 'nasty', r.restroom_type
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_id_does_not_change_on_init
|
29
|
-
b = Building.new
|
30
|
-
r = b.build_restroom
|
31
|
-
assert !r.id_changed?
|
32
|
-
assert !r.changes.keys.include?('id')
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require_relative './../test_helper'
|
2
|
-
|
3
|
-
class EmbeddedModelAttributeTest < MiniTest::Test
|
4
|
-
|
5
|
-
def test_can_set_attribute_on_embedded_model_init
|
6
|
-
b = Route.new route_surface: 'test'
|
7
|
-
assert_equal 'test', b.route_surface
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_can_set_attribute_on_existing_embedded_model
|
11
|
-
b = Route.new
|
12
|
-
b.route_surface = 'test'
|
13
|
-
assert_equal 'test', b.route_surface
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_can_set_enumeration_created_with_enumerates
|
17
|
-
door = Door.new
|
18
|
-
door.door_type = %w{sliding push}
|
19
|
-
assert_equal %w{sliding push}, door.door_type
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require_relative './../test_helper'
|
2
|
-
|
3
|
-
class EmbeddingTest < MiniTest::Test
|
4
|
-
|
5
|
-
def test_can_build_embedded_model
|
6
|
-
restroom = Restroom.new
|
7
|
-
door = restroom.build_door
|
8
|
-
assert door.is_a?(Door)
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_ensure_door_returns_existing_door
|
12
|
-
restroom = Restroom.new
|
13
|
-
restroom.build_door
|
14
|
-
restroom.door.open_handle = %w{knob}
|
15
|
-
restroom.ensure_door
|
16
|
-
assert_equal %w{knob}, restroom.door.open_handle
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_attributes_equals_sets_attributes
|
20
|
-
restroom = Restroom.new door_attributes: { clear_distance: 5, opening_force: 13, clear_space: 43 }
|
21
|
-
assert_equal 5, restroom.door.clear_distance
|
22
|
-
restroom.door_attributes = { _destroy: '1' }
|
23
|
-
assert_nil restroom.door.clear_distance
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_attributes_equals_sets_partial_attributes
|
27
|
-
restroom = Restroom.new door_attributes: { clear_distance: 5, opening_force: 13, clear_space: 43 }
|
28
|
-
restroom.door_attributes = { clear_distance: 7 }
|
29
|
-
assert_equal 7, restroom.door.clear_distance
|
30
|
-
assert_equal 13, restroom.door.opening_force
|
31
|
-
end
|
32
|
-
|
33
|
-
def test_embeds_many_attributes_equals_sets_partial_attributes
|
34
|
-
building = Building.new
|
35
|
-
building.restrooms << Restroom.new(door_attributes: { clear_distance: 5, opening_force: 13, clear_space: 43 })
|
36
|
-
building.restrooms << Restroom.new(door_attributes: { clear_distance: 6, opening_force: 14, clear_space: 44 })
|
37
|
-
building.restrooms << Restroom.new(door_attributes: { clear_distance: 7, opening_force: 15, clear_space: 45 })
|
38
|
-
restrooms_attributes = {
|
39
|
-
a1: { id: building.restrooms[0].id, door_attributes: { clear_distance: 10 } },
|
40
|
-
a2: { id: building.restrooms[1].id, _destroy: true },
|
41
|
-
a3: { id: building.restrooms[2].id, door_attributes: { clear_distance: 1 } }
|
42
|
-
}
|
43
|
-
building.restrooms_attributes = restrooms_attributes
|
44
|
-
assert_equal 2, building.restrooms.size
|
45
|
-
assert_equal 10, building.restrooms.first.door.clear_distance
|
46
|
-
assert_equal 15, building.restrooms.last.door.opening_force
|
47
|
-
assert_equal 1, building.restrooms.last.door.clear_distance
|
48
|
-
assert_nil building.restrooms.detect {|restroom| restroom.id == restrooms_attributes[:a2][:id] }
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_attributes_method_embeds_many_does_not_clobber_existing_embeds_that_are_not_in_array
|
52
|
-
building = Building.new
|
53
|
-
building.restrooms << Restroom.new(door_attributes: { clear_distance: 5, opening_force: 13, clear_space: 43 })
|
54
|
-
building.restrooms << Restroom.new(door_attributes: { clear_distance: 6, opening_force: 14, clear_space: 44 })
|
55
|
-
building.restrooms << Restroom.new(door_attributes: { clear_distance: 7, opening_force: 15, clear_space: 45 })
|
56
|
-
restrooms_attributes = {
|
57
|
-
a1: { id: building.restrooms[0].id, door_attributes: { clear_distance: 10 } },
|
58
|
-
}
|
59
|
-
building.restrooms_attributes = restrooms_attributes
|
60
|
-
assert_equal 3, building.restrooms.size
|
61
|
-
assert_equal 10, building.restrooms.first.door.clear_distance
|
62
|
-
assert_equal 15, building.restrooms.last.door.opening_force
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_attribute_validity_of_embedded_model_from_model
|
66
|
-
b = Building.new
|
67
|
-
r = Restroom.new
|
68
|
-
b.restrooms << r
|
69
|
-
assert !b.valid?
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_model_with_no_attributes
|
73
|
-
item = ThingWithEmptyModel.new
|
74
|
-
item.build_empty_model
|
75
|
-
assert item.empty_model.is_a?(EmptyModel)
|
76
|
-
end
|
77
|
-
|
78
|
-
def test_model_subclassing
|
79
|
-
assert_equal EmbeddableB.virtual_attributes.size, 3
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|