ar_doc_store 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +34 -7
- data/Rakefile +3 -0
- data/lib/ar_doc_store.rb +2 -0
- data/lib/ar_doc_store/attribute_types/base_attribute.rb +15 -6
- data/lib/ar_doc_store/attribute_types/datetime_attribute.rb +17 -0
- data/lib/ar_doc_store/storage.rb +2 -2
- data/lib/ar_doc_store/version.rb +1 -1
- data/test/attribute_types/datetime_attribute_test.rb +35 -0
- data/test/test_helper.rb +7 -0
- metadata +17 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 362c4a4c014d334818371ebac25b31607a288644
|
4
|
+
data.tar.gz: 58896da6067be1c5a3cf1db37daefc6c9887fce6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3becf0c88e32a96ad2fd7ff5422e828e53cd3a203485c2bfc19e657bf61ebb3236dbe89e1d2b0ffd0fe59de70cd8de28c8ad4a581040f3c2d02dfcb13985e4f
|
7
|
+
data.tar.gz: 2a668359a96d0085c2d37bcede71cd2da2962a1ec4cf334f921956c323b4e2fc53437c3a750f3b3055ff9fe92c7ed8b424babd1037df53fcd522008024388862
|
data/README.md
CHANGED
@@ -153,11 +153,11 @@ We probably have a form for the building, so here goes:
|
|
153
153
|
```ruby
|
154
154
|
# in the controller:
|
155
155
|
def new
|
156
|
-
|
156
|
+
@building = Building.new
|
157
157
|
end
|
158
158
|
|
159
159
|
def resource_params
|
160
|
-
|
160
|
+
params.require(:building).permit :name, :height, door_attributes: [:door_type]
|
161
161
|
end
|
162
162
|
|
163
163
|
# in the view, with a bonus plug for Slim templates:
|
@@ -205,10 +205,12 @@ Building.ransack name_cont: 'tall', height_lteq: 20
|
|
205
205
|
|
206
206
|
### Custom attribute types
|
207
207
|
|
208
|
-
ArDocStore comes with several basic attribute types: array, boolean, enumeration, float, integer, and string. The implementation and extension points are inspired by SimpleForm. You can either create a new attribute type or overwrite an existing one. Forewarned is forestalled, maybe: as with SimpleForm, the custom input system is way easier to use if you were the one who built it, and it's still a little raw.
|
208
|
+
ArDocStore comes with several basic attribute types: array, boolean, enumeration, float, integer, and string. The implementation and extension points are inspired by SimpleForm. You can either create a new attribute type or overwrite an existing one. Forewarned is forestalled, maybe: as with SimpleForm, the custom input system is way easier to use if you were the one who built it, and it's still a little raw.
|
209
|
+
|
210
|
+
All attributes inherit from `AttributeTypes::BaseAttribute` and custom attributes are implemented by overriding that class's methods. Let's start with the implementation of `:integer` :
|
209
211
|
|
210
212
|
```ruby
|
211
|
-
class IntegerAttribute <
|
213
|
+
class IntegerAttribute < ArDocStore::AttributeTypes::BaseAttribute
|
212
214
|
def conversion
|
213
215
|
:to_i
|
214
216
|
end
|
@@ -216,15 +218,20 @@ class IntegerAttribute < Base
|
|
216
218
|
def predicate
|
217
219
|
'int'
|
218
220
|
end
|
221
|
+
|
222
|
+
def type
|
223
|
+
:number
|
224
|
+
end
|
219
225
|
end
|
226
|
+
|
220
227
|
```
|
221
228
|
|
222
|
-
|
229
|
+
The data is put into a JSON column, so we want to make sure we serialize it correctly. The `conversion` method is a symbol or a string. It is called by the `dump` and `load` (in `BaseAttribute`) methods and is sent to the data when it is written and read. This makes sure that the data doesn't go in a integer and come back as a string. The `predicate` method tells Postgres (via the ransacker) how to cast the JSON for searching.
|
223
230
|
|
224
|
-
Not all attribute types are that simple. Sometimes we have to put all the juice in the build method, and take care to define the getter and setter ourselves. In this example, we want to replace the boolean attribute with a similar boolean that can do Yes, No, and N/A, where N/A isn't simply nil but something the user has to choose. Here goes:
|
231
|
+
Not all attribute types are that simple. Sometimes we have to put all the juice in the `build` method, and take care to define the getter and setter ourselves. In this example, we want to replace the boolean attribute with a similar boolean that can do Yes, No, and N/A, where N/A isn't simply nil but something the user has to choose. Here goes:
|
225
232
|
|
226
233
|
```ruby
|
227
|
-
class BooleanAttribute < ArDocStore::AttributeTypes::
|
234
|
+
class BooleanAttribute < ArDocStore::AttributeTypes::BaseAttribute
|
228
235
|
def build
|
229
236
|
key = attribute.to_sym
|
230
237
|
model.class_eval do
|
@@ -260,6 +267,26 @@ class BooleanInput < SimpleForm::Inputs::CollectionRadioButtonsInput
|
|
260
267
|
end
|
261
268
|
```
|
262
269
|
|
270
|
+
#### What if I need different conversions for reading and writing?
|
271
|
+
|
272
|
+
The `:datetime` attribute requires the data be cast to a string when written, but cast to a `Time` object when read. This overrides the `dump` and `load` methods instead. Here is the implementation:
|
273
|
+
|
274
|
+
```ruby
|
275
|
+
class DatetimeAttribute < ArDocStore::AttributeTypes::BaseAttribute
|
276
|
+
def type
|
277
|
+
:datetime
|
278
|
+
end
|
279
|
+
|
280
|
+
def dump
|
281
|
+
:to_time
|
282
|
+
end
|
283
|
+
|
284
|
+
def load
|
285
|
+
:to_s
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
289
|
+
|
263
290
|
## Roadmap
|
264
291
|
1. Ransackers for embedded model attributes. Deliberately left out because when you have nested JSON, Postgres searches on any keys within the JSON become 10x slower.
|
265
292
|
2. It would be nice if you could use the AR fluent query API on stored attributes, where is knows to replace, say, "name" with "data->>name" but I don't see how to do that, and Ransack provides a nice enough wrapper around ARel to get the job done another way.
|
data/Rakefile
CHANGED
data/lib/ar_doc_store.rb
CHANGED
@@ -17,6 +17,7 @@ module ArDocStore
|
|
17
17
|
autoload :UuidAttribute, "ar_doc_store/attribute_types/uuid_attribute"
|
18
18
|
autoload :EmbedsOneAttribute, "ar_doc_store/attribute_types/embeds_one_attribute"
|
19
19
|
autoload :EmbedsManyAttribute, "ar_doc_store/attribute_types/embeds_many_attribute"
|
20
|
+
autoload :DatetimeAttribute, "ar_doc_store/attribute_types/datetime_attribute"
|
20
21
|
end
|
21
22
|
|
22
23
|
@mappings = Hash.new
|
@@ -27,6 +28,7 @@ module ArDocStore
|
|
27
28
|
@mappings[:integer] = 'ArDocStore::AttributeTypes::IntegerAttribute'
|
28
29
|
@mappings[:string] = 'ArDocStore::AttributeTypes::StringAttribute'
|
29
30
|
@mappings[:uuid] = 'ArDocStore::AttributeTypes::UuidAttribute'
|
31
|
+
@mappings[:datetime] = 'ArDocStore::AttributeTypes::DatetimeAttribute'
|
30
32
|
|
31
33
|
def self.mappings
|
32
34
|
@mappings
|
@@ -20,15 +20,16 @@ module ArDocStore
|
|
20
20
|
#:nodoc:
|
21
21
|
def store_attribute
|
22
22
|
attribute = @attribute
|
23
|
-
|
24
|
-
predicate = @predicate
|
23
|
+
predicate_method = predicate
|
25
24
|
default_value = default
|
25
|
+
dump_method = dump
|
26
|
+
load_method = load
|
26
27
|
model.class_eval do
|
27
|
-
add_ransacker(attribute,
|
28
|
+
add_ransacker(attribute, predicate_method)
|
28
29
|
define_method attribute.to_sym, -> {
|
29
30
|
value = read_store_attribute(json_column, attribute)
|
30
31
|
if value
|
31
|
-
value.public_send(
|
32
|
+
value.public_send(load_method)
|
32
33
|
elsif default_value
|
33
34
|
write_default_store_attribute(attribute, default_value)
|
34
35
|
default_value
|
@@ -38,15 +39,23 @@ module ArDocStore
|
|
38
39
|
if value == '' || value.nil?
|
39
40
|
write_store_attribute json_column, attribute, nil
|
40
41
|
else
|
41
|
-
write_store_attribute(json_column, attribute, value.public_send(
|
42
|
+
write_store_attribute(json_column, attribute, value.public_send(dump_method))
|
42
43
|
end
|
43
44
|
}
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
48
|
+
def conversion
|
49
|
+
:to_s
|
50
|
+
end
|
47
51
|
|
52
|
+
def dump
|
53
|
+
conversion
|
54
|
+
end
|
48
55
|
|
56
|
+
def load
|
57
|
+
conversion
|
58
|
+
end
|
49
59
|
end
|
50
|
-
|
51
60
|
end
|
52
61
|
end
|
data/lib/ar_doc_store/storage.rb
CHANGED
@@ -81,8 +81,8 @@ module ArDocStore
|
|
81
81
|
#:nodoc:
|
82
82
|
def add_ransacker(key, predicate = nil)
|
83
83
|
return unless respond_to?(:ransacker)
|
84
|
-
ransacker key do
|
85
|
-
sql = "(data->>'#{key}')"
|
84
|
+
ransacker key do |parent|
|
85
|
+
sql = "(#{parent.table[:data]}->>'#{key}')"
|
86
86
|
if predicate
|
87
87
|
sql = "#{sql}::#{predicate}"
|
88
88
|
end
|
data/lib/ar_doc_store/version.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
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
|
data/test/test_helper.rb
CHANGED
@@ -141,3 +141,10 @@ class Building < ActiveRecord::Base
|
|
141
141
|
embeds_many :entrances
|
142
142
|
embeds_many :restrooms
|
143
143
|
end
|
144
|
+
|
145
|
+
class PurchaseOrder < ActiveRecord::Base
|
146
|
+
include ArDocStore::Model
|
147
|
+
attribute :name, :string
|
148
|
+
attribute :price, :float
|
149
|
+
attribute :approved_at, :datetime
|
150
|
+
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar_doc_store
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Furber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-04-18 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: '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: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
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
40
|
version: '0.17'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.7'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.7'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '10.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '10.0'
|
69
69
|
description: 'Provides an easy way to do something that is possible in Rails but still
|
@@ -76,7 +76,7 @@ executables: []
|
|
76
76
|
extensions: []
|
77
77
|
extra_rdoc_files: []
|
78
78
|
files:
|
79
|
-
-
|
79
|
+
- .gitignore
|
80
80
|
- Gemfile
|
81
81
|
- LICENSE.txt
|
82
82
|
- README.md
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- lib/ar_doc_store/attribute_types/array_attribute.rb
|
87
87
|
- lib/ar_doc_store/attribute_types/base_attribute.rb
|
88
88
|
- lib/ar_doc_store/attribute_types/boolean_attribute.rb
|
89
|
+
- lib/ar_doc_store/attribute_types/datetime_attribute.rb
|
89
90
|
- lib/ar_doc_store/attribute_types/embeds_many_attribute.rb
|
90
91
|
- lib/ar_doc_store/attribute_types/embeds_one_attribute.rb
|
91
92
|
- lib/ar_doc_store/attribute_types/enumeration_attribute.rb
|
@@ -100,6 +101,7 @@ files:
|
|
100
101
|
- lib/ar_doc_store/version.rb
|
101
102
|
- test/attribute_types/array_attribute_test.rb
|
102
103
|
- test/attribute_types/boolean_attribute_test.rb
|
104
|
+
- test/attribute_types/datetime_attribute_test.rb
|
103
105
|
- test/attribute_types/enumeration_attribute_test.rb
|
104
106
|
- test/attribute_types/float_attribute_test.rb
|
105
107
|
- test/attribute_types/integer_attribute_test.rb
|
@@ -118,23 +120,24 @@ require_paths:
|
|
118
120
|
- lib
|
119
121
|
required_ruby_version: !ruby/object:Gem::Requirement
|
120
122
|
requirements:
|
121
|
-
- -
|
123
|
+
- - '>='
|
122
124
|
- !ruby/object:Gem::Version
|
123
125
|
version: '0'
|
124
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
127
|
requirements:
|
126
|
-
- -
|
128
|
+
- - '>='
|
127
129
|
- !ruby/object:Gem::Version
|
128
130
|
version: '0'
|
129
131
|
requirements: []
|
130
132
|
rubyforge_project:
|
131
|
-
rubygems_version: 2.
|
133
|
+
rubygems_version: 2.0.14.1
|
132
134
|
signing_key:
|
133
135
|
specification_version: 4
|
134
136
|
summary: A document storage gem meant for ActiveRecord PostgresQL JSON storage.
|
135
137
|
test_files:
|
136
138
|
- test/attribute_types/array_attribute_test.rb
|
137
139
|
- test/attribute_types/boolean_attribute_test.rb
|
140
|
+
- test/attribute_types/datetime_attribute_test.rb
|
138
141
|
- test/attribute_types/enumeration_attribute_test.rb
|
139
142
|
- test/attribute_types/float_attribute_test.rb
|
140
143
|
- test/attribute_types/integer_attribute_test.rb
|