ar_doc_store 0.1.3 → 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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +2 -0
- data/Rakefile +15 -1
- data/ar_doc_store.gemspec +3 -2
- data/lib/ar_doc_store.rb +14 -11
- data/lib/ar_doc_store/attribute_types/{array.rb → array_attribute.rb} +1 -1
- data/lib/ar_doc_store/attribute_types/base_attribute.rb +52 -0
- data/lib/ar_doc_store/attribute_types/{boolean.rb → boolean_attribute.rb} +1 -1
- data/lib/ar_doc_store/attribute_types/{embeds_many.rb → embeds_many_attribute.rb} +4 -5
- data/lib/ar_doc_store/attribute_types/embeds_one_attribute.rb +74 -0
- data/lib/ar_doc_store/attribute_types/{enumeration.rb → enumeration_attribute.rb} +1 -1
- data/lib/ar_doc_store/attribute_types/{float.rb → float_attribute.rb} +1 -1
- data/lib/ar_doc_store/attribute_types/{integer.rb → integer_attribute.rb} +1 -1
- data/lib/ar_doc_store/attribute_types/{string.rb → string_attribute.rb} +1 -1
- data/lib/ar_doc_store/attribute_types/{uuid.rb → uuid_attribute.rb} +1 -1
- data/lib/ar_doc_store/embeddable_model.rb +48 -20
- data/lib/ar_doc_store/storage.rb +3 -61
- data/lib/ar_doc_store/version.rb +1 -1
- data/test/attribute_types/array_attribute_test.rb +25 -0
- data/test/attribute_types/boolean_attribute_test.rb +36 -0
- data/test/{model_attribute_access_test.rb → attribute_types/enumeration_attribute_test.rb} +8 -46
- data/test/attribute_types/float_attribute_test.rb +33 -0
- data/test/attribute_types/integer_attribute_test.rb +33 -0
- data/test/attribute_types/string_attribute_test.rb +27 -0
- data/test/originals/dirty_attributes_test.rb +35 -0
- data/test/{embedded_model_attribute_test.rb → originals/embedded_model_attribute_test.rb} +1 -1
- data/test/{embedding_test.rb → originals/embedding_test.rb} +1 -1
- data/test/test_helper.rb +4 -2
- metadata +37 -28
- data/lib/ar_doc_store/attribute_types/base.rb +0 -23
- data/lib/ar_doc_store/attribute_types/embeds_one.rb +0 -54
- data/lib/ar_doc_store/attribute_types/json.rb +0 -13
- data/test/dirty_attributes_test.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3700fe9780b88ba20e28eb918e3b49c307750cc
|
4
|
+
data.tar.gz: 0ccdb51dd3e3b5b2c27176282b6f4d0fab67953d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1794e597e28722089e23f160a3caf8f3376427ce57dfd9a5e942ccb24b3820cf37d5c886cc87cfc867451a70d29ed2ee8dbdab43f7380025e3f0284d12c271a
|
7
|
+
data.tar.gz: 8f70729d52793af0a9dc970957ab986970d617792518f80b872c22b5026ead45afc7d641602e63fc6917d77d245f9f93e78c04446a4ac5f9bd72698813aa0fb7
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -9,6 +9,7 @@ The use case is primarily when you have a rapidly evolving schema with scores of
|
|
9
9
|
Learn more about the JSON column in Postgres and using it as a document store:
|
10
10
|
* The Rails Guide on Postgres: http://edgeguides.rubyonrails.org/active_record_postgresql.html
|
11
11
|
* Document Store Gymnastics: http://rob.conery.io/2015/03/01/document-storage-gymnastics-in-postgres/
|
12
|
+
* Query JSON with Rails 4.2 and Postgres 9.4: http://robertbeene.com/rails-4-2-and-postgresql-9-4/
|
12
13
|
* PG as NoSQL: http://thebuild.com%5Cpresentations%5Cpg-as-nosql-pgday-fosdem-2013.pdf
|
13
14
|
* Why JSON in PostgreSQL is Awesome: https://functionwhatwhat.com/json-in-postgresql/
|
14
15
|
* Indexing JSONB: http://michael.otacoo.com/postgresql-2/postgres-9-4-feature-highlight-indexing-jsonb/
|
@@ -156,6 +157,7 @@ end
|
|
156
157
|
= form.input :height, as: :float
|
157
158
|
= form.object.ensure_door
|
158
159
|
= form.fields_for :door do |door_form|
|
160
|
+
= door_form.input :id # <-- because fields_for won't output the hidden id field for us - not a bad thing to put it where you want it instead of where they put it
|
159
161
|
= door_form.input :door_type, as: :check_boxes, collection: Door.door_type_choices
|
160
162
|
```
|
161
163
|
|
data/Rakefile
CHANGED
@@ -3,5 +3,19 @@ require "bundler/gem_tasks"
|
|
3
3
|
require 'rake/testtask'
|
4
4
|
|
5
5
|
Rake::TestTask.new do |t|
|
6
|
-
t.pattern = "test
|
6
|
+
t.pattern = "test/**/*_test.rb"
|
7
|
+
end
|
8
|
+
|
9
|
+
namespace :test do
|
10
|
+
task :prepare_ar_doc_store do
|
11
|
+
require 'active_record'
|
12
|
+
ActiveRecord::Base.establish_connection(adapter: 'postgresql', database: 'ar_doc_store_test', username: 'postgres', password: 'postgres')
|
13
|
+
|
14
|
+
ActiveRecord::Schema.define do
|
15
|
+
self.verbose = false
|
16
|
+
create_table :buildings, force: true do |t|
|
17
|
+
t.jsonb :data
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
7
21
|
end
|
data/ar_doc_store.gemspec
CHANGED
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "activerecord", "
|
22
|
-
spec.add_dependency "
|
21
|
+
spec.add_dependency "activerecord", "~> 4.0"
|
22
|
+
spec.add_dependency "pg", "~> 0.17"
|
23
|
+
# spec.add_dependency "hashie", ">=3.4.0"
|
23
24
|
spec.add_development_dependency "bundler", "~> 1.7"
|
24
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
26
|
end
|
data/lib/ar_doc_store.rb
CHANGED
@@ -3,18 +3,22 @@ require "ar_doc_store/storage"
|
|
3
3
|
require "ar_doc_store/embedding"
|
4
4
|
require "ar_doc_store/model"
|
5
5
|
require "ar_doc_store/embeddable_model"
|
6
|
-
require "ar_doc_store/attribute_types/base"
|
7
|
-
require "ar_doc_store/attribute_types/array"
|
8
|
-
require "ar_doc_store/attribute_types/boolean"
|
9
|
-
require "ar_doc_store/attribute_types/enumeration"
|
10
|
-
require "ar_doc_store/attribute_types/float"
|
11
|
-
require "ar_doc_store/attribute_types/integer"
|
12
|
-
require "ar_doc_store/attribute_types/string"
|
13
|
-
require "ar_doc_store/attribute_types/uuid"
|
14
|
-
require "ar_doc_store/attribute_types/embeds_one"
|
15
|
-
require "ar_doc_store/attribute_types/embeds_many"
|
16
6
|
|
17
7
|
module ArDocStore
|
8
|
+
|
9
|
+
module AttributeTypes
|
10
|
+
autoload :BaseAttribute, "ar_doc_store/attribute_types/base_attribute"
|
11
|
+
autoload :ArrayAttribute, "ar_doc_store/attribute_types/array_attribute"
|
12
|
+
autoload :BooleanAttribute, "ar_doc_store/attribute_types/boolean_attribute"
|
13
|
+
autoload :EnumerationAttribute, "ar_doc_store/attribute_types/enumeration_attribute"
|
14
|
+
autoload :FloatAttribute, "ar_doc_store/attribute_types/float_attribute"
|
15
|
+
autoload :IntegerAttribute, "ar_doc_store/attribute_types/integer_attribute"
|
16
|
+
autoload :StringAttribute, "ar_doc_store/attribute_types/string_attribute"
|
17
|
+
autoload :UuidAttribute, "ar_doc_store/attribute_types/uuid_attribute"
|
18
|
+
autoload :EmbedsOneAttribute, "ar_doc_store/attribute_types/embeds_one_attribute"
|
19
|
+
autoload :EmbedsManyAttribute, "ar_doc_store/attribute_types/embeds_many_attribute"
|
20
|
+
end
|
21
|
+
|
18
22
|
@mappings = Hash.new
|
19
23
|
@mappings[:array] = 'ArDocStore::AttributeTypes::ArrayAttribute'
|
20
24
|
@mappings[:boolean] = 'ArDocStore::AttributeTypes::BooleanAttribute'
|
@@ -22,7 +26,6 @@ module ArDocStore
|
|
22
26
|
@mappings[:float] = 'ArDocStore::AttributeTypes::FloatAttribute'
|
23
27
|
@mappings[:integer] = 'ArDocStore::AttributeTypes::IntegerAttribute'
|
24
28
|
@mappings[:string] = 'ArDocStore::AttributeTypes::StringAttribute'
|
25
|
-
@mappings[:json] = 'ArDocStore::AttributeTypes::JsonAttribute'
|
26
29
|
@mappings[:uuid] = 'ArDocStore::AttributeTypes::UuidAttribute'
|
27
30
|
|
28
31
|
def self.mappings
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module ArDocStore
|
2
|
+
module AttributeTypes
|
3
|
+
class BaseAttribute
|
4
|
+
attr_accessor :conversion, :predicate, :options, :model, :attribute, :default
|
5
|
+
|
6
|
+
def self.build(model, attribute, options={})
|
7
|
+
new(model, attribute, options).build
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(model, attribute, options)
|
11
|
+
@model, @attribute, @options = model, attribute, options
|
12
|
+
@model.virtual_attributes[attribute] = self
|
13
|
+
@default = options.delete(:default)
|
14
|
+
end
|
15
|
+
|
16
|
+
def build
|
17
|
+
store_attribute
|
18
|
+
end
|
19
|
+
|
20
|
+
#:nodoc:
|
21
|
+
def store_attribute
|
22
|
+
attribute = @attribute
|
23
|
+
typecast_method = conversion
|
24
|
+
predicate = @predicate
|
25
|
+
default_value = default
|
26
|
+
model.class_eval do
|
27
|
+
add_ransacker(attribute, predicate)
|
28
|
+
define_method attribute.to_sym, -> {
|
29
|
+
value = read_store_attribute(:data, attribute)
|
30
|
+
if value
|
31
|
+
value.public_send(typecast_method)
|
32
|
+
elsif default_value
|
33
|
+
write_default_store_attribute(attribute, default_value)
|
34
|
+
default_value
|
35
|
+
end
|
36
|
+
}
|
37
|
+
define_method "#{attribute}=".to_sym, -> (value) {
|
38
|
+
if value == '' || value.nil?
|
39
|
+
write_store_attribute :data, attribute, nil
|
40
|
+
else
|
41
|
+
write_store_attribute(:data, attribute, value.public_send(typecast_method))
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -6,7 +6,7 @@ module ArDocStore
|
|
6
6
|
|
7
7
|
module AttributeTypes
|
8
8
|
|
9
|
-
class EmbedsManyAttribute <
|
9
|
+
class EmbedsManyAttribute < BaseAttribute
|
10
10
|
|
11
11
|
def build
|
12
12
|
assn_name = attribute.to_sym
|
@@ -35,7 +35,7 @@ module ArDocStore
|
|
35
35
|
my_class_name = class_name.constantize
|
36
36
|
items = read_store_attribute(:data, assn_name)
|
37
37
|
if items.is_a?(Array) || items.is_a?(ArDocStore::EmbeddedCollection)
|
38
|
-
items = ArDocStore::EmbeddedCollection.new items.map { |item| my_class_name.
|
38
|
+
items = ArDocStore::EmbeddedCollection.new items.map { |item| my_class_name.build(item) }
|
39
39
|
else
|
40
40
|
items ||= ArDocStore::EmbeddedCollection.new
|
41
41
|
end
|
@@ -67,8 +67,7 @@ module ArDocStore
|
|
67
67
|
def create_build_method_for(assn_name, class_name)
|
68
68
|
add_method "build_#{assn_name.to_s.singularize}", -> (attributes=nil) {
|
69
69
|
assns = self.public_send assn_name
|
70
|
-
item = class_name.constantize.
|
71
|
-
item.id
|
70
|
+
item = class_name.constantize.build attributes
|
72
71
|
item.parent = self
|
73
72
|
assns << item
|
74
73
|
public_send "#{assn_name}=", assns
|
@@ -148,7 +147,7 @@ module ArDocStore
|
|
148
147
|
end
|
149
148
|
|
150
149
|
def update_attributes(model, value)
|
151
|
-
model.
|
150
|
+
model.attributes = value
|
152
151
|
end
|
153
152
|
|
154
153
|
def wants_to_die?(value)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module ArDocStore
|
2
|
+
module AttributeTypes
|
3
|
+
|
4
|
+
class EmbedsOneAttribute < BaseAttribute
|
5
|
+
attr_reader :class_name
|
6
|
+
def build
|
7
|
+
@class_name = options[:class_name] || attribute.to_s.classify
|
8
|
+
create_accessors
|
9
|
+
create_embed_one_attributes_method
|
10
|
+
create_embeds_one_accessors
|
11
|
+
create_embeds_one_validation
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_accessors
|
15
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
16
|
+
def #{attribute}
|
17
|
+
@#{attribute} || begin
|
18
|
+
item = read_store_attribute :data, :#{attribute}
|
19
|
+
item = #{class_name}.build(item) unless item.is_a?(#{class_name})
|
20
|
+
@#{attribute} = item
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def #{attribute}=(value)
|
25
|
+
if value == '' || !value
|
26
|
+
value = nil
|
27
|
+
elsif value.is_a?(#{class_name})
|
28
|
+
value = value.attributes
|
29
|
+
end
|
30
|
+
value = #{class_name}.build value
|
31
|
+
@#{attribute} = value
|
32
|
+
write_store_attribute :data, :#{attribute}, value
|
33
|
+
end
|
34
|
+
CODE
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_embeds_one_accessors
|
38
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
39
|
+
def build_#{attribute}(attributes=nil)
|
40
|
+
self.#{attribute} = #{class_name}.build(attributes)
|
41
|
+
end
|
42
|
+
def ensure_#{attribute}
|
43
|
+
#{attribute} || build_#{attribute}
|
44
|
+
end
|
45
|
+
CODE
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_embed_one_attributes_method
|
49
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
50
|
+
def #{attribute}_attributes=(values={})
|
51
|
+
values.symbolize_keys! if values.respond_to?(:symbolize_keys!)
|
52
|
+
if values[:_destroy] && (values[:_destroy] == '1')
|
53
|
+
self.#{attribute} = nil
|
54
|
+
else
|
55
|
+
item = ensure_#{attribute}
|
56
|
+
item.attributes = values
|
57
|
+
end
|
58
|
+
end
|
59
|
+
CODE
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_embeds_one_validation
|
63
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
64
|
+
def validate_embedded_record_for_#{attribute}
|
65
|
+
validate_embeds_one :#{attribute}
|
66
|
+
end
|
67
|
+
validate :validate_embedded_record_for_#{attribute}
|
68
|
+
CODE
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
1
3
|
module ArDocStore
|
2
4
|
module EmbeddableModel
|
3
5
|
def self.included(mod)
|
@@ -14,7 +16,8 @@ module ArDocStore
|
|
14
16
|
|
15
17
|
mod.class_eval do
|
16
18
|
attr_accessor :_destroy
|
17
|
-
attr_accessor :
|
19
|
+
attr_accessor :parent
|
20
|
+
attr_reader :attributes
|
18
21
|
|
19
22
|
class_attribute :virtual_attributes
|
20
23
|
self.virtual_attributes ||= HashWithIndifferentAccess.new
|
@@ -22,6 +25,7 @@ module ArDocStore
|
|
22
25
|
delegate :as_json, to: :attributes
|
23
26
|
|
24
27
|
attribute :id, :uuid
|
28
|
+
|
25
29
|
end
|
26
30
|
|
27
31
|
end
|
@@ -29,13 +33,23 @@ module ArDocStore
|
|
29
33
|
module InstanceMethods
|
30
34
|
|
31
35
|
def initialize(attrs=HashWithIndifferentAccess.new)
|
32
|
-
@attributes = HashWithIndifferentAccess.new
|
33
|
-
self.parent = attrs.delete(:parent) if attrs
|
34
|
-
apply_attributes attrs
|
35
36
|
@_initialized = true
|
37
|
+
initialize_attributes attrs
|
38
|
+
end
|
39
|
+
|
40
|
+
def instantiate(attrs=HashWithIndifferentAccess.new)
|
41
|
+
initialize_attributes attrs
|
42
|
+
@_initialized = true
|
43
|
+
self
|
36
44
|
end
|
37
45
|
|
38
|
-
def
|
46
|
+
def initialize_attributes(attrs)
|
47
|
+
@attributes ||= HashWithIndifferentAccess.new
|
48
|
+
self.parent = attributes.delete(:parent) if attributes
|
49
|
+
self.attributes = attrs
|
50
|
+
end
|
51
|
+
|
52
|
+
def attributes=(attrs=HashWithIndifferentAccess.new)
|
39
53
|
virtual_attributes.keys.each do |attr|
|
40
54
|
@attributes[attr] ||= nil
|
41
55
|
end
|
@@ -48,12 +62,6 @@ module ArDocStore
|
|
48
62
|
self
|
49
63
|
end
|
50
64
|
|
51
|
-
# TODO: This doesn't work very well for embeds_many because the parent needs to have its setter triggered
|
52
|
-
# before the embedded model will actually get saved.
|
53
|
-
def save
|
54
|
-
parent && parent.save
|
55
|
-
end
|
56
|
-
|
57
65
|
def persisted?
|
58
66
|
false
|
59
67
|
end
|
@@ -62,23 +70,33 @@ module ArDocStore
|
|
62
70
|
"#{self.class}: #{attributes.inspect}"
|
63
71
|
end
|
64
72
|
|
65
|
-
def read_store_attribute(store,
|
66
|
-
|
73
|
+
def read_store_attribute(store, attr)
|
74
|
+
attributes[attr]
|
67
75
|
end
|
68
76
|
|
69
|
-
def write_store_attribute(store,
|
70
|
-
|
71
|
-
|
77
|
+
def write_store_attribute(store, attribute, value)
|
78
|
+
if @_initialized
|
79
|
+
old_value = attributes[attribute]
|
80
|
+
if attribute.to_s != 'id' && value != old_value
|
81
|
+
public_send :"#{attribute}_will_change!"
|
82
|
+
parent.data_will_change! if parent
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
attributes[attribute] = value
|
72
87
|
end
|
73
88
|
|
74
|
-
def write_default_store_attribute(
|
75
|
-
|
89
|
+
def write_default_store_attribute(attr, value)
|
90
|
+
attributes[attr] = value
|
76
91
|
end
|
77
92
|
|
78
93
|
def to_param
|
79
94
|
id
|
80
95
|
end
|
81
|
-
|
96
|
+
|
97
|
+
def id_will_change!
|
98
|
+
end
|
99
|
+
|
82
100
|
end
|
83
101
|
|
84
102
|
module ClassMethods
|
@@ -91,7 +109,17 @@ module ArDocStore
|
|
91
109
|
define_method key, -> { read_store_attribute(:data, key) }
|
92
110
|
define_method "#{key}=".to_sym, -> (value) { write_store_attribute :data, key, value }
|
93
111
|
end
|
94
|
-
|
112
|
+
|
113
|
+
def build(attrs=HashWithIndifferentAccess.new)
|
114
|
+
if attrs.is_a?(self.class)
|
115
|
+
attrs
|
116
|
+
else
|
117
|
+
instance = allocate
|
118
|
+
instance.instantiate attrs
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
|
95
123
|
end
|
96
124
|
|
97
125
|
end
|
data/lib/ar_doc_store/storage.rb
CHANGED
@@ -66,10 +66,10 @@ module ArDocStore
|
|
66
66
|
options = args.extract_options!
|
67
67
|
type ||= options.delete(:as) || :string
|
68
68
|
class_name = ArDocStore.mappings[type] || "ArDocStore::AttributeTypes::#{type.to_s.classify}Attribute"
|
69
|
-
raise "Invalid attribute type: #{
|
70
|
-
class_name
|
71
|
-
class_name.build self, name, options
|
69
|
+
raise "Invalid attribute type: #{class_name}" unless const_defined?(class_name)
|
70
|
+
class_name.constantize.build self, name, options
|
72
71
|
define_virtual_attribute_method name
|
72
|
+
define_method "#{name}?", -> { public_send(name).present? }
|
73
73
|
end
|
74
74
|
|
75
75
|
#:nodoc:
|
@@ -84,64 +84,6 @@ module ArDocStore
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
#:nodoc:
|
88
|
-
def store_attribute(attribute, typecast_method, predicate=nil, default_value=nil)
|
89
|
-
store_accessor :data, attribute
|
90
|
-
add_ransacker(attribute, predicate)
|
91
|
-
if typecast_method.is_a?(Symbol)
|
92
|
-
store_attribute_from_symbol typecast_method, attribute, default_value
|
93
|
-
else
|
94
|
-
store_attribute_from_class typecast_method, attribute
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
#:nodoc:
|
99
|
-
def store_attribute_from_symbol(typecast_method, key, default_value)
|
100
|
-
define_method key.to_sym, -> {
|
101
|
-
value = read_store_attribute(:data, key)
|
102
|
-
if value
|
103
|
-
value.public_send(typecast_method)
|
104
|
-
elsif default_value
|
105
|
-
write_default_store_attribute(key, default_value)
|
106
|
-
default_value
|
107
|
-
end
|
108
|
-
}
|
109
|
-
define_method "#{key}=".to_sym, -> (value) {
|
110
|
-
if value == '' || value.nil?
|
111
|
-
write_store_attribute :data, key, nil
|
112
|
-
else
|
113
|
-
write_store_attribute(:data, key, value.public_send(typecast_method))
|
114
|
-
end
|
115
|
-
}
|
116
|
-
end
|
117
|
-
|
118
|
-
# TODO: add default value support.
|
119
|
-
# Default ought to be a hash of attributes,
|
120
|
-
# also accept a proc that receives the newly initialized object
|
121
|
-
def store_attribute_from_class(class_name, key)
|
122
|
-
define_method key.to_sym, -> {
|
123
|
-
ivar = "@#{key}"
|
124
|
-
instance_variable_get(ivar) || begin
|
125
|
-
item = read_store_attribute(:data, key)
|
126
|
-
class_name = class_name.constantize if class_name.respond_to?(:constantize)
|
127
|
-
item = class_name.new(item) unless item.is_a?(class_name)
|
128
|
-
instance_variable_set ivar, item
|
129
|
-
end
|
130
|
-
}
|
131
|
-
define_method "#{key}=".to_sym, -> (value) {
|
132
|
-
ivar = "@#{key}"
|
133
|
-
existing = public_send(key)
|
134
|
-
class_name = class_name.constantize if class_name.respond_to?(:constantize)
|
135
|
-
if value == '' || !value
|
136
|
-
value = nil
|
137
|
-
elsif !value.is_a?(class_name)
|
138
|
-
value = existing.apply_attributes(value)
|
139
|
-
end
|
140
|
-
instance_variable_set ivar, value
|
141
|
-
write_store_attribute :data, key, value
|
142
|
-
}
|
143
|
-
end
|
144
|
-
|
145
87
|
# Pretty much the same as define_attribute_method but skipping the matches that create read and write methods
|
146
88
|
def define_virtual_attribute_method(attr_name)
|
147
89
|
attr_name = attr_name.to_s
|
data/lib/ar_doc_store/version.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative './../test_helper'
|
2
|
+
|
3
|
+
class ArrayAttributeTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_attribute_on_model_init
|
6
|
+
architects = %W{Bob John Billy Bob}
|
7
|
+
b = Building.new architects: architects
|
8
|
+
assert_equal architects, b.architects
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_attribute_on_existing_model
|
12
|
+
architects = %W{Bob John Billy Bob}
|
13
|
+
b = Building.new
|
14
|
+
b.architects = architects
|
15
|
+
assert_equal architects, b.architects
|
16
|
+
assert b.architects_changed?
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_question_mark_method
|
20
|
+
b = Building.new architects: %W{Bob John}
|
21
|
+
assert_equal true, b.architects?
|
22
|
+
end
|
23
|
+
|
24
|
+
# Type conversion doesn't make sense here...
|
25
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative './../test_helper'
|
2
|
+
|
3
|
+
class BooleanAttributeTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_attribute_on_model_init
|
6
|
+
b = Building.new finished: true
|
7
|
+
assert_equal true, b.finished
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_attribute_on_existing_model
|
11
|
+
b = Building.new
|
12
|
+
b.finished = true
|
13
|
+
assert_equal true, b.finished
|
14
|
+
assert b.finished_changed?
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_question_mark_method
|
18
|
+
b = Building.new finished: true
|
19
|
+
assert_equal true, b.finished?
|
20
|
+
end
|
21
|
+
|
22
|
+
# The setter function doesn't appear to get called in this context.
|
23
|
+
# But more likely traces to ARDuck.
|
24
|
+
# TODO: Does this still fail after replacing ARDuck with AR::Base?
|
25
|
+
def test_type_conversion_on_init
|
26
|
+
b = Building.new finished: '1'
|
27
|
+
assert_equal true, b.finished
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_type_conversion_on_existing
|
31
|
+
b = Building.new
|
32
|
+
b.finished = '1'
|
33
|
+
assert_equal true, b.finished
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -1,49 +1,6 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative './../test_helper'
|
2
2
|
|
3
|
-
class
|
4
|
-
def test_string_attribute_on_model_init
|
5
|
-
b = Building.new name: 'test'
|
6
|
-
assert_equal 'test', b.name
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_string_attribute_on_existing_model
|
10
|
-
b = Building.new
|
11
|
-
b.name = 'test'
|
12
|
-
assert_equal 'test', b.name
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_boolean_attribute_on_model_init
|
16
|
-
b = Building.new finished: true
|
17
|
-
assert b.finished?
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_boolean_attribute_on_existing_model
|
21
|
-
b = Building.new
|
22
|
-
b.finished = true
|
23
|
-
assert b.finished?
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_float_attribute_on_init
|
27
|
-
b = Building.new height: 54.45
|
28
|
-
assert_equal 54.45, b.height
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_float_attribute_on_existing_model
|
32
|
-
b = Building.new
|
33
|
-
b.height = 54.45
|
34
|
-
assert_equal 54.45, b.height
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_int_attribute_on_init
|
38
|
-
b = Building.new stories: 5
|
39
|
-
assert_equal 5, b.stories
|
40
|
-
end
|
41
|
-
|
42
|
-
def test_int_attribute_on_set
|
43
|
-
b = Building.new
|
44
|
-
b.stories = 5
|
45
|
-
assert_equal 5, b.stories
|
46
|
-
end
|
3
|
+
class EnumerationAttributeTest < MiniTest::Test
|
47
4
|
|
48
5
|
def test_simple_enumeration_attribute
|
49
6
|
b = Building.new construction: 'wood'
|
@@ -96,5 +53,10 @@ class ModelAttributeAccessTest < MiniTest::Test
|
|
96
53
|
def test_enumeration_has_choices_to_use_for_select
|
97
54
|
assert Building.construction_choices.present?
|
98
55
|
end
|
99
|
-
|
56
|
+
|
57
|
+
def test_question_mark_method
|
58
|
+
b = Building.new strict_multi_enumeration: %w{glad bad}
|
59
|
+
assert_equal true, b.strict_multi_enumeration?
|
60
|
+
end
|
61
|
+
|
100
62
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require_relative './../test_helper'
|
2
|
+
|
3
|
+
class FloatAttributeTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_attribute_on_model_init
|
6
|
+
b = Building.new height: 5.42
|
7
|
+
assert_equal 5.42, b.height
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_attribute_on_existing_model
|
11
|
+
b = Building.new
|
12
|
+
b.height = 5.42
|
13
|
+
assert_equal 5.42, b.height
|
14
|
+
assert b.height_changed?
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_question_mark_method
|
18
|
+
b = Building.new height: 5.42
|
19
|
+
assert_equal true, b.height?
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_type_conversion_on_init
|
23
|
+
b = Building.new height: '5.42'
|
24
|
+
assert_equal 5.42, b.height
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_type_conversion_on_existing
|
28
|
+
b = Building.new
|
29
|
+
b.height = '5.42'
|
30
|
+
assert_equal 5.42, b.height
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
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
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative './../test_helper'
|
2
|
+
|
3
|
+
class StringAttributeTest < MiniTest::Test
|
4
|
+
|
5
|
+
def test_attribute_on_model_init
|
6
|
+
b = Building.new name: 'test'
|
7
|
+
assert_equal 'test', b.name
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_attribute_on_existing_model
|
11
|
+
b = Building.new
|
12
|
+
b.name = 'test'
|
13
|
+
assert_equal 'test', b.name
|
14
|
+
assert b.name_changed?
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_question_mark_method
|
18
|
+
b = Building.new name: 'test'
|
19
|
+
assert_equal true, b.name?
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_conversion
|
23
|
+
b = Building.new name: 51
|
24
|
+
assert_equal '51', b.name
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
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
|
data/test/test_helper.rb
CHANGED
@@ -5,6 +5,7 @@ require 'minitest/autorun'
|
|
5
5
|
require 'active_record'
|
6
6
|
|
7
7
|
require_relative './../lib/ar_doc_store'
|
8
|
+
ActiveRecord::Base.establish_connection(adapter: 'postgresql', database: 'ar_doc_store_test', username: 'postgres', password: 'postgres')
|
8
9
|
|
9
10
|
# A building has many entrances and restrooms and some fields of its own
|
10
11
|
# An entrance has a door, a route, and some fields of its own
|
@@ -26,7 +27,7 @@ class ARDuck
|
|
26
27
|
@attributes = HashWithIndifferentAccess.new
|
27
28
|
unless attrs.nil?
|
28
29
|
attrs.each { |key, value|
|
29
|
-
@attributes[key] = value
|
30
|
+
@attributes[key] = public_send("#{key}=", value)
|
30
31
|
}
|
31
32
|
end
|
32
33
|
@_initialized = true
|
@@ -125,13 +126,14 @@ class Restroom
|
|
125
126
|
|
126
127
|
end
|
127
128
|
|
128
|
-
class Building <
|
129
|
+
class Building < ActiveRecord::Base
|
129
130
|
include ArDocStore::Model
|
130
131
|
attribute :name, :string
|
131
132
|
attribute :comments, as: :string
|
132
133
|
attribute :finished, :boolean
|
133
134
|
attribute :stories, as: :integer
|
134
135
|
attribute :height, as: :float
|
136
|
+
attribute :architects, as: :array
|
135
137
|
attribute :construction, as: :enumeration, values: %w{concrete wood brick plaster steel}
|
136
138
|
attribute :multiconstruction, as: :enumeration, values: %w{concrete wood brick plaster steel}, multiple: true
|
137
139
|
attribute :strict_enumeration, as: :enumeration, values: %w{happy sad glad bad}, strict: true
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar_doc_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Furber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-04-
|
11
|
+
date: 2015-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '0.17'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '0.17'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,26 +83,30 @@ files:
|
|
83
83
|
- Rakefile
|
84
84
|
- ar_doc_store.gemspec
|
85
85
|
- lib/ar_doc_store.rb
|
86
|
-
- lib/ar_doc_store/attribute_types/
|
87
|
-
- lib/ar_doc_store/attribute_types/
|
88
|
-
- lib/ar_doc_store/attribute_types/
|
89
|
-
- lib/ar_doc_store/attribute_types/
|
90
|
-
- lib/ar_doc_store/attribute_types/
|
91
|
-
- lib/ar_doc_store/attribute_types/
|
92
|
-
- lib/ar_doc_store/attribute_types/
|
93
|
-
- lib/ar_doc_store/attribute_types/
|
94
|
-
- lib/ar_doc_store/attribute_types/
|
95
|
-
- lib/ar_doc_store/attribute_types/
|
96
|
-
- lib/ar_doc_store/attribute_types/uuid.rb
|
86
|
+
- lib/ar_doc_store/attribute_types/array_attribute.rb
|
87
|
+
- lib/ar_doc_store/attribute_types/base_attribute.rb
|
88
|
+
- lib/ar_doc_store/attribute_types/boolean_attribute.rb
|
89
|
+
- lib/ar_doc_store/attribute_types/embeds_many_attribute.rb
|
90
|
+
- lib/ar_doc_store/attribute_types/embeds_one_attribute.rb
|
91
|
+
- lib/ar_doc_store/attribute_types/enumeration_attribute.rb
|
92
|
+
- lib/ar_doc_store/attribute_types/float_attribute.rb
|
93
|
+
- lib/ar_doc_store/attribute_types/integer_attribute.rb
|
94
|
+
- lib/ar_doc_store/attribute_types/string_attribute.rb
|
95
|
+
- lib/ar_doc_store/attribute_types/uuid_attribute.rb
|
97
96
|
- lib/ar_doc_store/embeddable_model.rb
|
98
97
|
- lib/ar_doc_store/embedding.rb
|
99
98
|
- lib/ar_doc_store/model.rb
|
100
99
|
- lib/ar_doc_store/storage.rb
|
101
100
|
- lib/ar_doc_store/version.rb
|
102
|
-
- test/
|
103
|
-
- test/
|
104
|
-
- test/
|
105
|
-
- test/
|
101
|
+
- test/attribute_types/array_attribute_test.rb
|
102
|
+
- test/attribute_types/boolean_attribute_test.rb
|
103
|
+
- test/attribute_types/enumeration_attribute_test.rb
|
104
|
+
- test/attribute_types/float_attribute_test.rb
|
105
|
+
- test/attribute_types/integer_attribute_test.rb
|
106
|
+
- test/attribute_types/string_attribute_test.rb
|
107
|
+
- test/originals/dirty_attributes_test.rb
|
108
|
+
- test/originals/embedded_model_attribute_test.rb
|
109
|
+
- test/originals/embedding_test.rb
|
106
110
|
- test/test_helper.rb
|
107
111
|
homepage: https://github.com/dfurber/ar_doc_store
|
108
112
|
licenses:
|
@@ -129,8 +133,13 @@ signing_key:
|
|
129
133
|
specification_version: 4
|
130
134
|
summary: A document storage gem meant for ActiveRecord PostgresQL JSON storage.
|
131
135
|
test_files:
|
132
|
-
- test/
|
133
|
-
- test/
|
134
|
-
- test/
|
135
|
-
- test/
|
136
|
+
- test/attribute_types/array_attribute_test.rb
|
137
|
+
- test/attribute_types/boolean_attribute_test.rb
|
138
|
+
- test/attribute_types/enumeration_attribute_test.rb
|
139
|
+
- test/attribute_types/float_attribute_test.rb
|
140
|
+
- test/attribute_types/integer_attribute_test.rb
|
141
|
+
- test/attribute_types/string_attribute_test.rb
|
142
|
+
- test/originals/dirty_attributes_test.rb
|
143
|
+
- test/originals/embedded_model_attribute_test.rb
|
144
|
+
- test/originals/embedding_test.rb
|
136
145
|
- test/test_helper.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module ArDocStore
|
2
|
-
module AttributeTypes
|
3
|
-
class Base
|
4
|
-
attr_accessor :conversion, :predicate, :options, :model, :attribute, :default
|
5
|
-
|
6
|
-
def self.build(model, attribute, options={})
|
7
|
-
new(model, attribute, options).build
|
8
|
-
end
|
9
|
-
|
10
|
-
def initialize(model, attribute, options)
|
11
|
-
@model, @attribute, @options = model, attribute, options
|
12
|
-
@model.virtual_attributes[attribute] = self
|
13
|
-
@default = options.delete(:default)
|
14
|
-
end
|
15
|
-
|
16
|
-
def build
|
17
|
-
model.store_attribute attribute, conversion, predicate, default
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module ArDocStore
|
2
|
-
module AttributeTypes
|
3
|
-
|
4
|
-
class EmbedsOneAttribute < Base
|
5
|
-
def build
|
6
|
-
assn_name = attribute.to_sym
|
7
|
-
class_name = options[:class_name] || attribute.to_s.classify
|
8
|
-
model.store_accessor :data, assn_name
|
9
|
-
model.store_attribute_from_class class_name, assn_name
|
10
|
-
create_embed_one_attributes_method(assn_name)
|
11
|
-
create_embeds_one_accessors assn_name, class_name
|
12
|
-
create_embeds_one_validation(assn_name)
|
13
|
-
end
|
14
|
-
|
15
|
-
def create_embeds_one_accessors(assn_name, class_name)
|
16
|
-
model.class_eval do
|
17
|
-
define_method "build_#{assn_name}", -> (attributes=nil) {
|
18
|
-
class_name = class_name.constantize if class_name.respond_to?(:constantize)
|
19
|
-
public_send "#{assn_name}=", class_name.new(attributes)
|
20
|
-
public_send assn_name
|
21
|
-
}
|
22
|
-
define_method "ensure_#{assn_name}", -> {
|
23
|
-
public_send "build_#{assn_name}" if public_send(assn_name).blank?
|
24
|
-
}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def create_embed_one_attributes_method(assn_name)
|
29
|
-
model.class_eval do
|
30
|
-
define_method "#{assn_name}_attributes=", -> (values) {
|
31
|
-
values ||= {}
|
32
|
-
values.symbolize_keys! if values.respond_to?(:symbolize_keys!)
|
33
|
-
if values[:_destroy] && (values[:_destroy] == '1')
|
34
|
-
self.public_send "#{assn_name}=", nil
|
35
|
-
else
|
36
|
-
public_send "#{assn_name}=", values
|
37
|
-
end
|
38
|
-
}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def create_embeds_one_validation(assn_name)
|
43
|
-
model.class_eval do
|
44
|
-
validate_method = "validate_embedded_record_for_#{assn_name}"
|
45
|
-
define_method validate_method, -> { validate_embeds_one assn_name }
|
46
|
-
validate validate_method
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require_relative './test_helper'
|
2
|
-
|
3
|
-
class DirtylAttributeTest < MiniTest::Test
|
4
|
-
|
5
|
-
def test_dirty_attributes_on_model
|
6
|
-
b = Building.new name: 'Foo!'
|
7
|
-
assert_equal b.name_changed?, false
|
8
|
-
b.name = 'Bar.'
|
9
|
-
assert b.name_changed?
|
10
|
-
assert_equal 'Foo!', b.name_was
|
11
|
-
assert_equal 'Bar.', b.name
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_dirty_attributes_on_embedded_model
|
15
|
-
b = Building.new
|
16
|
-
r = b.build_restroom is_signage_clear: true
|
17
|
-
assert_equal r.is_signage_clear_changed?, false
|
18
|
-
r.is_signage_clear = false
|
19
|
-
assert r.is_signage_clear_changed?
|
20
|
-
assert_equal true, r.is_signage_clear_was
|
21
|
-
assert_equal false, r.is_signage_clear
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|