ar_doc_store 1.0.5 → 2.0.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.
- 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
|