jsonb_accessor 0.2.0 → 0.3.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/.rubocop.yml +2 -0
- data/Appraisals +4 -0
- data/README.md +253 -27
- data/bin/console +0 -4
- data/gemfiles/activerecord_4.2.1.gemfile.lock +7 -6
- data/gemfiles/activerecord_4.2.2.gemfile +7 -0
- data/gemfiles/activerecord_4.2.2.gemfile.lock +143 -0
- data/jsonb_accessor.gemspec +2 -1
- data/lib/jsonb_accessor.rb +3 -0
- data/lib/jsonb_accessor/class_builder.rb +67 -43
- data/lib/jsonb_accessor/fields_map.rb +32 -0
- data/lib/jsonb_accessor/helpers.rb +19 -0
- data/lib/jsonb_accessor/macro.rb +101 -56
- data/lib/jsonb_accessor/nested_base.rb +2 -0
- data/lib/jsonb_accessor/type_helper.rb +9 -0
- data/lib/jsonb_accessor/version.rb +1 -1
- metadata +24 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3280838fd8b45ffea5fd3892240799bbb6785f7
|
4
|
+
data.tar.gz: 561e28860a5d43e25afc95ef79473480d2cc368e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 080851d0cc5ea02c5d443a65473d97fd588de21c24011bbb65ab1ec7a96bdbc74b9b44298234d322dc942f8adf7ee775fb0d2fdb336cdb59455ec3f6d9a72e52
|
7
|
+
data.tar.gz: 1430c66ba53b9a01ce052043fbb49e75a1f90233f27de85a77e619eaa04e397853aeb7c82a5f2f1e8a0cf5f2ebe1f859e6680b9fc7161b160389c3b0366986b5
|
data/.rubocop.yml
CHANGED
data/Appraisals
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,21 @@
|
|
1
1
|
# JSONb Accessor
|
2
2
|
|
3
|
-
[](https://travis-ci.org/devmynd/jsonb_accessor)
|
3
|
+
[](http://badge.fury.io/rb/jsonb_accessor) [](https://travis-ci.org/devmynd/jsonb_accessor)
|
4
4
|
|
5
5
|
Adds typed `jsonb` backed fields as first class citizens to your `ActiveRecord` models. This gem is similar in spirit to [HstoreAccessor](https://github.com/devmynd/hstore_accessor), but the `jsonb` column in PostgreSQL has a few distinct advantages, mostly around nested documents and support for collections.
|
6
6
|
|
7
|
-
|
7
|
+
## Table of Contents
|
8
|
+
|
9
|
+
* [Installation](#installation)
|
10
|
+
* [Usage](#usage)
|
11
|
+
* [ActiveRecord Methods Generated for Fields](#activerecord-methods-generated-for-fields)
|
12
|
+
* [Validations](#validations)
|
13
|
+
* [Single-Table Inheritance](#single-table-inheritance)
|
14
|
+
* [Scopes](#scopes)
|
15
|
+
* [Migrations](#migrations)
|
16
|
+
* [Dependencies](#dependencies)
|
17
|
+
* [Development](#development)
|
18
|
+
* [Contributing](#contributing)
|
8
19
|
|
9
20
|
## Installation
|
10
21
|
|
@@ -42,7 +53,7 @@ class Product < ActiveRecord::Base
|
|
42
53
|
title: :string,
|
43
54
|
id_value: :value,
|
44
55
|
external_id: :integer,
|
45
|
-
reviewed_at: :
|
56
|
+
reviewed_at: :date_time
|
46
57
|
)
|
47
58
|
end
|
48
59
|
```
|
@@ -79,28 +90,40 @@ end
|
|
79
90
|
|
80
91
|
### Supported Types
|
81
92
|
|
82
|
-
The following types are supported
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
93
|
+
The following types are supported:
|
94
|
+
|
95
|
+
* big_integer
|
96
|
+
* binary
|
97
|
+
* bit
|
98
|
+
* bit_varying
|
99
|
+
* boolean
|
100
|
+
* bytea
|
101
|
+
* cidr
|
102
|
+
* date
|
103
|
+
* date_time
|
104
|
+
* decimal
|
105
|
+
* decimal_without_scale
|
106
|
+
* enum
|
107
|
+
* float
|
108
|
+
* hstore
|
109
|
+
* inet
|
110
|
+
* integer
|
111
|
+
* json
|
112
|
+
* jsonb
|
113
|
+
* money
|
114
|
+
* point
|
115
|
+
* range
|
116
|
+
* specialized_string
|
117
|
+
* string
|
118
|
+
* text
|
119
|
+
* time
|
120
|
+
* unsigned_integer
|
121
|
+
* uuid
|
122
|
+
* value
|
123
|
+
* vector
|
124
|
+
* xml
|
125
|
+
|
126
|
+
Typed arrays are also supported by specifying `:type_array` (i.e. `:float_array`). `:array` is interpreted as an array of `value` types.
|
104
127
|
|
105
128
|
Support for nested types is also available but experimental at this point. If you must, you may try something like this for nested objects.
|
106
129
|
|
@@ -123,11 +146,214 @@ Because this gem promotes attributes nested into the JSON column to first level
|
|
123
146
|
|
124
147
|
## Single-Table Inheritance
|
125
148
|
|
126
|
-
|
149
|
+
One of the big issues with `ActiveRecord` single-table inheritance (STI)
|
150
|
+
is sparse columns. Essentially, as sub-types of the original table
|
151
|
+
diverge further from their parent more columns are left empty in a given
|
152
|
+
table. Postgres' `jsonb` type provides part of the solution in that
|
153
|
+
the values in an `jsonb` column does not impose a structure - different
|
154
|
+
rows can have different values.
|
155
|
+
|
156
|
+
We set up our table with an `jsonb` field:
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
# db/migration/<timestamp>_create_players_table.rb
|
160
|
+
class CreateVehiclesTable < ActiveRecord::Migration
|
161
|
+
def change
|
162
|
+
create_table :vehicles do |t|
|
163
|
+
t.string :make
|
164
|
+
t.string :model
|
165
|
+
t.integer :model_year
|
166
|
+
t.string :type
|
167
|
+
t.jsonb :data
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
And for our models:
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
# app/models/vehicle.rb
|
177
|
+
class Vehicle < ActiveRecord::Base
|
178
|
+
end
|
179
|
+
|
180
|
+
# app/models/vehicles/automobile.rb
|
181
|
+
class Automobile < Vehicle
|
182
|
+
jsonb_accessor :data,
|
183
|
+
axle_count: :integer,
|
184
|
+
weight: :float
|
185
|
+
end
|
186
|
+
|
187
|
+
# app/models/vehicles/airplane.rb
|
188
|
+
class Airplane < Vehicle
|
189
|
+
jsonb_accessor :data,
|
190
|
+
engine_type: :string,
|
191
|
+
safety_rating: :integer
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
From here any attributes specific to any sub-class can be stored in the
|
196
|
+
`jsonb` column avoiding sparse data. Indices can also be created on
|
197
|
+
individual fields in an `jsonb` column.
|
198
|
+
|
199
|
+
This approach was originally concieved by Joe Hirn in [this blog
|
200
|
+
post](http://www.devmynd.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination).
|
127
201
|
|
128
202
|
## Scopes
|
129
203
|
|
130
|
-
|
204
|
+
JsonbAccessor currently supports several scopes. Let's say we have a class that looks like this:
|
205
|
+
|
206
|
+
```ruby
|
207
|
+
class Product < ActiveRecord::Base
|
208
|
+
jsonb_accessor :data,
|
209
|
+
approved: :boolean,
|
210
|
+
name: :string,
|
211
|
+
price: :integer,
|
212
|
+
previous_prices: :integer_array,
|
213
|
+
reviewed_at: :date_time
|
214
|
+
end
|
215
|
+
```
|
216
|
+
|
217
|
+
### General Scopes
|
218
|
+
|
219
|
+
#### `<jsonb_field>_contains`
|
220
|
+
|
221
|
+
**Description:** returns all records that contain matching attributes in the specified `jsonb` field.
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
product_1 = Product.create!(name: "foo", approved: true, reviewed_at: 3.days.ago)
|
225
|
+
product_2 = Product.create!(name: "bar", approved: true)
|
226
|
+
product_3 = Product.create!(name: "foo", approved: false)
|
227
|
+
|
228
|
+
Product.data_contains(name: "foo", approved: true) # => [product_1]
|
229
|
+
```
|
230
|
+
|
231
|
+
**Note:** when including an array attribute, the stored array and the array used for the query do not need to match exactly. For example, when queried with `[1, 2]`, records that have arrays of `[2, 1, 3]` will be returned.
|
232
|
+
|
233
|
+
#### `with_<jsonb_defined_field>`
|
234
|
+
|
235
|
+
**Description:** returns all records with the given value in the field. This is defined for all `jsonb_accessor` defined fields. It's a convenience method that allows you to do `Product.with_name("foo")` instead of `Product.data_contains(name: "foo")`.
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
product_1 = Product.create!(name: "foo")
|
239
|
+
product_2 = Product.create!(name: "bar")
|
240
|
+
|
241
|
+
Product.with_name("foo") # => [product_1]
|
242
|
+
```
|
243
|
+
|
244
|
+
**Note:** when including an array attribute, the stored array and the array used for the query do not need to match exactly. For example, when queried with `[1, 2]`, records that have arrays of `[2, 1, 3]` will be returned.
|
245
|
+
|
246
|
+
### Integer, Big Integer, Decimal, and Float Scopes
|
247
|
+
|
248
|
+
#### `<jsonb_defined_field>_gt`
|
249
|
+
|
250
|
+
**Description:** returns all records with a value that is greater than the argument.
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
product_1 = Product.create!(price: 10)
|
254
|
+
product_2 = Product.create!(price: 11)
|
255
|
+
|
256
|
+
Product.price_gt(10) # => [product_2]
|
257
|
+
```
|
258
|
+
|
259
|
+
#### `<jsonb_defined_field>_gte`
|
260
|
+
|
261
|
+
**Description:** returns all records with a value that is greater than or equal to the argument.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
product_1 = Product.create!(price: 10)
|
265
|
+
product_2 = Product.create!(price: 11)
|
266
|
+
product_3 = Product.create!(price: 9)
|
267
|
+
|
268
|
+
Product.price_gte(10) # => [product_1, product_2]
|
269
|
+
```
|
270
|
+
|
271
|
+
#### `<jsonb_defined_field>_lt`
|
272
|
+
|
273
|
+
**Description:** returns all records with a value that is less than the argument.
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
product_1 = Product.create!(price: 10)
|
277
|
+
product_2 = Product.create!(price: 11)
|
278
|
+
|
279
|
+
Product.price_lt(11) # => [product_1]
|
280
|
+
```
|
281
|
+
|
282
|
+
#### `<jsonb_defined_field>_lte`
|
283
|
+
|
284
|
+
**Description:** returns all records with a value that is less than or equal to the argument.
|
285
|
+
|
286
|
+
```ruby
|
287
|
+
product_1 = Product.create!(price: 10)
|
288
|
+
product_2 = Product.create!(price: 11)
|
289
|
+
product_3 = Product.create!(price: 12)
|
290
|
+
|
291
|
+
Product.price_lte(11) # => [product_1, product_2]
|
292
|
+
```
|
293
|
+
|
294
|
+
|
295
|
+
### Boolean Scopes
|
296
|
+
|
297
|
+
#### `is_<jsonb_defined_field>`
|
298
|
+
|
299
|
+
**Description:** returns all records where the value is `true`.
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
product_1 = Product.create!(approved: true)
|
303
|
+
product_2 = Product.create!(approved: false)
|
304
|
+
|
305
|
+
Product.is_approved # => [product_1]
|
306
|
+
```
|
307
|
+
|
308
|
+
#### `not_<jsonb_defined_field>`
|
309
|
+
|
310
|
+
**Description:** returns all records where the value is `false`.
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
product_1 = Product.create!(approved: true)
|
314
|
+
product_2 = Product.create!(approved: false)
|
315
|
+
|
316
|
+
Product.not_approved # => [product_2]
|
317
|
+
```
|
318
|
+
|
319
|
+
### Date, DateTime Scopes
|
320
|
+
|
321
|
+
#### `<jsonb_defined_field>_before`
|
322
|
+
|
323
|
+
**Description:** returns all records where the value is before the argument. Also supports JSON string arguments.
|
324
|
+
|
325
|
+
```ruby
|
326
|
+
product_1 = Product.create!(reviewed_at: 3.days.ago)
|
327
|
+
product_2 = Product.create!(reviewed_at: 5.days.ago)
|
328
|
+
|
329
|
+
Product.reviewed_at_before(4.days.ago) # => [product_2]
|
330
|
+
Product.reviewed_at_before(4.days.ago.to_json) # => [product_2]
|
331
|
+
```
|
332
|
+
|
333
|
+
#### `<jsonb_defined_field>_after`
|
334
|
+
|
335
|
+
**Description:** returns all records where the value is after the argument. Also supports JSON string arguments.
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
product_1 = Product.create!(reviewed_at: 3.days.from_now)
|
339
|
+
product_2 = Product.create!(reviewed_at: 5.days.from_now)
|
340
|
+
|
341
|
+
Product.reviewed_at_after(4.days.from_now) # => [product_2]
|
342
|
+
Product.reviewed_at_after(4.days.from_now.to_json) # => [product_2]
|
343
|
+
```
|
344
|
+
|
345
|
+
### Array Scopes
|
346
|
+
|
347
|
+
#### `<jsonb_defined_fields>_contains`
|
348
|
+
|
349
|
+
**Description:** returns all records where the value is contained in the array field.
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
product_1 = Product.create!(previous_prices: [3])
|
353
|
+
product_2 = Product.create!(previous_prices: [4, 5, 6])
|
354
|
+
|
355
|
+
Product.previous_prices_contains(5) # => [product_2]
|
356
|
+
```
|
131
357
|
|
132
358
|
## Migrations
|
133
359
|
|
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
jsonb_accessor (0.
|
4
|
+
jsonb_accessor (0.3.0)
|
5
5
|
activerecord (>= 4.2.1)
|
6
6
|
pg
|
7
7
|
|
@@ -57,10 +57,10 @@ GEM
|
|
57
57
|
minitest (5.5.1)
|
58
58
|
nokogiri (1.6.6.2)
|
59
59
|
mini_portile (~> 0.6.0)
|
60
|
-
parser (2.2.
|
60
|
+
parser (2.2.2.5)
|
61
61
|
ast (>= 1.1, < 3.0)
|
62
62
|
pg (0.18.1)
|
63
|
-
powerpack (0.1.
|
63
|
+
powerpack (0.1.1)
|
64
64
|
pry (0.10.1)
|
65
65
|
coderay (~> 1.1.0)
|
66
66
|
method_source (~> 0.8.1)
|
@@ -101,9 +101,9 @@ GEM
|
|
101
101
|
diff-lcs (>= 1.2.0, < 2.0)
|
102
102
|
rspec-support (~> 3.2.0)
|
103
103
|
rspec-support (3.2.2)
|
104
|
-
rubocop (0.
|
104
|
+
rubocop (0.31.0)
|
105
105
|
astrolabe (~> 1.3)
|
106
|
-
parser (>= 2.2.
|
106
|
+
parser (>= 2.2.2.1, < 3.0)
|
107
107
|
powerpack (~> 0.1)
|
108
108
|
rainbow (>= 1.99.1, < 3.0)
|
109
109
|
ruby-progressbar (~> 1.4)
|
@@ -125,6 +125,7 @@ PLATFORMS
|
|
125
125
|
ruby
|
126
126
|
|
127
127
|
DEPENDENCIES
|
128
|
+
actionpack (~> 4.2.1)
|
128
129
|
activerecord (= 4.2.1)
|
129
130
|
appraisal
|
130
131
|
awesome_print
|
@@ -136,6 +137,6 @@ DEPENDENCIES
|
|
136
137
|
pry-nav
|
137
138
|
rake (~> 10.0)
|
138
139
|
rspec (~> 3.2.0)
|
139
|
-
rubocop
|
140
|
+
rubocop (= 0.31.0)
|
140
141
|
shoulda-matchers
|
141
142
|
standalone_migrations
|
@@ -0,0 +1,143 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
jsonb_accessor (0.3.0)
|
5
|
+
activerecord (>= 4.2.1)
|
6
|
+
pg
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
actionpack (4.2.2)
|
12
|
+
actionview (= 4.2.2)
|
13
|
+
activesupport (= 4.2.2)
|
14
|
+
rack (~> 1.6)
|
15
|
+
rack-test (~> 0.6.2)
|
16
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
17
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
18
|
+
actionview (4.2.2)
|
19
|
+
activesupport (= 4.2.2)
|
20
|
+
builder (~> 3.1)
|
21
|
+
erubis (~> 2.7.0)
|
22
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
23
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.1)
|
24
|
+
activemodel (4.2.2)
|
25
|
+
activesupport (= 4.2.2)
|
26
|
+
builder (~> 3.1)
|
27
|
+
activerecord (4.2.2)
|
28
|
+
activemodel (= 4.2.2)
|
29
|
+
activesupport (= 4.2.2)
|
30
|
+
arel (~> 6.0)
|
31
|
+
activesupport (4.2.2)
|
32
|
+
i18n (~> 0.7)
|
33
|
+
json (~> 1.7, >= 1.7.7)
|
34
|
+
minitest (~> 5.1)
|
35
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
36
|
+
tzinfo (~> 1.1)
|
37
|
+
appraisal (2.0.1)
|
38
|
+
activesupport (>= 3.2.21)
|
39
|
+
bundler
|
40
|
+
rake
|
41
|
+
thor (>= 0.14.0)
|
42
|
+
arel (6.0.0)
|
43
|
+
ast (2.0.0)
|
44
|
+
astrolabe (1.3.0)
|
45
|
+
parser (>= 2.2.0.pre.3, < 3.0)
|
46
|
+
awesome_print (1.6.1)
|
47
|
+
builder (3.2.2)
|
48
|
+
coderay (1.1.0)
|
49
|
+
database_cleaner (1.4.1)
|
50
|
+
diff-lcs (1.2.5)
|
51
|
+
erubis (2.7.0)
|
52
|
+
i18n (0.7.0)
|
53
|
+
json (1.8.3)
|
54
|
+
loofah (2.0.2)
|
55
|
+
nokogiri (>= 1.5.9)
|
56
|
+
method_source (0.8.2)
|
57
|
+
mini_portile (0.6.2)
|
58
|
+
minitest (5.7.0)
|
59
|
+
nokogiri (1.6.6.2)
|
60
|
+
mini_portile (~> 0.6.0)
|
61
|
+
parser (2.3.0.pre.2)
|
62
|
+
ast (>= 1.1, < 3.0)
|
63
|
+
pg (0.18.2)
|
64
|
+
powerpack (0.1.1)
|
65
|
+
pry (0.10.1)
|
66
|
+
coderay (~> 1.1.0)
|
67
|
+
method_source (~> 0.8.1)
|
68
|
+
slop (~> 3.4)
|
69
|
+
pry-doc (0.8.0)
|
70
|
+
pry (~> 0.9)
|
71
|
+
yard (~> 0.8)
|
72
|
+
pry-nav (0.2.4)
|
73
|
+
pry (>= 0.9.10, < 0.11.0)
|
74
|
+
rack (1.6.2)
|
75
|
+
rack-test (0.6.3)
|
76
|
+
rack (>= 1.0)
|
77
|
+
rails-deprecated_sanitizer (1.0.3)
|
78
|
+
activesupport (>= 4.2.0.alpha)
|
79
|
+
rails-dom-testing (1.0.6)
|
80
|
+
activesupport (>= 4.2.0.beta, < 5.0)
|
81
|
+
nokogiri (~> 1.6.0)
|
82
|
+
rails-deprecated_sanitizer (>= 1.0.1)
|
83
|
+
rails-html-sanitizer (1.0.2)
|
84
|
+
loofah (~> 2.0)
|
85
|
+
railties (4.2.2)
|
86
|
+
actionpack (= 4.2.2)
|
87
|
+
activesupport (= 4.2.2)
|
88
|
+
rake (>= 0.8.7)
|
89
|
+
thor (>= 0.18.1, < 2.0)
|
90
|
+
rainbow (2.0.0)
|
91
|
+
rake (10.4.2)
|
92
|
+
rspec (3.2.0)
|
93
|
+
rspec-core (~> 3.2.0)
|
94
|
+
rspec-expectations (~> 3.2.0)
|
95
|
+
rspec-mocks (~> 3.2.0)
|
96
|
+
rspec-core (3.2.3)
|
97
|
+
rspec-support (~> 3.2.0)
|
98
|
+
rspec-expectations (3.2.1)
|
99
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
100
|
+
rspec-support (~> 3.2.0)
|
101
|
+
rspec-mocks (3.2.1)
|
102
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
103
|
+
rspec-support (~> 3.2.0)
|
104
|
+
rspec-support (3.2.2)
|
105
|
+
rubocop (0.31.0)
|
106
|
+
astrolabe (~> 1.3)
|
107
|
+
parser (>= 2.2.2.1, < 3.0)
|
108
|
+
powerpack (~> 0.1)
|
109
|
+
rainbow (>= 1.99.1, < 3.0)
|
110
|
+
ruby-progressbar (~> 1.4)
|
111
|
+
ruby-progressbar (1.7.5)
|
112
|
+
shoulda-matchers (2.8.0)
|
113
|
+
activesupport (>= 3.0.0)
|
114
|
+
slop (3.6.0)
|
115
|
+
standalone_migrations (4.0.2)
|
116
|
+
activerecord (~> 4.2.0)
|
117
|
+
railties (~> 4.2.0)
|
118
|
+
rake (~> 10.0)
|
119
|
+
thor (0.19.1)
|
120
|
+
thread_safe (0.3.5)
|
121
|
+
tzinfo (1.2.2)
|
122
|
+
thread_safe (~> 0.1)
|
123
|
+
yard (0.8.7.6)
|
124
|
+
|
125
|
+
PLATFORMS
|
126
|
+
ruby
|
127
|
+
|
128
|
+
DEPENDENCIES
|
129
|
+
actionpack (~> 4.2.1)
|
130
|
+
activerecord (= 4.2.2)
|
131
|
+
appraisal
|
132
|
+
awesome_print
|
133
|
+
bundler (~> 1.9)
|
134
|
+
database_cleaner
|
135
|
+
jsonb_accessor!
|
136
|
+
pry
|
137
|
+
pry-doc
|
138
|
+
pry-nav
|
139
|
+
rake (~> 10.0)
|
140
|
+
rspec (~> 3.2.0)
|
141
|
+
rubocop (= 0.31.0)
|
142
|
+
shoulda-matchers
|
143
|
+
standalone_migrations
|
data/jsonb_accessor.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_dependency "activerecord", ">= 4.2.1"
|
23
23
|
spec.add_dependency "pg"
|
24
24
|
|
25
|
+
spec.add_development_dependency "actionpack", "~> 4.2.1"
|
25
26
|
spec.add_development_dependency "appraisal"
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.9"
|
27
28
|
spec.add_development_dependency "database_cleaner"
|
@@ -31,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
31
32
|
spec.add_development_dependency "pry-nav"
|
32
33
|
spec.add_development_dependency "rake", "~> 10.0"
|
33
34
|
spec.add_development_dependency "rspec", "~> 3.2.0"
|
34
|
-
spec.add_development_dependency "rubocop"
|
35
|
+
spec.add_development_dependency "rubocop", "0.31.0"
|
35
36
|
spec.add_development_dependency "shoulda-matchers"
|
36
37
|
spec.add_development_dependency "standalone_migrations"
|
37
38
|
end
|
data/lib/jsonb_accessor.rb
CHANGED
@@ -3,6 +3,8 @@ require "active_record"
|
|
3
3
|
require "active_record/connection_adapters/postgresql_adapter"
|
4
4
|
|
5
5
|
require "jsonb_accessor/version"
|
6
|
+
require "jsonb_accessor/fields_map"
|
7
|
+
require "jsonb_accessor/helpers"
|
6
8
|
require "jsonb_accessor/type_helper"
|
7
9
|
require "jsonb_accessor/nested_base"
|
8
10
|
require "jsonb_accessor/class_builder"
|
@@ -15,4 +17,5 @@ end
|
|
15
17
|
|
16
18
|
ActiveSupport.on_load(:active_record) do
|
17
19
|
ActiveRecord::Base.send(:include, JsonbAccessor)
|
20
|
+
ActiveRecord::Base.send(:include, JsonbAccessor::Helpers)
|
18
21
|
end
|
@@ -1,76 +1,100 @@
|
|
1
1
|
module JsonbAccessor
|
2
2
|
UnknownValue = Class.new(StandardError)
|
3
|
+
CLASS_PREFIX = "JA"
|
3
4
|
|
4
5
|
module ClassBuilder
|
5
6
|
class << self
|
6
7
|
def generate_class(namespace, new_class_name, attribute_definitions)
|
7
|
-
|
8
|
+
fields_map = JsonbAccessor::FieldsMap.new([], attribute_definitions)
|
9
|
+
klass = generate_new_class(new_class_name, fields_map, namespace)
|
10
|
+
nested_classes = generate_nested_classes(klass, fields_map.nested_fields)
|
8
11
|
|
9
|
-
klass
|
10
|
-
|
11
|
-
|
12
|
+
define_class_methods(klass, nested_classes, new_class_name)
|
13
|
+
define_attributes_and_data_types(klass, fields_map)
|
14
|
+
define_typed_accessors(klass, fields_map)
|
15
|
+
define_nested_accessors(klass, fields_map)
|
12
16
|
|
13
|
-
klass
|
14
|
-
|
15
|
-
singleton_class.send(:define_method, :attribute_on_parent_name) { new_class_name }
|
17
|
+
klass
|
18
|
+
end
|
16
19
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def generate_nested_classes(klass, nested_attributes)
|
21
|
+
nested_attributes.each_with_object({}) do |(attribute_name, nested_attrs), nested_classes|
|
22
|
+
nested_classes[attribute_name] = generate_class(klass, attribute_name, nested_attrs)
|
23
|
+
end
|
24
|
+
end
|
22
25
|
|
23
|
-
|
24
|
-
|
26
|
+
def generate_class_namespace(name)
|
27
|
+
class_name = CLASS_PREFIX + name.gsub(CONSTANT_SEPARATOR, "")
|
28
|
+
if JsonbAccessor.constants.any? { |c| c.to_s == class_name }
|
29
|
+
class_namespace = JsonbAccessor.const_get(class_name)
|
30
|
+
else
|
31
|
+
class_namespace = Module.new
|
32
|
+
JsonbAccessor.const_set(class_name, class_namespace)
|
33
|
+
end
|
34
|
+
class_namespace
|
35
|
+
end
|
25
36
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
37
|
+
def generate_attribute_namespace(attribute_name, class_namespace)
|
38
|
+
attribute_namespace = Module.new
|
39
|
+
name = generate_constant_name(attribute_name)
|
40
|
+
class_namespace.const_set(name, attribute_namespace)
|
41
|
+
end
|
32
42
|
|
33
|
-
|
43
|
+
private
|
44
|
+
|
45
|
+
def define_nested_accessors(klass, fields_map)
|
46
|
+
klass.class_eval do
|
47
|
+
fields_map.nested_fields.keys.each do |attribute_name|
|
34
48
|
attr_reader attribute_name
|
35
49
|
|
36
50
|
define_method("#{attribute_name}=") do |value|
|
37
51
|
instance_class = nested_classes[attribute_name]
|
52
|
+
instance = cast_nested_field_value(value, instance_class, __method__)
|
38
53
|
|
39
|
-
case value
|
40
|
-
when instance_class
|
41
|
-
instance = instance_class.new(value.attributes)
|
42
|
-
when Hash
|
43
|
-
instance = instance_class.new(value)
|
44
|
-
when nil
|
45
|
-
instance = instance_class.new
|
46
|
-
else
|
47
|
-
raise UnknownValue, "unable to set value '#{value}' is not a hash, `nil`, or an instance of #{instance_class} in #{__method__}"
|
48
|
-
end
|
49
|
-
|
50
|
-
instance.parent = self
|
51
54
|
instance_variable_set("@#{attribute_name}", instance)
|
52
55
|
attributes[attribute_name] = instance.attributes
|
53
56
|
update_parent
|
54
57
|
end
|
55
58
|
end
|
56
59
|
end
|
57
|
-
klass
|
58
60
|
end
|
59
61
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
62
|
+
def define_typed_accessors(klass, fields_map)
|
63
|
+
klass.class_eval do
|
64
|
+
fields_map.typed_fields.keys.each do |attribute_name|
|
65
|
+
define_method(attribute_name) { attributes[attribute_name] }
|
66
|
+
|
67
|
+
define_method("#{attribute_name}=") do |value|
|
68
|
+
cast_value = attributes_and_data_types[attribute_name].type_cast_from_user(value)
|
69
|
+
attributes[attribute_name] = cast_value
|
70
|
+
update_parent
|
71
|
+
end
|
72
|
+
end
|
63
73
|
end
|
64
74
|
end
|
65
75
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
group[name] = type_or_nested
|
76
|
+
def define_attributes_and_data_types(klass, fields_map)
|
77
|
+
klass.send(:define_method, :attributes_and_data_types) do
|
78
|
+
@attributes_and_data_types ||= fields_map.typed_fields.each_with_object({}) do |(name, type), attrs_and_data_types|
|
79
|
+
attrs_and_data_types[name] = TypeHelper.fetch(type)
|
80
|
+
end
|
72
81
|
end
|
73
82
|
end
|
83
|
+
|
84
|
+
def define_class_methods(klass, nested_classes, attribute_name)
|
85
|
+
klass.singleton_class.send(:define_method, :nested_classes) { nested_classes }
|
86
|
+
klass.singleton_class.send(:define_method, :attribute_on_parent_name) { attribute_name }
|
87
|
+
end
|
88
|
+
|
89
|
+
def generate_new_class(new_class_name, fields_map, namespace)
|
90
|
+
klass = Class.new(NestedBase)
|
91
|
+
new_class_name_camelized = generate_constant_name(new_class_name)
|
92
|
+
namespace.const_set(new_class_name_camelized, klass)
|
93
|
+
end
|
94
|
+
|
95
|
+
def generate_constant_name(attribute_name)
|
96
|
+
"#{CLASS_PREFIX}#{attribute_name.to_s.camelize}"
|
97
|
+
end
|
74
98
|
end
|
75
99
|
end
|
76
100
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JsonbAccessor
|
2
|
+
class FieldsMap
|
3
|
+
attr_accessor :nested_fields, :typed_fields
|
4
|
+
|
5
|
+
def initialize(value_fields, typed_and_nested_fields)
|
6
|
+
grouped_fields = extract_typed_and_nested_fields(typed_and_nested_fields)
|
7
|
+
nested_fields, typed_fields = grouped_fields.values_at(:nested, :typed)
|
8
|
+
|
9
|
+
self.typed_fields = implicitly_typed_fields(value_fields).merge(typed_fields)
|
10
|
+
self.nested_fields = nested_fields
|
11
|
+
end
|
12
|
+
|
13
|
+
def names
|
14
|
+
typed_fields.keys + nested_fields.keys
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def implicitly_typed_fields(value_fields)
|
20
|
+
value_fields.each_with_object({}) do |field_name, implicitly_typed_fields|
|
21
|
+
implicitly_typed_fields[field_name] = :value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def extract_typed_and_nested_fields(typed_and_nested_fields)
|
26
|
+
typed_and_nested_fields.each_with_object(nested: {}, typed: {}) do |(attribute_name, type_or_nested), grouped_attributes|
|
27
|
+
group = type_or_nested.is_a?(Hash) ? grouped_attributes[:nested] : grouped_attributes[:typed]
|
28
|
+
group[attribute_name] = type_or_nested
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module JsonbAccessor
|
2
|
+
module Helpers
|
3
|
+
def cast_nested_field_value(value, klass, method_name)
|
4
|
+
case value
|
5
|
+
when klass
|
6
|
+
instance = klass.new(value.attributes)
|
7
|
+
when Hash
|
8
|
+
instance = klass.new(value)
|
9
|
+
when nil
|
10
|
+
instance = klass.new
|
11
|
+
else
|
12
|
+
raise UnknownValue, "unable to set value '#{value}' is not a hash, `nil`, or an instance of #{klass} in #{method_name}"
|
13
|
+
end
|
14
|
+
|
15
|
+
instance.parent = self
|
16
|
+
instance
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/jsonb_accessor/macro.rb
CHANGED
@@ -1,61 +1,112 @@
|
|
1
1
|
module JsonbAccessor
|
2
2
|
module Macro
|
3
|
-
|
4
|
-
def
|
5
|
-
|
3
|
+
module ClassMethods
|
4
|
+
def jsonb_accessor(jsonb_attribute, *value_fields, **typed_fields)
|
5
|
+
fields_map = JsonbAccessor::FieldsMap.new(value_fields, typed_fields)
|
6
|
+
class_namespace = ClassBuilder.generate_class_namespace(name)
|
7
|
+
attribute_namespace = ClassBuilder.generate_attribute_namespace(jsonb_attribute, class_namespace)
|
8
|
+
nested_classes = ClassBuilder.generate_nested_classes(attribute_namespace, fields_map.nested_fields)
|
9
|
+
jsonb_attribute_initialization_method_name = "initialize_jsonb_attrs_for_#{jsonb_attribute}"
|
10
|
+
jsonb_attribute_scope_name = "#{jsonb_attribute}_contains"
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
group[attribute_name] = type_or_nested
|
12
|
+
singleton_class.send(:define_method, "#{jsonb_attribute}_classes") do
|
13
|
+
nested_classes
|
10
14
|
end
|
15
|
+
|
16
|
+
delegate "#{jsonb_attribute}_classes", to: :class
|
17
|
+
|
18
|
+
_initialize_jsonb_attrs(jsonb_attribute, fields_map, jsonb_attribute_initialization_method_name)
|
19
|
+
_create_jsonb_attribute_scope_name(jsonb_attribute, jsonb_attribute_scope_name)
|
20
|
+
_create_jsonb_scopes(jsonb_attribute, fields_map, jsonb_attribute_scope_name)
|
21
|
+
_create_jsonb_accessor_methods(jsonb_attribute, jsonb_attribute_initialization_method_name, fields_map)
|
22
|
+
|
23
|
+
_register_jsonb_classes_for_cleanup
|
11
24
|
end
|
12
25
|
|
13
|
-
|
14
|
-
|
15
|
-
|
26
|
+
private
|
27
|
+
|
28
|
+
def _register_jsonb_classes_for_cleanup
|
29
|
+
if defined?(ActionDispatch) && ENV["RACK_ENV"] == "development"
|
30
|
+
class_name = CLASS_PREFIX + name
|
31
|
+
ActionDispatch::Reloader.to_cleanup do
|
32
|
+
if JsonbAccessor.constants.any? { |c| c.to_s == class_name }
|
33
|
+
JsonbAccessor.send(:remove_const, class_name)
|
34
|
+
end
|
35
|
+
end
|
16
36
|
end
|
17
37
|
end
|
18
38
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
JsonbAccessor.const_set(class_name, class_namespace)
|
39
|
+
def _initialize_jsonb_attrs(jsonb_attribute, fields_map, jsonb_attribute_initialization_method_name)
|
40
|
+
define_method(jsonb_attribute_initialization_method_name) do
|
41
|
+
jsonb_attribute_hash = send(jsonb_attribute) || {}
|
42
|
+
fields_map.names.each do |field|
|
43
|
+
send("#{field}=", jsonb_attribute_hash[field.to_s])
|
44
|
+
end
|
26
45
|
end
|
27
|
-
|
46
|
+
after_initialize(jsonb_attribute_initialization_method_name)
|
28
47
|
end
|
29
|
-
end
|
30
48
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
49
|
+
def _create_jsonb_attribute_scope_name(jsonb_attribute, jsonb_attribute_scope_name)
|
50
|
+
scope jsonb_attribute_scope_name, (lambda do |attributes|
|
51
|
+
query_options = new(attributes).send(jsonb_attribute)
|
52
|
+
fields = attributes.keys.map(&:to_s)
|
53
|
+
query_options.delete_if { |key, value| fields.exclude?(key) }
|
54
|
+
query_json = TypeHelper.type_cast_as_jsonb(query_options)
|
55
|
+
where("#{table_name}.#{jsonb_attribute} @> ?", query_json)
|
56
|
+
end)
|
57
|
+
end
|
35
58
|
|
36
|
-
|
37
|
-
|
38
|
-
|
59
|
+
def _create_jsonb_scopes(jsonb_attribute, fields_map, jsonb_attribute_scope_name)
|
60
|
+
__create_jsonb_standard_scopes(fields_map, jsonb_attribute_scope_name)
|
61
|
+
__create_jsonb_typed_scopes(jsonb_attribute, fields_map)
|
62
|
+
end
|
39
63
|
|
40
|
-
|
64
|
+
def __create_jsonb_standard_scopes(fields_map, jsonb_attribute_scope_name)
|
65
|
+
fields_map.names.each do |field|
|
66
|
+
scope "with_#{field}", -> (value) { send(jsonb_attribute_scope_name, field => value) }
|
67
|
+
end
|
68
|
+
end
|
41
69
|
|
42
|
-
|
43
|
-
|
70
|
+
def __create_jsonb_typed_scopes(jsonb_attribute, fields_map)
|
71
|
+
fields_map.typed_fields.each do |field, type|
|
72
|
+
case type
|
73
|
+
when :boolean
|
74
|
+
___create_jsonb_boolean_scopes(field)
|
75
|
+
when :integer, :float, :decimal, :big_integer
|
76
|
+
___create_jsonb_numeric_scopes(field, jsonb_attribute, type)
|
77
|
+
when :date_time, :date
|
78
|
+
___create_jsonb_date_time_scopes(field, jsonb_attribute, type)
|
79
|
+
when /array/
|
80
|
+
___create_jsonb_array_scopes(field)
|
81
|
+
end
|
44
82
|
end
|
83
|
+
end
|
45
84
|
|
46
|
-
|
85
|
+
def ___create_jsonb_boolean_scopes(field)
|
86
|
+
scope "is_#{field}", -> { send("with_#{field}", true) }
|
87
|
+
scope "not_#{field}", -> { send("with_#{field}", false) }
|
88
|
+
end
|
47
89
|
|
48
|
-
|
90
|
+
def ___create_jsonb_numeric_scopes(field, jsonb_attribute, type)
|
91
|
+
safe_type = type.to_s.gsub("big_", "")
|
92
|
+
scope "__numeric_#{field}_comparator", -> (value, operator) { where("((#{table_name}.#{jsonb_attribute}) ->> ?)::#{safe_type} #{operator} ?", field, value) }
|
93
|
+
scope "#{field}_lt", -> (value) { send("__numeric_#{field}_comparator", value, "<") }
|
94
|
+
scope "#{field}_lte", -> (value) { send("__numeric_#{field}_comparator", value, "<=") }
|
95
|
+
scope "#{field}_gte", -> (value) { send("__numeric_#{field}_comparator", value, ">=") }
|
96
|
+
scope "#{field}_gt", -> (value) { send("__numeric_#{field}_comparator", value, ">") }
|
97
|
+
end
|
49
98
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
99
|
+
def ___create_jsonb_date_time_scopes(field, jsonb_attribute, type)
|
100
|
+
scope "__date_time_#{field}_comparator", -> (value, operator) { where("((#{table_name}.#{jsonb_attribute}) ->> ?)::timestamp #{operator} ?::timestamp", field, value.to_json) }
|
101
|
+
scope "#{field}_before", -> (value) { send("__date_time_#{field}_comparator", value, "<") }
|
102
|
+
scope "#{field}_after", -> (value) { send("__date_time_#{field}_comparator", value, ">") }
|
103
|
+
end
|
56
104
|
|
57
|
-
|
105
|
+
def ___create_jsonb_array_scopes(field)
|
106
|
+
scope "#{field}_contains", -> (value) { send("with_#{field}", [value]) }
|
107
|
+
end
|
58
108
|
|
109
|
+
def _create_jsonb_accessor_methods(jsonb_attribute, jsonb_attribute_initialization_method_name, fields_map)
|
59
110
|
jsonb_accessor_methods = Module.new do
|
60
111
|
define_method("#{jsonb_attribute}=") do |value|
|
61
112
|
write_attribute(jsonb_attribute, value)
|
@@ -69,7 +120,13 @@ module JsonbAccessor
|
|
69
120
|
end
|
70
121
|
end
|
71
122
|
|
72
|
-
|
123
|
+
__create_jsonb_typed_field_setters(jsonb_attribute, jsonb_accessor_methods, fields_map)
|
124
|
+
__create_jsonb_nested_field_accessors(jsonb_attribute, jsonb_accessor_methods, fields_map)
|
125
|
+
include jsonb_accessor_methods
|
126
|
+
end
|
127
|
+
|
128
|
+
def __create_jsonb_typed_field_setters(jsonb_attribute, jsonb_accessor_methods, fields_map)
|
129
|
+
fields_map.typed_fields.each do |field, type|
|
73
130
|
attribute(field.to_s, TypeHelper.fetch(type))
|
74
131
|
|
75
132
|
jsonb_accessor_methods.instance_eval do
|
@@ -80,34 +137,22 @@ module JsonbAccessor
|
|
80
137
|
end
|
81
138
|
end
|
82
139
|
end
|
140
|
+
end
|
83
141
|
|
84
|
-
|
142
|
+
def __create_jsonb_nested_field_accessors(jsonb_attribute, jsonb_accessor_methods, fields_map)
|
143
|
+
fields_map.nested_fields.each do |field, nested_attributes|
|
85
144
|
attribute(field.to_s, TypeHelper.fetch(:value))
|
86
|
-
|
87
145
|
jsonb_accessor_methods.instance_eval do
|
88
146
|
define_method("#{field}=") do |value|
|
89
|
-
instance_class =
|
90
|
-
|
91
|
-
|
92
|
-
when instance_class
|
93
|
-
instance = instance_class.new(value.attributes)
|
94
|
-
when Hash
|
95
|
-
instance = instance_class.new(value)
|
96
|
-
when nil
|
97
|
-
instance = instance_class.new
|
98
|
-
else
|
99
|
-
raise UnknownValue, "unable to set value '#{value}' is not a hash, `nil`, or an instance of #{instance_class} in #{__method__}"
|
100
|
-
end
|
101
|
-
|
102
|
-
instance.parent = self
|
147
|
+
instance_class = send("#{jsonb_attribute}_classes")[field]
|
148
|
+
instance = cast_nested_field_value(value, instance_class, __method__)
|
149
|
+
|
103
150
|
new_jsonb_value = (send(jsonb_attribute) || {}).merge(field.to_s => instance.attributes)
|
104
151
|
write_attribute(jsonb_attribute, new_jsonb_value)
|
105
152
|
super(instance)
|
106
153
|
end
|
107
154
|
end
|
108
155
|
end
|
109
|
-
|
110
|
-
include jsonb_accessor_methods
|
111
156
|
end
|
112
157
|
end
|
113
158
|
end
|
@@ -18,8 +18,17 @@ module JsonbAccessor
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
def type_cast_as_jsonb(suspect)
|
22
|
+
type_cast_hash = jsonb.type_cast_from_user(suspect)
|
23
|
+
jsonb.type_cast_for_database(type_cast_hash)
|
24
|
+
end
|
25
|
+
|
21
26
|
private
|
22
27
|
|
28
|
+
def jsonb
|
29
|
+
@jsonb ||= fetch(:jsonb)
|
30
|
+
end
|
31
|
+
|
23
32
|
def fetch_active_record_array_type(type)
|
24
33
|
subtype = type.to_s.sub(ARRAY_MATCHER, "")
|
25
34
|
new_array(fetch_active_record_type(subtype))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonb_accessor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Crismali
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-06-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: actionpack
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 4.2.1
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 4.2.1
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: appraisal
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -169,16 +183,16 @@ dependencies:
|
|
169
183
|
name: rubocop
|
170
184
|
requirement: !ruby/object:Gem::Requirement
|
171
185
|
requirements:
|
172
|
-
- -
|
186
|
+
- - '='
|
173
187
|
- !ruby/object:Gem::Version
|
174
|
-
version:
|
188
|
+
version: 0.31.0
|
175
189
|
type: :development
|
176
190
|
prerelease: false
|
177
191
|
version_requirements: !ruby/object:Gem::Requirement
|
178
192
|
requirements:
|
179
|
-
- -
|
193
|
+
- - '='
|
180
194
|
- !ruby/object:Gem::Version
|
181
|
-
version:
|
195
|
+
version: 0.31.0
|
182
196
|
- !ruby/object:Gem::Dependency
|
183
197
|
name: shoulda-matchers
|
184
198
|
requirement: !ruby/object:Gem::Requirement
|
@@ -234,9 +248,13 @@ files:
|
|
234
248
|
- docker-compose.yml
|
235
249
|
- gemfiles/activerecord_4.2.1.gemfile
|
236
250
|
- gemfiles/activerecord_4.2.1.gemfile.lock
|
251
|
+
- gemfiles/activerecord_4.2.2.gemfile
|
252
|
+
- gemfiles/activerecord_4.2.2.gemfile.lock
|
237
253
|
- jsonb_accessor.gemspec
|
238
254
|
- lib/jsonb_accessor.rb
|
239
255
|
- lib/jsonb_accessor/class_builder.rb
|
256
|
+
- lib/jsonb_accessor/fields_map.rb
|
257
|
+
- lib/jsonb_accessor/helpers.rb
|
240
258
|
- lib/jsonb_accessor/macro.rb
|
241
259
|
- lib/jsonb_accessor/nested_base.rb
|
242
260
|
- lib/jsonb_accessor/type_helper.rb
|