jsonb_accessor 1.0.0.beta.4 → 1.1.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/.gitignore +2 -0
- data/.rubocop.yml +3 -1
- data/.ruby-version +1 -1
- data/.travis.yml +5 -2
- data/Appraisals +6 -1
- data/Gemfile +4 -2
- data/README.md +35 -10
- data/Rakefile +2 -1
- data/bin/console +1 -0
- data/db/schema.rb +12 -12
- data/gemfiles/activerecord_5.0.0.gemfile +1 -1
- data/gemfiles/activerecord_5.1.0.gemfile +8 -0
- data/jsonb_accessor.gemspec +14 -12
- data/lib/jsonb_accessor.rb +2 -0
- data/lib/jsonb_accessor/macro.rb +35 -10
- data/lib/jsonb_accessor/query_builder.rb +24 -92
- data/lib/jsonb_accessor/query_helper.rb +114 -0
- data/lib/jsonb_accessor/version.rb +2 -1
- metadata +44 -43
- data/gemfiles/activerecord_5.0.0.gemfile.lock +0 -140
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4572af0f905157b8a8540b7c4fac70426908926c
|
4
|
+
data.tar.gz: 7fc82fc4c45831589b5de1d041cab1e651783447
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb272c8509fce56497c5863feb5879428cab9d90e42243c791caf14928e11a40f1a3c07af10574028be601874f49a26560354c85d4f561b5fb22d9258e79e944
|
7
|
+
data.tar.gz: c053957f581dbcb37aa77c8c7cdce51193ec073927abaa43832e9ad4330a8d8d60de45ab671cf4a48dacf24a4dddbc576b743abc02c6019979bd5901257f4556
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -5,7 +5,7 @@ AllCops:
|
|
5
5
|
- db/**/*
|
6
6
|
- gemfiles/**/*
|
7
7
|
- vendor/**/*
|
8
|
-
|
8
|
+
Style/SpaceBeforeFirstArg:
|
9
9
|
Enabled: false
|
10
10
|
Lint/UnusedBlockArgument:
|
11
11
|
Enabled: false
|
@@ -59,3 +59,5 @@ Style/SpaceAroundEqualsInParameterDefault:
|
|
59
59
|
Enabled: false
|
60
60
|
Style/StringLiterals:
|
61
61
|
EnforcedStyle: double_quotes
|
62
|
+
Performance/EndWith:
|
63
|
+
Enabled: false
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.1
|
data/.travis.yml
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.
|
3
|
+
- 2.3.8
|
4
|
+
- 2.4.5
|
5
|
+
- 2.5.3
|
6
|
+
- 2.6.1
|
5
7
|
addons:
|
6
8
|
postgresql: '9.4'
|
7
9
|
before_install:
|
@@ -14,3 +16,4 @@ before_script:
|
|
14
16
|
cache: bundler
|
15
17
|
gemfile:
|
16
18
|
- gemfiles/activerecord_5.0.0.gemfile
|
19
|
+
- gemfiles/activerecord_5.1.0.gemfile
|
data/Appraisals
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,17 +1,13 @@
|
|
1
1
|
# JSONb Accessor
|
2
2
|
|
3
|
-
Created by [<img src="https://raw.githubusercontent.com/
|
3
|
+
Created by [<img src="https://raw.githubusercontent.com/madeintandem/jsonb_accessor/master/tandem-logo.png" alt="Tandem Logo" />](https://www.madeintandem.com/)
|
4
4
|
|
5
|
-
[](http://badge.fury.io/rb/jsonb_accessor) [](http://badge.fury.io/rb/jsonb_accessor) [](https://travis-ci.org/madeintandem/jsonb_accessor) <img src="https://raw.githubusercontent.com/madeintandem/jsonb_accessor/master/json-bee.png" alt="JSONb Accessor Logo" align="right" />
|
6
6
|
|
7
|
-
Adds typed `jsonb` backed fields as first class citizens to your `ActiveRecord` models. This gem is similar in spirit to [HstoreAccessor](https://github.com/
|
7
|
+
Adds typed `jsonb` backed fields as first class citizens to your `ActiveRecord` models. This gem is similar in spirit to [HstoreAccessor](https://github.com/madeintandem/hstore_accessor), but the `jsonb` column in PostgreSQL has a few distinct advantages, mostly around nested documents and support for collections.
|
8
8
|
|
9
9
|
It also adds generic scopes for querying `jsonb` columns.
|
10
10
|
|
11
|
-
## 1.0 Beta
|
12
|
-
|
13
|
-
This README reflects the most recent 1.0 beta. Method names and interfaces may still change.
|
14
|
-
|
15
11
|
## Table of Contents
|
16
12
|
|
17
13
|
* [Installation](#installation)
|
@@ -29,7 +25,7 @@ This README reflects the most recent 1.0 beta. Method names and interfaces may s
|
|
29
25
|
Add this line to your application's `Gemfile`:
|
30
26
|
|
31
27
|
```ruby
|
32
|
-
gem "jsonb_accessor", "1.0.0
|
28
|
+
gem "jsonb_accessor", "~> 1.0.0"
|
33
29
|
```
|
34
30
|
|
35
31
|
And then execute:
|
@@ -141,6 +137,14 @@ For time related fields you can query using `before` and `after`.
|
|
141
137
|
Product.all.data_where(reviewed_at: { before: Time.current.beginning_of_week, after: 4.weeks.ago })
|
142
138
|
```
|
143
139
|
|
140
|
+
If you want to search for records within a certain time, date, or number range, just pass in the range (Note: this is just shorthand for the above mentioned `before`/`after`/`less_than`/`less_than_or_equal_to`/`greater_than_or_equal_to`/etc options).
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
Product.all.data_where(price: 10..20)
|
144
|
+
Product.all.data_where(price: 10...20)
|
145
|
+
Product.all.data_where(reviewed_at: Time.current..3.days.from_now)
|
146
|
+
```
|
147
|
+
|
144
148
|
This scope is a convenient wrapper around the `jsonb_where` `scope` that saves you from having to convert the given keys to the store keys and from specifying the column.
|
145
149
|
|
146
150
|
### `jsonb_where`
|
@@ -164,6 +168,27 @@ Just the opposite of `jsonb_where`. Note that this will automatically exclude al
|
|
164
168
|
Product.all.jsonb_where_not(:data, reviewed_at: { before: Time.current }, p: { greater_than: 5 })
|
165
169
|
```
|
166
170
|
|
171
|
+
### `<jsonb_attribute>_order`
|
172
|
+
|
173
|
+
Orders your query according to values in the Jsonb Accessor fields similar to ActiveRecord's `order`.
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
Product.all.data_order(:price)
|
177
|
+
Product.all.data_order(:price, :reviewed_at)
|
178
|
+
Product.all.data_order(:price, reviewed_at: :desc)
|
179
|
+
```
|
180
|
+
|
181
|
+
It will convert your given keys into store keys if necessary.
|
182
|
+
|
183
|
+
### `jsonb_order`
|
184
|
+
|
185
|
+
Allows you to order by a Jsonb Accessor field.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
Product.all.jsonb_order(:data, :price, :asc)
|
189
|
+
Product.all.jsonb_order(:data, :price, :desc)
|
190
|
+
```
|
191
|
+
|
167
192
|
### `jsonb_contains`
|
168
193
|
|
169
194
|
Returns all records that contain the given JSON paths.
|
@@ -284,7 +309,7 @@ From here any attributes specific to any sub-class can be stored in the
|
|
284
309
|
individual fields in an `jsonb` column.
|
285
310
|
|
286
311
|
This approach was originally conceived by Joe Hirn in [this blog
|
287
|
-
post](
|
312
|
+
post](https://madeintandem.com/blog/2013-3-single-table-inheritance-hstore-lovely-combination/).
|
288
313
|
|
289
314
|
## Validations
|
290
315
|
|
@@ -309,7 +334,7 @@ Run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
309
334
|
|
310
335
|
## Contributing
|
311
336
|
|
312
|
-
1. [Fork it](https://github.com/
|
337
|
+
1. [Fork it](https://github.com/madeintandem/jsonb_accessor/fork)
|
313
338
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
314
339
|
3. Add tests and changes (run the tests with `rake`)
|
315
340
|
4. Commit your changes (`git commit -am 'Add some feature'`)
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "rubygems"
|
3
4
|
require "bundler/setup"
|
4
5
|
require "bundler/gem_tasks"
|
@@ -10,4 +11,4 @@ StandaloneMigrations::Tasks.load_tasks
|
|
10
11
|
RSpec::Core::RakeTask.new
|
11
12
|
RuboCop::RakeTask.new
|
12
13
|
|
13
|
-
task(default: [
|
14
|
+
task(default: %i[rubocop spec])
|
data/bin/console
CHANGED
data/db/schema.rb
CHANGED
@@ -15,22 +15,22 @@ ActiveRecord::Schema.define(version: 20150407031737) do
|
|
15
15
|
# These are extensions that must be enabled in order to support this database
|
16
16
|
enable_extension "plpgsql"
|
17
17
|
|
18
|
-
create_table "product_categories", force: :cascade do |t|
|
18
|
+
create_table "product_categories", id: :serial, force: :cascade do |t|
|
19
19
|
t.jsonb "options"
|
20
20
|
end
|
21
21
|
|
22
|
-
create_table "products", force: :cascade do |t|
|
23
|
-
t.jsonb
|
24
|
-
t.jsonb
|
25
|
-
t.string
|
26
|
-
t.integer
|
27
|
-
t.integer
|
28
|
-
t.boolean
|
29
|
-
t.float
|
30
|
-
t.time
|
31
|
-
t.date
|
22
|
+
create_table "products", id: :serial, force: :cascade do |t|
|
23
|
+
t.jsonb "options"
|
24
|
+
t.jsonb "data"
|
25
|
+
t.string "string_type"
|
26
|
+
t.integer "integer_type"
|
27
|
+
t.integer "product_category_id"
|
28
|
+
t.boolean "boolean_type"
|
29
|
+
t.float "float_type"
|
30
|
+
t.time "time_type"
|
31
|
+
t.date "date_type"
|
32
32
|
t.datetime "datetime_type"
|
33
|
-
t.decimal
|
33
|
+
t.decimal "decimal_type"
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
data/jsonb_accessor.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
lib = File.expand_path("../lib", __FILE__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require "jsonb_accessor/version"
|
@@ -7,32 +9,32 @@ Gem::Specification.new do |spec|
|
|
7
9
|
spec.name = "jsonb_accessor"
|
8
10
|
spec.version = JsonbAccessor::VERSION
|
9
11
|
spec.authors = ["Michael Crismali", "Joe Hirn", "Jason Haruska"]
|
10
|
-
spec.email = ["michael
|
12
|
+
spec.email = ["michael@crismali.com", "joe@devmynd.com", "jason@haruska.com"]
|
11
13
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
+
spec.summary = "Adds typed jsonb backed fields to your ActiveRecord models."
|
15
|
+
spec.description = "Adds typed jsonb backed fields to your ActiveRecord models."
|
14
16
|
spec.homepage = "https://github.com/devmynd/jsonb_accessor"
|
15
17
|
spec.license = "MIT"
|
16
18
|
spec.required_ruby_version = "~> 2.0"
|
17
19
|
|
18
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.match(
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) || f.match(/png\z/) }
|
19
21
|
spec.bindir = "exe"
|
20
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
23
|
spec.require_paths = ["lib"]
|
22
24
|
|
23
25
|
spec.add_dependency "activerecord", ">= 5.0"
|
26
|
+
spec.add_dependency "activesupport", ">= 5.0"
|
24
27
|
spec.add_dependency "pg", ">= 0.18.1"
|
25
28
|
|
26
|
-
spec.add_development_dependency "appraisal"
|
27
|
-
spec.add_development_dependency "bundler", "~> 1.
|
28
|
-
spec.add_development_dependency "database_cleaner"
|
29
|
+
spec.add_development_dependency "appraisal", "~> 2.2.0"
|
30
|
+
spec.add_development_dependency "bundler", "~> 2.1.4"
|
31
|
+
spec.add_development_dependency "database_cleaner", "~> 1.6.0"
|
29
32
|
spec.add_development_dependency "awesome_print"
|
30
33
|
spec.add_development_dependency "pry"
|
31
34
|
spec.add_development_dependency "pry-doc"
|
32
35
|
spec.add_development_dependency "pry-nav"
|
33
|
-
spec.add_development_dependency "rake", "
|
34
|
-
spec.add_development_dependency "rspec", "~> 3.
|
35
|
-
spec.add_development_dependency "rubocop", "~> 0.
|
36
|
-
spec.add_development_dependency "
|
37
|
-
spec.add_development_dependency "standalone_migrations"
|
36
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.6.0"
|
38
|
+
spec.add_development_dependency "rubocop", "~> 0.48.1"
|
39
|
+
spec.add_development_dependency "standalone_migrations", "~> 5.2.0"
|
38
40
|
end
|
data/lib/jsonb_accessor.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require "active_record"
|
3
4
|
|
4
5
|
require "active_record/connection_adapters/postgresql_adapter"
|
5
6
|
|
6
7
|
require "jsonb_accessor/version"
|
7
8
|
require "jsonb_accessor/macro"
|
9
|
+
require "jsonb_accessor/query_helper"
|
8
10
|
require "jsonb_accessor/query_builder"
|
9
11
|
|
10
12
|
module JsonbAccessor
|
data/lib/jsonb_accessor/macro.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module JsonbAccessor
|
3
4
|
module Macro
|
4
5
|
module ClassMethods
|
@@ -33,7 +34,7 @@ module JsonbAccessor
|
|
33
34
|
end
|
34
35
|
|
35
36
|
# Get store keys to default values mapping
|
36
|
-
store_keys_and_defaults = ::JsonbAccessor::
|
37
|
+
store_keys_and_defaults = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(names_and_defaults, public_send(store_key_mapping_method_name))
|
37
38
|
|
38
39
|
# Define jsonb_defaults_mapping_for_<jsonb_attribute>
|
39
40
|
defaults_mapping_method_name = "jsonb_defaults_mapping_for_#{jsonb_attribute}"
|
@@ -45,7 +46,14 @@ module JsonbAccessor
|
|
45
46
|
end
|
46
47
|
|
47
48
|
all_defaults_mapping = public_send(defaults_mapping_method_name)
|
48
|
-
|
49
|
+
# Fields may have procs as default value. This means `all_defaults_mapping` may contain procs as values. To make this work
|
50
|
+
# with the attributes API, we need to wrap `all_defaults_mapping` with a proc itself, making sure it returns a plain hash
|
51
|
+
# each time it is evaluated.
|
52
|
+
all_defaults_mapping_proc =
|
53
|
+
if all_defaults_mapping.present?
|
54
|
+
-> { all_defaults_mapping.map { |key, value| [key, value.respond_to?(:call) ? value.call : value] }.to_h }
|
55
|
+
end
|
56
|
+
attribute jsonb_attribute, :jsonb, default: all_defaults_mapping_proc if all_defaults_mapping_proc.present?
|
49
57
|
|
50
58
|
# Setters are in a module to allow users to override them and still be able to use `super`.
|
51
59
|
setters = Module.new do
|
@@ -66,7 +74,7 @@ module JsonbAccessor
|
|
66
74
|
empty_store_key_attributes = names_to_store_keys.values.each_with_object({}) { |name, defaults| defaults[name] = nil }
|
67
75
|
empty_named_attributes = names_to_store_keys.keys.each_with_object({}) { |name, defaults| defaults[name] = nil }
|
68
76
|
|
69
|
-
store_key_attributes = ::JsonbAccessor::
|
77
|
+
store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(value, names_to_store_keys)
|
70
78
|
write_attribute(jsonb_attribute, empty_store_key_attributes.merge(store_key_attributes))
|
71
79
|
|
72
80
|
empty_named_attributes.merge(value).each { |name, attribute_value| write_attribute(name, attribute_value) }
|
@@ -76,25 +84,42 @@ module JsonbAccessor
|
|
76
84
|
|
77
85
|
# Makes sure new objects have the appropriate values in their jsonb fields.
|
78
86
|
after_initialize do
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
87
|
+
if has_attribute?(jsonb_attribute)
|
88
|
+
jsonb_values = public_send(jsonb_attribute) || {}
|
89
|
+
jsonb_values.each do |store_key, value|
|
90
|
+
name = names_and_store_keys.key(store_key)
|
91
|
+
next unless name
|
92
|
+
|
93
|
+
write_attribute(name, value)
|
94
|
+
clear_attribute_change(name) if persisted?
|
95
|
+
end
|
83
96
|
end
|
84
|
-
clear_changes_information if persisted?
|
85
97
|
end
|
86
98
|
|
87
99
|
# <jsonb_attribute>_where scope
|
88
100
|
scope("#{jsonb_attribute}_where", lambda do |attributes|
|
89
|
-
store_key_attributes = ::JsonbAccessor::
|
101
|
+
store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
|
90
102
|
jsonb_where(jsonb_attribute, store_key_attributes)
|
91
103
|
end)
|
92
104
|
|
93
105
|
# <jsonb_attribute>_where_not scope
|
94
106
|
scope("#{jsonb_attribute}_where_not", lambda do |attributes|
|
95
|
-
store_key_attributes = ::JsonbAccessor::
|
107
|
+
store_key_attributes = ::JsonbAccessor::QueryHelper.convert_keys_to_store_keys(attributes, all.model.public_send(store_key_mapping_method_name))
|
96
108
|
jsonb_where_not(jsonb_attribute, store_key_attributes)
|
97
109
|
end)
|
110
|
+
|
111
|
+
# <jsonb_attribute>_order scope
|
112
|
+
scope("#{jsonb_attribute}_order", lambda do |*args|
|
113
|
+
ordering_options = args.extract_options!
|
114
|
+
order_by_defaults = args.each_with_object({}) { |attribute, config| config[attribute] = :asc }
|
115
|
+
store_key_mapping = all.model.public_send(store_key_mapping_method_name)
|
116
|
+
|
117
|
+
order_by_defaults.merge(ordering_options).reduce(all) do |query, (name, direction)|
|
118
|
+
key = store_key_mapping[name.to_s]
|
119
|
+
order_query = jsonb_order(jsonb_attribute, key, direction)
|
120
|
+
query.merge(order_query)
|
121
|
+
end
|
122
|
+
end)
|
98
123
|
end
|
99
124
|
end
|
100
125
|
end
|
@@ -1,111 +1,41 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module JsonbAccessor
|
3
|
-
GREATER_THAN = ">"
|
4
|
-
GREATER_THAN_OR_EQUAL_TO = ">="
|
5
|
-
LESS_THAN = "<"
|
6
|
-
LESS_THAN_OR_EQUAL_TO = "<="
|
7
|
-
|
8
|
-
NUMBER_OPERATORS_MAP = {
|
9
|
-
GREATER_THAN => GREATER_THAN,
|
10
|
-
"greater_than" => GREATER_THAN,
|
11
|
-
"gt" => GREATER_THAN,
|
12
|
-
GREATER_THAN_OR_EQUAL_TO => GREATER_THAN_OR_EQUAL_TO,
|
13
|
-
"greater_than_or_equal_to" => GREATER_THAN_OR_EQUAL_TO,
|
14
|
-
"gte" => GREATER_THAN_OR_EQUAL_TO,
|
15
|
-
LESS_THAN => LESS_THAN,
|
16
|
-
"less_than" => LESS_THAN,
|
17
|
-
"lt" => LESS_THAN,
|
18
|
-
LESS_THAN_OR_EQUAL_TO => LESS_THAN_OR_EQUAL_TO,
|
19
|
-
"less_than_or_equal_to" => LESS_THAN_OR_EQUAL_TO,
|
20
|
-
"lte" => LESS_THAN_OR_EQUAL_TO
|
21
|
-
}.freeze
|
22
|
-
|
23
|
-
NUMBER_OPERATORS = NUMBER_OPERATORS_MAP.keys.freeze
|
24
|
-
|
25
|
-
TIME_OPERATORS_MAP = {
|
26
|
-
"after" => GREATER_THAN,
|
27
|
-
"before" => LESS_THAN
|
28
|
-
}.freeze
|
29
|
-
|
30
|
-
TIME_OPERATORS = TIME_OPERATORS_MAP.keys.freeze
|
31
|
-
|
32
|
-
IS_NUMBER_QUERY_ARGUMENTS = lambda do |arg|
|
33
|
-
arg.is_a?(Hash) &&
|
34
|
-
arg.keys.map(&:to_s).all? { |key| JsonbAccessor::NUMBER_OPERATORS.include?(key) }
|
35
|
-
end
|
36
|
-
|
37
|
-
IS_TIME_QUERY_ARGUMENTS = lambda do |arg|
|
38
|
-
arg.is_a?(Hash) &&
|
39
|
-
arg.keys.map(&:to_s).all? { |key| JsonbAccessor::TIME_OPERATORS.include?(key) }
|
40
|
-
end
|
41
|
-
|
42
|
-
ORDER_DIRECTIONS = [:asc, :desc, "asc", "desc"].freeze
|
43
2
|
|
3
|
+
module JsonbAccessor
|
44
4
|
module QueryBuilder
|
45
5
|
extend ActiveSupport::Concern
|
46
|
-
InvalidColumnName = Class.new(StandardError)
|
47
|
-
InvalidFieldName = Class.new(StandardError)
|
48
|
-
InvalidDirection = Class.new(StandardError)
|
49
|
-
|
50
|
-
def self.validate_column_name!(query, column_name)
|
51
|
-
if query.model.columns.none? { |column| column.name == column_name.to_s }
|
52
|
-
raise InvalidColumnName, "a column named `#{column_name}` does not exist on the `#{query.model.table_name}` table"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.validate_field_name!(query, column_name, field_name)
|
57
|
-
store_keys = query.model.public_send("jsonb_store_key_mapping_for_#{column_name}").values
|
58
|
-
if store_keys.exclude?(field_name.to_s)
|
59
|
-
valid_field_names = store_keys.map { |key| "`#{key}`" }.join(", ")
|
60
|
-
raise InvalidFieldName, "`#{field_name}` is not a valid field name, valid field names include: #{valid_field_names}"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def self.validate_direction!(option)
|
65
|
-
if ORDER_DIRECTIONS.exclude?(option)
|
66
|
-
raise InvalidDirection, "`#{option}` is not a valid direction for ordering, only `asc` and `desc` are accepted"
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.convert_keys_to_store_keys(attributes, store_key_mapping)
|
71
|
-
attributes.each_with_object({}) do |(name, value), new_attributes|
|
72
|
-
store_key = store_key_mapping[name.to_s]
|
73
|
-
new_attributes[store_key] = value
|
74
|
-
end
|
75
|
-
end
|
76
6
|
|
77
7
|
included do
|
78
8
|
scope(:jsonb_contains, lambda do |column_name, attributes|
|
79
|
-
JsonbAccessor::
|
9
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
80
10
|
where("#{table_name}.#{column_name} @> (?)::jsonb", attributes.to_json)
|
81
11
|
end)
|
82
12
|
|
83
13
|
scope(:jsonb_excludes, lambda do |column_name, attributes|
|
84
|
-
JsonbAccessor::
|
14
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
85
15
|
where.not("#{table_name}.#{column_name} @> (?)::jsonb", attributes.to_json)
|
86
16
|
end)
|
87
17
|
|
88
18
|
scope(:jsonb_number_where, lambda do |column_name, field_name, given_operator, value|
|
89
|
-
JsonbAccessor::
|
90
|
-
operator = JsonbAccessor::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
|
19
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
20
|
+
operator = JsonbAccessor::QueryHelper::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
|
91
21
|
where("(#{table_name}.#{column_name} ->> ?)::float #{operator} ?", field_name, value)
|
92
22
|
end)
|
93
23
|
|
94
24
|
scope(:jsonb_number_where_not, lambda do |column_name, field_name, given_operator, value|
|
95
|
-
JsonbAccessor::
|
96
|
-
operator = JsonbAccessor::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
|
25
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
26
|
+
operator = JsonbAccessor::QueryHelper::NUMBER_OPERATORS_MAP.fetch(given_operator.to_s)
|
97
27
|
where.not("(#{table_name}.#{column_name} ->> ?)::float #{operator} ?", field_name, value)
|
98
28
|
end)
|
99
29
|
|
100
30
|
scope(:jsonb_time_where, lambda do |column_name, field_name, given_operator, value|
|
101
|
-
JsonbAccessor::
|
102
|
-
operator = JsonbAccessor::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
|
31
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
32
|
+
operator = JsonbAccessor::QueryHelper::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
|
103
33
|
where("(#{table_name}.#{column_name} ->> ?)::timestamp #{operator} ?", field_name, value)
|
104
34
|
end)
|
105
35
|
|
106
36
|
scope(:jsonb_time_where_not, lambda do |column_name, field_name, given_operator, value|
|
107
|
-
JsonbAccessor::
|
108
|
-
operator = JsonbAccessor::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
|
37
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
38
|
+
operator = JsonbAccessor::QueryHelper::TIME_OPERATORS_MAP.fetch(given_operator.to_s)
|
109
39
|
where.not("(#{table_name}.#{column_name} ->> ?)::timestamp #{operator} ?", field_name, value)
|
110
40
|
end)
|
111
41
|
|
@@ -113,11 +43,10 @@ module JsonbAccessor
|
|
113
43
|
query = all
|
114
44
|
contains_attributes = {}
|
115
45
|
|
116
|
-
attributes.each do |name, value|
|
117
|
-
|
118
|
-
when IS_NUMBER_QUERY_ARGUMENTS
|
46
|
+
JsonbAccessor::QueryHelper.convert_ranges(attributes).each do |name, value|
|
47
|
+
if JsonbAccessor::QueryHelper.number_query_arguments?(value)
|
119
48
|
value.each { |operator, query_value| query = query.jsonb_number_where(column_name, name, operator, query_value) }
|
120
|
-
|
49
|
+
elsif JsonbAccessor::QueryHelper.time_query_arguments?(value)
|
121
50
|
value.each { |operator, query_value| query = query.jsonb_time_where(column_name, name, operator, query_value) }
|
122
51
|
else
|
123
52
|
contains_attributes[name] = value
|
@@ -132,10 +61,13 @@ module JsonbAccessor
|
|
132
61
|
excludes_attributes = {}
|
133
62
|
|
134
63
|
attributes.each do |name, value|
|
135
|
-
|
136
|
-
|
64
|
+
if value.is_a?(Range)
|
65
|
+
raise JsonbAccessor::QueryHelper::NotSupported, "`jsonb_where_not` scope does not accept ranges as arguments. Given `#{value}` for `#{name}` field"
|
66
|
+
end
|
67
|
+
|
68
|
+
if JsonbAccessor::QueryHelper.number_query_arguments?(value)
|
137
69
|
value.each { |operator, query_value| query = query.jsonb_number_where_not(column_name, name, operator, query_value) }
|
138
|
-
|
70
|
+
elsif JsonbAccessor::QueryHelper.time_query_arguments?(value)
|
139
71
|
value.each { |operator, query_value| query = query.jsonb_time_where_not(column_name, name, operator, query_value) }
|
140
72
|
else
|
141
73
|
excludes_attributes[name] = value
|
@@ -146,10 +78,10 @@ module JsonbAccessor
|
|
146
78
|
end)
|
147
79
|
|
148
80
|
scope(:jsonb_order, lambda do |column_name, field_name, direction|
|
149
|
-
JsonbAccessor::
|
150
|
-
JsonbAccessor::
|
151
|
-
JsonbAccessor::
|
152
|
-
order("(#{table_name}.#{column_name} -> '#{field_name}') #{direction}")
|
81
|
+
JsonbAccessor::QueryHelper.validate_column_name!(all, column_name)
|
82
|
+
JsonbAccessor::QueryHelper.validate_field_name!(all, column_name, field_name)
|
83
|
+
JsonbAccessor::QueryHelper.validate_direction!(direction)
|
84
|
+
order(Arel.sql("(#{table_name}.#{column_name} -> '#{field_name}') #{direction}"))
|
153
85
|
end)
|
154
86
|
end
|
155
87
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JsonbAccessor
|
4
|
+
module QueryHelper
|
5
|
+
# Errors
|
6
|
+
InvalidColumnName = Class.new(StandardError)
|
7
|
+
InvalidFieldName = Class.new(StandardError)
|
8
|
+
InvalidDirection = Class.new(StandardError)
|
9
|
+
NotSupported = Class.new(StandardError)
|
10
|
+
|
11
|
+
# Constants
|
12
|
+
GREATER_THAN = ">"
|
13
|
+
GREATER_THAN_OR_EQUAL_TO = ">="
|
14
|
+
LESS_THAN = "<"
|
15
|
+
LESS_THAN_OR_EQUAL_TO = "<="
|
16
|
+
|
17
|
+
NUMBER_OPERATORS_MAP = {
|
18
|
+
GREATER_THAN => GREATER_THAN,
|
19
|
+
"greater_than" => GREATER_THAN,
|
20
|
+
"gt" => GREATER_THAN,
|
21
|
+
GREATER_THAN_OR_EQUAL_TO => GREATER_THAN_OR_EQUAL_TO,
|
22
|
+
"greater_than_or_equal_to" => GREATER_THAN_OR_EQUAL_TO,
|
23
|
+
"gte" => GREATER_THAN_OR_EQUAL_TO,
|
24
|
+
LESS_THAN => LESS_THAN,
|
25
|
+
"less_than" => LESS_THAN,
|
26
|
+
"lt" => LESS_THAN,
|
27
|
+
LESS_THAN_OR_EQUAL_TO => LESS_THAN_OR_EQUAL_TO,
|
28
|
+
"less_than_or_equal_to" => LESS_THAN_OR_EQUAL_TO,
|
29
|
+
"lte" => LESS_THAN_OR_EQUAL_TO
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
NUMBER_OPERATORS = NUMBER_OPERATORS_MAP.keys.freeze
|
33
|
+
|
34
|
+
TIME_OPERATORS_MAP = {
|
35
|
+
"after" => GREATER_THAN,
|
36
|
+
"before" => LESS_THAN
|
37
|
+
}.freeze
|
38
|
+
|
39
|
+
TIME_OPERATORS = TIME_OPERATORS_MAP.keys.freeze
|
40
|
+
|
41
|
+
ORDER_DIRECTIONS = [:asc, :desc, "asc", "desc"].freeze
|
42
|
+
|
43
|
+
class << self
|
44
|
+
def validate_column_name!(query, column_name)
|
45
|
+
if query.model.columns.none? { |column| column.name == column_name.to_s }
|
46
|
+
raise InvalidColumnName, "a column named `#{column_name}` does not exist on the `#{query.model.table_name}` table"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate_field_name!(query, column_name, field_name)
|
51
|
+
store_keys = query.model.public_send("jsonb_store_key_mapping_for_#{column_name}").values
|
52
|
+
if store_keys.exclude?(field_name.to_s)
|
53
|
+
valid_field_names = store_keys.map { |key| "`#{key}`" }.join(", ")
|
54
|
+
raise InvalidFieldName, "`#{field_name}` is not a valid field name, valid field names include: #{valid_field_names}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate_direction!(option)
|
59
|
+
if ORDER_DIRECTIONS.exclude?(option)
|
60
|
+
raise InvalidDirection, "`#{option}` is not a valid direction for ordering, only `asc` and `desc` are accepted"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def convert_keys_to_store_keys(attributes, store_key_mapping)
|
65
|
+
attributes.each_with_object({}) do |(name, value), new_attributes|
|
66
|
+
store_key = store_key_mapping[name.to_s]
|
67
|
+
new_attributes[store_key] = value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def number_query_arguments?(arg)
|
72
|
+
arg.is_a?(Hash) && arg.keys.map(&:to_s).all? { |key| NUMBER_OPERATORS.include?(key) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def time_query_arguments?(arg)
|
76
|
+
arg.is_a?(Hash) && arg.keys.map(&:to_s).all? { |key| TIME_OPERATORS.include?(key) }
|
77
|
+
end
|
78
|
+
|
79
|
+
def convert_number_ranges(attributes)
|
80
|
+
attributes.each_with_object({}) do |(name, value), new_attributes|
|
81
|
+
is_range = value.is_a?(Range)
|
82
|
+
|
83
|
+
new_attributes[name] = if is_range && value.first.is_a?(Numeric) && value.exclude_end?
|
84
|
+
{ greater_than_or_equal_to: value.first, less_than: value.end }
|
85
|
+
elsif is_range && value.first.is_a?(Numeric)
|
86
|
+
{ greater_than_or_equal_to: value.first, less_than_or_equal_to: value.end }
|
87
|
+
else
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def convert_time_ranges(attributes)
|
94
|
+
attributes.each_with_object({}) do |(name, value), new_attributes|
|
95
|
+
is_range = value.is_a?(Range)
|
96
|
+
|
97
|
+
if is_range && (value.first.is_a?(Time) || value.first.is_a?(Date))
|
98
|
+
start_time = value.first
|
99
|
+
end_time = value.end
|
100
|
+
new_attributes[name] = { before: end_time, after: start_time }
|
101
|
+
else
|
102
|
+
new_attributes[name] = value
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def convert_ranges(attributes)
|
108
|
+
%i[convert_number_ranges convert_time_ranges].reduce(attributes) do |new_attributes, converter_method|
|
109
|
+
public_send(converter_method, new_attributes)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
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: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Crismali
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -26,6 +26,20 @@ dependencies:
|
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
28
|
version: '5.0'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: activesupport
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: '5.0'
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '5.0'
|
29
43
|
- !ruby/object:Gem::Dependency
|
30
44
|
name: pg
|
31
45
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,44 +58,44 @@ dependencies:
|
|
44
58
|
name: appraisal
|
45
59
|
requirement: !ruby/object:Gem::Requirement
|
46
60
|
requirements:
|
47
|
-
- - "
|
61
|
+
- - "~>"
|
48
62
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
63
|
+
version: 2.2.0
|
50
64
|
type: :development
|
51
65
|
prerelease: false
|
52
66
|
version_requirements: !ruby/object:Gem::Requirement
|
53
67
|
requirements:
|
54
|
-
- - "
|
68
|
+
- - "~>"
|
55
69
|
- !ruby/object:Gem::Version
|
56
|
-
version:
|
70
|
+
version: 2.2.0
|
57
71
|
- !ruby/object:Gem::Dependency
|
58
72
|
name: bundler
|
59
73
|
requirement: !ruby/object:Gem::Requirement
|
60
74
|
requirements:
|
61
75
|
- - "~>"
|
62
76
|
- !ruby/object:Gem::Version
|
63
|
-
version:
|
77
|
+
version: 2.1.4
|
64
78
|
type: :development
|
65
79
|
prerelease: false
|
66
80
|
version_requirements: !ruby/object:Gem::Requirement
|
67
81
|
requirements:
|
68
82
|
- - "~>"
|
69
83
|
- !ruby/object:Gem::Version
|
70
|
-
version:
|
84
|
+
version: 2.1.4
|
71
85
|
- !ruby/object:Gem::Dependency
|
72
86
|
name: database_cleaner
|
73
87
|
requirement: !ruby/object:Gem::Requirement
|
74
88
|
requirements:
|
75
|
-
- - "
|
89
|
+
- - "~>"
|
76
90
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
91
|
+
version: 1.6.0
|
78
92
|
type: :development
|
79
93
|
prerelease: false
|
80
94
|
version_requirements: !ruby/object:Gem::Requirement
|
81
95
|
requirements:
|
82
|
-
- - "
|
96
|
+
- - "~>"
|
83
97
|
- !ruby/object:Gem::Version
|
84
|
-
version:
|
98
|
+
version: 1.6.0
|
85
99
|
- !ruby/object:Gem::Dependency
|
86
100
|
name: awesome_print
|
87
101
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,75 +156,61 @@ dependencies:
|
|
142
156
|
name: rake
|
143
157
|
requirement: !ruby/object:Gem::Requirement
|
144
158
|
requirements:
|
145
|
-
- - "
|
159
|
+
- - ">="
|
146
160
|
- !ruby/object:Gem::Version
|
147
|
-
version:
|
161
|
+
version: 12.3.3
|
148
162
|
type: :development
|
149
163
|
prerelease: false
|
150
164
|
version_requirements: !ruby/object:Gem::Requirement
|
151
165
|
requirements:
|
152
|
-
- - "
|
166
|
+
- - ">="
|
153
167
|
- !ruby/object:Gem::Version
|
154
|
-
version:
|
168
|
+
version: 12.3.3
|
155
169
|
- !ruby/object:Gem::Dependency
|
156
170
|
name: rspec
|
157
171
|
requirement: !ruby/object:Gem::Requirement
|
158
172
|
requirements:
|
159
173
|
- - "~>"
|
160
174
|
- !ruby/object:Gem::Version
|
161
|
-
version:
|
175
|
+
version: 3.6.0
|
162
176
|
type: :development
|
163
177
|
prerelease: false
|
164
178
|
version_requirements: !ruby/object:Gem::Requirement
|
165
179
|
requirements:
|
166
180
|
- - "~>"
|
167
181
|
- !ruby/object:Gem::Version
|
168
|
-
version:
|
182
|
+
version: 3.6.0
|
169
183
|
- !ruby/object:Gem::Dependency
|
170
184
|
name: rubocop
|
171
185
|
requirement: !ruby/object:Gem::Requirement
|
172
186
|
requirements:
|
173
187
|
- - "~>"
|
174
188
|
- !ruby/object:Gem::Version
|
175
|
-
version:
|
189
|
+
version: 0.48.1
|
176
190
|
type: :development
|
177
191
|
prerelease: false
|
178
192
|
version_requirements: !ruby/object:Gem::Requirement
|
179
193
|
requirements:
|
180
194
|
- - "~>"
|
181
195
|
- !ruby/object:Gem::Version
|
182
|
-
version:
|
183
|
-
- !ruby/object:Gem::Dependency
|
184
|
-
name: shoulda-matchers
|
185
|
-
requirement: !ruby/object:Gem::Requirement
|
186
|
-
requirements:
|
187
|
-
- - ">="
|
188
|
-
- !ruby/object:Gem::Version
|
189
|
-
version: '0'
|
190
|
-
type: :development
|
191
|
-
prerelease: false
|
192
|
-
version_requirements: !ruby/object:Gem::Requirement
|
193
|
-
requirements:
|
194
|
-
- - ">="
|
195
|
-
- !ruby/object:Gem::Version
|
196
|
-
version: '0'
|
196
|
+
version: 0.48.1
|
197
197
|
- !ruby/object:Gem::Dependency
|
198
198
|
name: standalone_migrations
|
199
199
|
requirement: !ruby/object:Gem::Requirement
|
200
200
|
requirements:
|
201
|
-
- - "
|
201
|
+
- - "~>"
|
202
202
|
- !ruby/object:Gem::Version
|
203
|
-
version:
|
203
|
+
version: 5.2.0
|
204
204
|
type: :development
|
205
205
|
prerelease: false
|
206
206
|
version_requirements: !ruby/object:Gem::Requirement
|
207
207
|
requirements:
|
208
|
-
- - "
|
208
|
+
- - "~>"
|
209
209
|
- !ruby/object:Gem::Version
|
210
|
-
version:
|
210
|
+
version: 5.2.0
|
211
211
|
description: Adds typed jsonb backed fields to your ActiveRecord models.
|
212
212
|
email:
|
213
|
-
- michael
|
213
|
+
- michael@crismali.com
|
214
214
|
- joe@devmynd.com
|
215
215
|
- jason@haruska.com
|
216
216
|
executables: []
|
@@ -235,11 +235,12 @@ files:
|
|
235
235
|
- db/migrate/20150407031737_set_up_testing_db.rb
|
236
236
|
- db/schema.rb
|
237
237
|
- gemfiles/activerecord_5.0.0.gemfile
|
238
|
-
- gemfiles/activerecord_5.
|
238
|
+
- gemfiles/activerecord_5.1.0.gemfile
|
239
239
|
- jsonb_accessor.gemspec
|
240
240
|
- lib/jsonb_accessor.rb
|
241
241
|
- lib/jsonb_accessor/macro.rb
|
242
242
|
- lib/jsonb_accessor/query_builder.rb
|
243
|
+
- lib/jsonb_accessor/query_helper.rb
|
243
244
|
- lib/jsonb_accessor/version.rb
|
244
245
|
homepage: https://github.com/devmynd/jsonb_accessor
|
245
246
|
licenses:
|
@@ -256,12 +257,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
256
257
|
version: '2.0'
|
257
258
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
258
259
|
requirements:
|
259
|
-
- - "
|
260
|
+
- - ">="
|
260
261
|
- !ruby/object:Gem::Version
|
261
|
-
version:
|
262
|
+
version: '0'
|
262
263
|
requirements: []
|
263
264
|
rubyforge_project:
|
264
|
-
rubygems_version: 2.
|
265
|
+
rubygems_version: 2.6.11
|
265
266
|
signing_key:
|
266
267
|
specification_version: 4
|
267
268
|
summary: Adds typed jsonb backed fields to your ActiveRecord models.
|
@@ -1,140 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ..
|
3
|
-
specs:
|
4
|
-
jsonb_accessor (1.0.0.beta.4)
|
5
|
-
activerecord (>= 5.0)
|
6
|
-
pg (>= 0.18.1)
|
7
|
-
|
8
|
-
GEM
|
9
|
-
remote: https://rubygems.org/
|
10
|
-
specs:
|
11
|
-
actionpack (5.0.2)
|
12
|
-
actionview (= 5.0.2)
|
13
|
-
activesupport (= 5.0.2)
|
14
|
-
rack (~> 2.0)
|
15
|
-
rack-test (~> 0.6.3)
|
16
|
-
rails-dom-testing (~> 2.0)
|
17
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
18
|
-
actionview (5.0.2)
|
19
|
-
activesupport (= 5.0.2)
|
20
|
-
builder (~> 3.1)
|
21
|
-
erubis (~> 2.7.0)
|
22
|
-
rails-dom-testing (~> 2.0)
|
23
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
24
|
-
activemodel (5.0.2)
|
25
|
-
activesupport (= 5.0.2)
|
26
|
-
activerecord (5.0.2)
|
27
|
-
activemodel (= 5.0.2)
|
28
|
-
activesupport (= 5.0.2)
|
29
|
-
arel (~> 7.0)
|
30
|
-
activesupport (5.0.2)
|
31
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
32
|
-
i18n (~> 0.7)
|
33
|
-
minitest (~> 5.1)
|
34
|
-
tzinfo (~> 1.1)
|
35
|
-
appraisal (2.1.0)
|
36
|
-
bundler
|
37
|
-
rake
|
38
|
-
thor (>= 0.14.0)
|
39
|
-
arel (7.1.4)
|
40
|
-
ast (2.3.0)
|
41
|
-
awesome_print (1.7.0)
|
42
|
-
builder (3.2.3)
|
43
|
-
coderay (1.1.1)
|
44
|
-
concurrent-ruby (1.0.5)
|
45
|
-
database_cleaner (1.5.3)
|
46
|
-
diff-lcs (1.3)
|
47
|
-
erubis (2.7.0)
|
48
|
-
i18n (0.8.1)
|
49
|
-
loofah (2.0.3)
|
50
|
-
nokogiri (>= 1.5.9)
|
51
|
-
method_source (0.8.2)
|
52
|
-
mini_portile2 (2.1.0)
|
53
|
-
minitest (5.10.1)
|
54
|
-
nokogiri (1.7.0.1)
|
55
|
-
mini_portile2 (~> 2.1.0)
|
56
|
-
parser (2.4.0.0)
|
57
|
-
ast (~> 2.2)
|
58
|
-
pg (0.20.0)
|
59
|
-
powerpack (0.1.1)
|
60
|
-
pry (0.10.4)
|
61
|
-
coderay (~> 1.1.0)
|
62
|
-
method_source (~> 0.8.1)
|
63
|
-
slop (~> 3.4)
|
64
|
-
pry-doc (0.10.0)
|
65
|
-
pry (~> 0.9)
|
66
|
-
yard (~> 0.9)
|
67
|
-
pry-nav (0.2.4)
|
68
|
-
pry (>= 0.9.10, < 0.11.0)
|
69
|
-
rack (2.0.1)
|
70
|
-
rack-test (0.6.3)
|
71
|
-
rack (>= 1.0)
|
72
|
-
rails-dom-testing (2.0.2)
|
73
|
-
activesupport (>= 4.2.0, < 6.0)
|
74
|
-
nokogiri (~> 1.6)
|
75
|
-
rails-html-sanitizer (1.0.3)
|
76
|
-
loofah (~> 2.0)
|
77
|
-
railties (5.0.2)
|
78
|
-
actionpack (= 5.0.2)
|
79
|
-
activesupport (= 5.0.2)
|
80
|
-
method_source
|
81
|
-
rake (>= 0.8.7)
|
82
|
-
thor (>= 0.18.1, < 2.0)
|
83
|
-
rainbow (2.2.1)
|
84
|
-
rake (10.5.0)
|
85
|
-
rspec (3.5.0)
|
86
|
-
rspec-core (~> 3.5.0)
|
87
|
-
rspec-expectations (~> 3.5.0)
|
88
|
-
rspec-mocks (~> 3.5.0)
|
89
|
-
rspec-core (3.5.4)
|
90
|
-
rspec-support (~> 3.5.0)
|
91
|
-
rspec-expectations (3.5.0)
|
92
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
93
|
-
rspec-support (~> 3.5.0)
|
94
|
-
rspec-mocks (3.5.0)
|
95
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
96
|
-
rspec-support (~> 3.5.0)
|
97
|
-
rspec-support (3.5.0)
|
98
|
-
rubocop (0.47.1)
|
99
|
-
parser (>= 2.3.3.1, < 3.0)
|
100
|
-
powerpack (~> 0.1)
|
101
|
-
rainbow (>= 1.99.1, < 3.0)
|
102
|
-
ruby-progressbar (~> 1.7)
|
103
|
-
unicode-display_width (~> 1.0, >= 1.0.1)
|
104
|
-
ruby-progressbar (1.8.1)
|
105
|
-
shoulda-matchers (3.1.1)
|
106
|
-
activesupport (>= 4.0.0)
|
107
|
-
slop (3.6.0)
|
108
|
-
standalone_migrations (5.0.0)
|
109
|
-
activerecord (>= 4.2.7, < 5.1.0)
|
110
|
-
railties (>= 4.2.7, < 5.1.0)
|
111
|
-
rake (~> 10.0)
|
112
|
-
thor (0.19.4)
|
113
|
-
thread_safe (0.3.6)
|
114
|
-
tzinfo (1.2.2)
|
115
|
-
thread_safe (~> 0.1)
|
116
|
-
unicode-display_width (1.1.3)
|
117
|
-
yard (0.9.8)
|
118
|
-
|
119
|
-
PLATFORMS
|
120
|
-
ruby
|
121
|
-
|
122
|
-
DEPENDENCIES
|
123
|
-
activerecord (~> 5.0.0)
|
124
|
-
appraisal
|
125
|
-
awesome_print
|
126
|
-
bundler (~> 1.9)
|
127
|
-
database_cleaner
|
128
|
-
jsonb_accessor!
|
129
|
-
pg
|
130
|
-
pry
|
131
|
-
pry-doc
|
132
|
-
pry-nav
|
133
|
-
rake (~> 10.0)
|
134
|
-
rspec (~> 3.2)
|
135
|
-
rubocop (~> 0.31)
|
136
|
-
shoulda-matchers
|
137
|
-
standalone_migrations
|
138
|
-
|
139
|
-
BUNDLED WITH
|
140
|
-
1.14.6
|