nested_record 1.0.0.beta → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +3 -1
- data/.travis.yml +3 -0
- data/gemfiles/rails_5.2.gemfile +5 -0
- data/gemfiles/rails_6.0.gemfile +5 -0
- data/lib/nested_record.rb +7 -0
- data/lib/nested_record/base.rb +21 -1
- data/lib/nested_record/collection.rb +8 -2
- data/lib/nested_record/collection_proxy.rb +68 -0
- data/lib/nested_record/concern.rb +14 -0
- data/lib/nested_record/errors.rb +1 -0
- data/lib/nested_record/macro.rb +4 -0
- data/lib/nested_record/macro_recorder.rb +44 -0
- data/lib/nested_record/methods.rb +8 -2
- data/lib/nested_record/methods/many.rb +29 -23
- data/lib/nested_record/nested_accessors_setup.rb +66 -0
- data/lib/nested_record/primary_key_check.rb +44 -0
- data/lib/nested_record/setup.rb +105 -49
- data/lib/nested_record/version.rb +1 -1
- data/nested_record.gemspec +1 -1
- data/spec/nested_record/base_spec.rb +27 -0
- data/spec/nested_record/concern_spec.rb +46 -0
- data/spec/nested_record_spec.rb +126 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/model.rb +29 -10
- metadata +19 -6
- data/Gemfile.lock +0 -152
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5710483535afba122d37dded3a1cf743254f6a2ea43e40ad126adf8a98bf8ed3
|
4
|
+
data.tar.gz: 6219144c936fae21346f49380082b773e44c6e1bbed670de53f6b96a5d4200ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbfb5c90d3a43c9a0671af39bc3969ca544deb1ba58809800d018621d4dccded78efdba763dfb425299236cb8fd631dfe6b30786fdab45d1fe6b712070afd0fd
|
7
|
+
data.tar.gz: b18afab1feb7f6e29cac1cfec75a7b4176c3a35bf3de85a6b411d4a6501617f363c5e757d04cd009094002cba78c45682f189ad9235908598db04e65aac3e0da
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/lib/nested_record.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module NestedRecord
|
2
2
|
require 'nested_record/version'
|
3
3
|
|
4
|
+
require 'forwardable'
|
5
|
+
|
4
6
|
require 'active_record'
|
5
7
|
require 'active_support/dependencies'
|
6
8
|
require 'active_support/concern'
|
@@ -8,9 +10,14 @@ module NestedRecord
|
|
8
10
|
require 'nested_record/macro'
|
9
11
|
require 'nested_record/base'
|
10
12
|
require 'nested_record/collection'
|
13
|
+
require 'nested_record/collection_proxy'
|
11
14
|
require 'nested_record/setup'
|
15
|
+
require 'nested_record/nested_accessors_setup'
|
16
|
+
require 'nested_record/primary_key_check'
|
12
17
|
require 'nested_record/methods'
|
13
18
|
require 'nested_record/type'
|
14
19
|
require 'nested_record/errors'
|
15
20
|
require 'nested_record/lookup_const'
|
21
|
+
require 'nested_record/macro_recorder'
|
22
|
+
require 'nested_record/concern'
|
16
23
|
end
|
data/lib/nested_record/base.rb
CHANGED
@@ -4,6 +4,7 @@ class NestedRecord::Base
|
|
4
4
|
include ActiveModel::Model
|
5
5
|
include ActiveModel::Attributes
|
6
6
|
include ActiveModel::Dirty
|
7
|
+
include ActiveModel::Validations::Callbacks
|
7
8
|
include NestedRecord::Macro
|
8
9
|
|
9
10
|
class << self
|
@@ -246,7 +247,17 @@ class NestedRecord::Base
|
|
246
247
|
end
|
247
248
|
|
248
249
|
def read_attribute(attr)
|
249
|
-
|
250
|
+
attribute(attr)
|
251
|
+
end
|
252
|
+
|
253
|
+
def query_attribute(attr)
|
254
|
+
value = read_attribute(attr)
|
255
|
+
|
256
|
+
case value
|
257
|
+
when true then true
|
258
|
+
when false, nil then false
|
259
|
+
else !value.blank?
|
260
|
+
end
|
250
261
|
end
|
251
262
|
|
252
263
|
def match?(attrs)
|
@@ -256,6 +267,8 @@ class NestedRecord::Base
|
|
256
267
|
is_a? others
|
257
268
|
when :_instance_of?, '_instance_of?'
|
258
269
|
instance_of? others
|
270
|
+
when :_not_equal?, '_not_equal?'
|
271
|
+
!equal?(others)
|
259
272
|
else
|
260
273
|
ours = read_attribute(attr)
|
261
274
|
if others.is_a? Array
|
@@ -268,4 +281,11 @@ class NestedRecord::Base
|
|
268
281
|
end
|
269
282
|
|
270
283
|
define_model_callbacks :initialize, only: :after
|
284
|
+
attribute_method_suffix '?'
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
def attribute?(attr)
|
289
|
+
query_attribute(attr)
|
290
|
+
end
|
271
291
|
end
|
@@ -42,6 +42,7 @@ class NestedRecord::Collection
|
|
42
42
|
|
43
43
|
def build(attributes = {})
|
44
44
|
record_class.new(attributes).tap do |obj|
|
45
|
+
yield obj if block_given?
|
45
46
|
self << obj
|
46
47
|
end
|
47
48
|
end
|
@@ -110,14 +111,19 @@ class NestedRecord::Collection
|
|
110
111
|
RUBY
|
111
112
|
end
|
112
113
|
|
114
|
+
def exists?(attrs)
|
115
|
+
attrs = attrs.stringify_keys
|
116
|
+
any? { |obj| obj.match?(attrs) }
|
117
|
+
end
|
118
|
+
|
113
119
|
def find_by(attrs)
|
114
120
|
attrs = attrs.stringify_keys
|
115
121
|
find { |obj| obj.match?(attrs) }
|
116
122
|
end
|
117
123
|
|
118
|
-
def find_or_initialize_by(attrs)
|
124
|
+
def find_or_initialize_by(attrs, &block)
|
119
125
|
attrs = attrs.stringify_keys
|
120
|
-
find_by(attrs) || build(attrs)
|
126
|
+
find_by(attrs) || build(attrs, &block)
|
121
127
|
end
|
122
128
|
|
123
129
|
private
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class NestedRecord::CollectionProxy
|
2
|
+
extend Forwardable
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def subclass_for(setup)
|
7
|
+
Class.new(self) do
|
8
|
+
methods = setup.collection_class.public_instance_methods
|
9
|
+
methods -= NestedRecord::Collection.public_instance_methods
|
10
|
+
methods -= NestedRecord::CollectionProxy.public_instance_methods(false)
|
11
|
+
def_delegators :__collection__, *methods unless methods.empty?
|
12
|
+
@setup = setup
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def __nested_record_setup__
|
17
|
+
@setup
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(owner)
|
22
|
+
@owner = owner
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing(method_name, *args, &block)
|
26
|
+
collection = __collection__
|
27
|
+
if collection.respond_to? method_name
|
28
|
+
collection.public_send(method_name, *args, &block)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def respond_to_missing?(method_name, _)
|
35
|
+
super || __collection__.respond_to?(method_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def build(attributes = {})
|
39
|
+
__collection__.build(attributes) do |record|
|
40
|
+
ensure_primary! record
|
41
|
+
yield record if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def __build__(attributes)
|
46
|
+
__collection__.build(attributes)
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_or_initialize_by(attributes)
|
50
|
+
__collection__.find_or_initialize_by(attributes) do |record|
|
51
|
+
ensure_primary! record
|
52
|
+
yield record if block_given?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def_delegators :__collection__, *(NestedRecord::Collection.public_instance_methods(false) - public_instance_methods(false))
|
57
|
+
|
58
|
+
def __collection__
|
59
|
+
@owner.read_attribute(self.class.__nested_record_setup__.name)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def ensure_primary!(record)
|
65
|
+
check = self.class.__nested_record_setup__.primary_check(record.read_attribute('type'))
|
66
|
+
check&.perform!(__collection__, record)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module NestedRecord::Concern
|
2
|
+
extend Forwardable
|
3
|
+
|
4
|
+
def_delegators :macro_recorder, *(NestedRecord::MacroRecorder::MACROS - [:include])
|
5
|
+
|
6
|
+
def included(mod_or_class)
|
7
|
+
super
|
8
|
+
macro_recorder.apply_to(mod_or_class)
|
9
|
+
end
|
10
|
+
|
11
|
+
def macro_recorder
|
12
|
+
@macro_recorder ||= NestedRecord::MacroRecorder.new
|
13
|
+
end
|
14
|
+
end
|
data/lib/nested_record/errors.rb
CHANGED
data/lib/nested_record/macro.rb
CHANGED
@@ -11,5 +11,9 @@ module NestedRecord::Macro
|
|
11
11
|
def has_one_nested(name, **options, &block)
|
12
12
|
NestedRecord::Setup::HasOne.new(self, name, **options, &block)
|
13
13
|
end
|
14
|
+
|
15
|
+
def nested_accessors(from:, **options, &block)
|
16
|
+
NestedRecord::NestedAccessorsSetup.new(self, from, **options, &block)
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class NestedRecord::MacroRecorder
|
2
|
+
def initialize
|
3
|
+
@macros = []
|
4
|
+
end
|
5
|
+
|
6
|
+
attr_reader :macros
|
7
|
+
|
8
|
+
MACROS = %i[
|
9
|
+
include
|
10
|
+
attribute
|
11
|
+
def_primary_uuid
|
12
|
+
primary_key
|
13
|
+
has_one_nested
|
14
|
+
has_many_nested
|
15
|
+
subtype subtypes
|
16
|
+
collection_methods
|
17
|
+
validate validates validates! validates_with validates_each
|
18
|
+
validates_absence_of
|
19
|
+
validates_acceptance_of
|
20
|
+
validates_confirmation_of
|
21
|
+
validates_exclusion_of
|
22
|
+
validates_format_of
|
23
|
+
validates_inclusion_of
|
24
|
+
validates_length_of
|
25
|
+
validates_numericality_of
|
26
|
+
validates_presence_of
|
27
|
+
validates_size_of
|
28
|
+
after_initialize
|
29
|
+
before_validation after_validation
|
30
|
+
].freeze.each do |meth|
|
31
|
+
define_method(meth) do |*args, &block|
|
32
|
+
@macros << [meth, args, block]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def apply_to(mod_or_class)
|
37
|
+
macros = @macros
|
38
|
+
mod_or_class.module_eval do
|
39
|
+
macros.each do |meth, args, block|
|
40
|
+
public_send(meth, *args, &block)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -5,12 +5,14 @@ class NestedRecord::Methods < Module
|
|
5
5
|
|
6
6
|
def define(name)
|
7
7
|
method_name = public_send("#{name}_method_name")
|
8
|
-
method_body =
|
8
|
+
method_body = (bodym = public_method("#{name}_method_body")).call
|
9
9
|
case method_body
|
10
10
|
when Proc
|
11
11
|
define_method(method_name, &method_body)
|
12
12
|
when String
|
13
|
-
|
13
|
+
location = bodym.source_location
|
14
|
+
location[1] += 1
|
15
|
+
module_eval <<~RUBY, *location
|
14
16
|
def #{method_name}
|
15
17
|
#{method_body}
|
16
18
|
end
|
@@ -20,6 +22,10 @@ class NestedRecord::Methods < Module
|
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
25
|
+
def reader_method_name
|
26
|
+
@setup.name
|
27
|
+
end
|
28
|
+
|
23
29
|
def writer_method_name
|
24
30
|
:"#{@setup.name}="
|
25
31
|
end
|
@@ -2,6 +2,7 @@ class NestedRecord::Methods
|
|
2
2
|
class Many < self
|
3
3
|
def initialize(setup)
|
4
4
|
super
|
5
|
+
define :reader
|
5
6
|
define :writer
|
6
7
|
define :rewrite_attributes
|
7
8
|
define :upsert_attributes
|
@@ -13,13 +14,30 @@ class NestedRecord::Methods
|
|
13
14
|
:"validate_associated_records_for_#{@setup.name}"
|
14
15
|
end
|
15
16
|
|
17
|
+
def reader_method_body
|
18
|
+
setup = @setup
|
19
|
+
ivar = :"@_#{@setup.name}_collection_proxy"
|
20
|
+
proc do
|
21
|
+
instance_variable_get(ivar) || instance_variable_set(ivar, setup.collection_proxy_class.new(self))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
16
25
|
def writer_method_body
|
17
26
|
setup = @setup
|
18
27
|
proc do |records|
|
19
28
|
collection_class = setup.collection_class
|
20
|
-
|
21
|
-
|
22
|
-
|
29
|
+
if records.is_a? collection_class
|
30
|
+
collection = records.dup
|
31
|
+
else
|
32
|
+
collection = collection_class.new
|
33
|
+
records.each { |record| collection << record }
|
34
|
+
end
|
35
|
+
collection.group_by { |record| setup.primary_check(record.read_attribute('type')) }.each do |check, records|
|
36
|
+
next unless check
|
37
|
+
records.each do |record|
|
38
|
+
check.perform!(collection, record)
|
39
|
+
end
|
40
|
+
end
|
23
41
|
super(collection)
|
24
42
|
end
|
25
43
|
end
|
@@ -39,29 +57,17 @@ class NestedRecord::Methods
|
|
39
57
|
attributes = attributes.stringify_keys
|
40
58
|
next if setup.reject_if_proc&.call(attributes)
|
41
59
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
klass = setup.record_class.find_subtype(attributes['type'])
|
46
|
-
while !(pkey_attributes = klass.primary_key) && (klass < NestedRecord::Base)
|
47
|
-
klass = klass.superclass
|
48
|
-
end
|
49
|
-
unless pkey_attributes
|
50
|
-
raise NestedRecord::ConfigurationError, 'You should specify a primary_key when using :upsert strategy'
|
51
|
-
end
|
60
|
+
pkey_check = setup.primary_check(attributes['type'])
|
61
|
+
unless pkey_check
|
62
|
+
raise NestedRecord::ConfigurationError, 'You should specify a primary_key when using :upsert strategy'
|
52
63
|
end
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
value = type.cast(value)
|
58
|
-
end
|
59
|
-
key[name] = value
|
60
|
-
end
|
61
|
-
if (record = collection.find_by(key))
|
64
|
+
|
65
|
+
pkey = pkey_check.build_pkey(attributes)
|
66
|
+
|
67
|
+
if (record = collection.find_by(pkey))
|
62
68
|
record.assign_attributes(attributes)
|
63
69
|
else
|
64
|
-
collection.
|
70
|
+
collection.__build__(attributes)
|
65
71
|
end
|
66
72
|
end
|
67
73
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class NestedRecord::NestedAccessorsSetup
|
2
|
+
def initialize(owner, name, class_name: false, default: {}, &block)
|
3
|
+
raise ArgumentError, 'block is required for .nested_accessors_in' unless block
|
4
|
+
|
5
|
+
recorder = NestedRecord::MacroRecorder.new
|
6
|
+
recorder.instance_eval(&block)
|
7
|
+
|
8
|
+
@has_one_setup = owner.has_one_nested(name, class_name: class_name, default: default, attributes_writer: { strategy: :rewrite }) do
|
9
|
+
recorder.apply_to(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
@extension = Module.new
|
13
|
+
|
14
|
+
macros = [
|
15
|
+
recorder,
|
16
|
+
*recorder.macros.select do |macro, args, _|
|
17
|
+
macro == :include && args.first.is_a?(NestedRecord::Concern)
|
18
|
+
end.map! { |_, args, _| args.first.macro_recorder }
|
19
|
+
].flat_map(&:macros)
|
20
|
+
|
21
|
+
macros.each do |macro, args, _block|
|
22
|
+
case macro
|
23
|
+
when :attribute
|
24
|
+
attr_name = args.first
|
25
|
+
delegate(attr_name)
|
26
|
+
delegate("#{attr_name}?")
|
27
|
+
delegate1("#{attr_name}=")
|
28
|
+
when :has_one_nested
|
29
|
+
assoc_name = args.first
|
30
|
+
delegate(assoc_name)
|
31
|
+
delegate("#{assoc_name}!")
|
32
|
+
delegate1("#{assoc_name}=")
|
33
|
+
delegate1("#{assoc_name}_attributes=")
|
34
|
+
when :has_many_nested
|
35
|
+
assoc_name = args.first
|
36
|
+
delegate(assoc_name)
|
37
|
+
delegate1("#{assoc_name}=")
|
38
|
+
delegate1("#{assoc_name}_attributes=")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
owner.include(@extension)
|
43
|
+
end
|
44
|
+
|
45
|
+
def name
|
46
|
+
@has_one_setup.name
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def delegate(meth)
|
52
|
+
@extension.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
53
|
+
def #{meth}
|
54
|
+
#{name}!.#{meth}
|
55
|
+
end
|
56
|
+
RUBY
|
57
|
+
end
|
58
|
+
|
59
|
+
def delegate1(meth)
|
60
|
+
@extension.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
61
|
+
def #{meth}(arg)
|
62
|
+
#{name}!.#{meth}(arg)
|
63
|
+
end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class NestedRecord::PrimaryKeyCheck
|
2
|
+
def initialize(klass, pkey_attributes)
|
3
|
+
@klass = klass
|
4
|
+
@pkey_attributes = pkey_attributes
|
5
|
+
@params = [klass, pkey_attributes]
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :params
|
9
|
+
|
10
|
+
def hash
|
11
|
+
params.hash
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
self.class === other && params == other.params
|
16
|
+
end
|
17
|
+
alias eql? ==
|
18
|
+
|
19
|
+
def build_pkey(obj)
|
20
|
+
pkey = { _is_a?: @klass }
|
21
|
+
if obj.is_a? @klass
|
22
|
+
pkey[:_not_equal?] = obj
|
23
|
+
@pkey_attributes.each do |name|
|
24
|
+
pkey[name] = obj.read_attribute(name)
|
25
|
+
end
|
26
|
+
elsif obj.respond_to? :[]
|
27
|
+
@pkey_attributes.each do |name|
|
28
|
+
value = obj[name]
|
29
|
+
if (type = @klass.type_for_attribute(name))
|
30
|
+
value = type.cast(value)
|
31
|
+
end
|
32
|
+
pkey[name] = value
|
33
|
+
end
|
34
|
+
else
|
35
|
+
fail
|
36
|
+
end
|
37
|
+
pkey
|
38
|
+
end
|
39
|
+
|
40
|
+
def perform!(collection, obj)
|
41
|
+
pkey = build_pkey(obj)
|
42
|
+
raise NestedRecord::PrimaryKeyError if collection.exists?(pkey)
|
43
|
+
end
|
44
|
+
end
|
data/lib/nested_record/setup.rb
CHANGED
@@ -8,53 +8,10 @@ class NestedRecord::Setup
|
|
8
8
|
@owner = owner
|
9
9
|
@name = name
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
cn = cn.singularize if self.is_a?(HasMany)
|
16
|
-
class_name = cn
|
17
|
-
when false
|
18
|
-
class_name = nil
|
19
|
-
when String, Symbol
|
20
|
-
class_name = cn.to_s
|
21
|
-
else
|
22
|
-
raise NestedRecord::ConfigurationError, "Bad :class_name option #{cn.inspect}"
|
23
|
-
end
|
24
|
-
@record_class = Class.new(NestedRecord::Base, &block)
|
25
|
-
@owner.const_set(class_name, @record_class) if class_name
|
26
|
-
else
|
27
|
-
if options.key? :class_name
|
28
|
-
case (cn = options.fetch(:class_name))
|
29
|
-
when String, Symbol
|
30
|
-
@record_class = cn.to_s
|
31
|
-
else
|
32
|
-
raise NestedRecord::ConfigurationError, "Bad :class_name option #{cn.inspect}"
|
33
|
-
end
|
34
|
-
else
|
35
|
-
cn = name.to_s.camelize
|
36
|
-
cn = cn.singularize if self.is_a?(HasMany)
|
37
|
-
@record_class = cn
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
case (aw = options.fetch(:attributes_writer) { {} })
|
42
|
-
when Hash
|
43
|
-
@attributes_writer_opts = aw
|
44
|
-
when true, false
|
45
|
-
@attributes_writer_opts = {}
|
46
|
-
when Symbol
|
47
|
-
@attributes_writer_opts = { strategy: aw }
|
48
|
-
else
|
49
|
-
raise NestedRecord::ConfigurationError, "Bad :attributes_writer option #{aw.inspect}"
|
50
|
-
end
|
51
|
-
@reject_if_proc = @attributes_writer_opts[:reject_if]
|
52
|
-
|
53
|
-
@methods_extension = build_methods_extension
|
54
|
-
|
55
|
-
@owner.attribute @name, type, default: default_value
|
56
|
-
@owner.include @methods_extension
|
57
|
-
@owner.validate @methods_extension.validation_method_name
|
11
|
+
setup_association_attribute
|
12
|
+
setup_record_class(&block)
|
13
|
+
setup_attributes_writer_opts
|
14
|
+
setup_methods_extension
|
58
15
|
end
|
59
16
|
|
60
17
|
def record_class
|
@@ -66,6 +23,7 @@ class NestedRecord::Setup
|
|
66
23
|
|
67
24
|
def primary_key
|
68
25
|
return @primary_key if defined? @primary_key
|
26
|
+
|
69
27
|
@primary_key = Array(@options[:primary_key])
|
70
28
|
if @primary_key.empty?
|
71
29
|
@primary_key = nil
|
@@ -77,6 +35,7 @@ class NestedRecord::Setup
|
|
77
35
|
|
78
36
|
def attributes_writer_strategy
|
79
37
|
return unless @options.fetch(:attributes_writer) { true }
|
38
|
+
|
80
39
|
case (strategy = @attributes_writer_opts.fetch(:strategy) { :upsert })
|
81
40
|
when :rewrite, :upsert
|
82
41
|
return strategy
|
@@ -85,8 +44,92 @@ class NestedRecord::Setup
|
|
85
44
|
end
|
86
45
|
end
|
87
46
|
|
47
|
+
def primary_check(type)
|
48
|
+
if (pkey_attributes = primary_key)
|
49
|
+
klass = record_class
|
50
|
+
else
|
51
|
+
klass = record_class.find_subtype(type)
|
52
|
+
while !(pkey_attributes = klass.primary_key) && (klass < NestedRecord::Base)
|
53
|
+
klass = klass.superclass
|
54
|
+
end
|
55
|
+
end
|
56
|
+
# TODO: cache this
|
57
|
+
NestedRecord::PrimaryKeyCheck.new(klass, pkey_attributes) if pkey_attributes
|
58
|
+
end
|
59
|
+
|
88
60
|
private
|
89
61
|
|
62
|
+
def setup_association_attribute
|
63
|
+
@owner.attribute name, type, default: default_value
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup_record_class(&block)
|
67
|
+
if block
|
68
|
+
define_local_record_class(&block)
|
69
|
+
else
|
70
|
+
link_existing_record_class
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def define_local_record_class(&block)
|
75
|
+
case (cn = @options.fetch(:class_name) { false })
|
76
|
+
when true
|
77
|
+
class_name = infer_record_class_name
|
78
|
+
when false
|
79
|
+
class_name = nil
|
80
|
+
when String, Symbol
|
81
|
+
class_name = cn.to_s
|
82
|
+
else
|
83
|
+
raise NestedRecord::ConfigurationError, "Bad :class_name option #{cn.inspect}"
|
84
|
+
end
|
85
|
+
@record_class = Class.new(NestedRecord::Base, &block)
|
86
|
+
@owner.const_set(class_name, @record_class) if class_name
|
87
|
+
end
|
88
|
+
|
89
|
+
def link_existing_record_class
|
90
|
+
if @options.key? :class_name
|
91
|
+
case (cn = @options.fetch(:class_name))
|
92
|
+
when String, Symbol
|
93
|
+
@record_class = cn.to_s
|
94
|
+
else
|
95
|
+
raise NestedRecord::ConfigurationError, "Bad :class_name option #{cn.inspect}"
|
96
|
+
end
|
97
|
+
else
|
98
|
+
@record_class = infer_record_class_name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def infer_record_class_name
|
103
|
+
cn = name.to_s.camelize
|
104
|
+
cn = cn.singularize if self.is_a?(HasMany)
|
105
|
+
cn
|
106
|
+
end
|
107
|
+
|
108
|
+
def setup_attributes_writer_opts
|
109
|
+
case (aw = @options.fetch(:attributes_writer) { {} })
|
110
|
+
when Hash
|
111
|
+
@attributes_writer_opts = aw
|
112
|
+
when true, false
|
113
|
+
@attributes_writer_opts = {}
|
114
|
+
when Symbol
|
115
|
+
@attributes_writer_opts = { strategy: aw }
|
116
|
+
else
|
117
|
+
raise NestedRecord::ConfigurationError, "Bad :attributes_writer option #{aw.inspect}"
|
118
|
+
end
|
119
|
+
@reject_if_proc = @attributes_writer_opts[:reject_if]
|
120
|
+
end
|
121
|
+
|
122
|
+
def setup_methods_extension
|
123
|
+
methods_extension = build_methods_extension
|
124
|
+
@owner.include methods_extension
|
125
|
+
@owner.const_set methods_extension_module_name, methods_extension
|
126
|
+
@owner.validate methods_extension.validation_method_name
|
127
|
+
end
|
128
|
+
|
129
|
+
def methods_extension_module_name
|
130
|
+
@methods_extension_module_name ||= :"NestedRecord_#{self.class.name.demodulize}_#{name.to_s.camelize}"
|
131
|
+
end
|
132
|
+
|
90
133
|
class HasMany < self
|
91
134
|
def type
|
92
135
|
@type ||= NestedRecord::Type::Many.new(self)
|
@@ -96,10 +139,23 @@ class NestedRecord::Setup
|
|
96
139
|
record_class.collection_class
|
97
140
|
end
|
98
141
|
|
142
|
+
def collection_proxy_class
|
143
|
+
return @owner.const_get(collection_proxy_class_name, false) if @owner.const_defined?(collection_proxy_class_name, false)
|
144
|
+
|
145
|
+
@owner.const_set(
|
146
|
+
collection_proxy_class_name,
|
147
|
+
::NestedRecord::CollectionProxy.subclass_for(self)
|
148
|
+
)
|
149
|
+
end
|
150
|
+
|
151
|
+
def collection_proxy_class_name
|
152
|
+
@collection_proxy_class_name ||= :"NestedRecord_#{self.class.name.demodulize}_#{name.to_s.camelize}_CollectionProxy"
|
153
|
+
end
|
154
|
+
|
99
155
|
private
|
100
156
|
|
101
157
|
def default_value
|
102
|
-
[]
|
158
|
+
@options.fetch(:default) { [] }
|
103
159
|
end
|
104
160
|
|
105
161
|
def build_methods_extension
|
@@ -115,7 +171,7 @@ class NestedRecord::Setup
|
|
115
171
|
private
|
116
172
|
|
117
173
|
def default_value
|
118
|
-
nil
|
174
|
+
@options.fetch(:default) { nil }
|
119
175
|
end
|
120
176
|
|
121
177
|
def build_methods_extension
|
data/nested_record.gemspec
CHANGED
@@ -257,6 +257,33 @@ RSpec.describe NestedRecord::Base do
|
|
257
257
|
end
|
258
258
|
end
|
259
259
|
|
260
|
+
describe '#query_attribute' do
|
261
|
+
it 'returns true if boolean attribute is true' do
|
262
|
+
expect(Foo.new(z: true).query_attribute(:z)).to eq true
|
263
|
+
end
|
264
|
+
|
265
|
+
it 'returns true if boolean attribute is false' do
|
266
|
+
expect(Foo.new(z: false).query_attribute(:z)).to eq false
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'returns true if string attribute is non-blank' do
|
270
|
+
expect(Foo.new(x: 'a').query_attribute(:x)).to eq true
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'returns false if string attribute is blank' do
|
274
|
+
expect(Foo.new(x: '').query_attribute(:x)).to eq false
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'works with suffix ? version' do
|
278
|
+
foo = Foo.new(x: '1', z: false)
|
279
|
+
expect(foo.x?).to eq true
|
280
|
+
expect(foo.z?).to eq false
|
281
|
+
foo = Foo.new(x: '', z: true)
|
282
|
+
expect(foo.x?).to eq false
|
283
|
+
expect(foo.z?).to eq true
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
260
287
|
describe '#match?' do
|
261
288
|
let(:record) { Foo.new(x: 'aa', y: 123, z: true) }
|
262
289
|
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe NestedRecord::Concern do
|
4
|
+
nested_concern(:Y) do
|
5
|
+
attribute :y, :integer
|
6
|
+
has_many_nested :ys, attributes_writer: { strategy: :rewrite } do
|
7
|
+
attribute :yy
|
8
|
+
end
|
9
|
+
end
|
10
|
+
nested_model(:XY) do
|
11
|
+
attribute :x, :string
|
12
|
+
include Y
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'adds attributes to the class where it is included' do
|
16
|
+
expect(XY.new(x: 'foo', y: 123, ys_attributes: [{ yy: 'yy' }])).to match an_object_having_attributes(
|
17
|
+
x: 'foo',
|
18
|
+
y: 123,
|
19
|
+
ys: [
|
20
|
+
an_object_having_attributes(yy: 'yy')
|
21
|
+
]
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'with nested concerns' do
|
26
|
+
nested_concern(:YZ) do
|
27
|
+
include Y
|
28
|
+
attribute :z, :boolean
|
29
|
+
end
|
30
|
+
nested_model(:XYZ) do
|
31
|
+
attribute :x, :string
|
32
|
+
include YZ
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'adds attributes to the class where it is included' do
|
36
|
+
expect(XYZ.new(x: 'foo', y: 123, z: true, ys_attributes: [{ yy: 'yy' }])).to match an_object_having_attributes(
|
37
|
+
x: 'foo',
|
38
|
+
y: 123,
|
39
|
+
z: true,
|
40
|
+
ys: [
|
41
|
+
an_object_having_attributes(yy: 'yy')
|
42
|
+
]
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/spec/nested_record_spec.rb
CHANGED
@@ -325,6 +325,27 @@ RSpec.describe NestedRecord do
|
|
325
325
|
foo.bars = []
|
326
326
|
expect(foo.bars).to be_empty
|
327
327
|
end
|
328
|
+
|
329
|
+
it 'raises an error if primary key is violated' do
|
330
|
+
foo = Foo.new
|
331
|
+
expect { foo.bars = [Bar.new(id: 'wow'), Bar.new(id: 'wow')] }.to raise_error(NestedRecord::PrimaryKeyError)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe 'build method' do
|
336
|
+
it 'builds a new record' do
|
337
|
+
foo = Foo.new
|
338
|
+
foo.bars.build(x: 'xx')
|
339
|
+
expect(foo.bars).to match [
|
340
|
+
an_object_having_attributes(x: 'xx')
|
341
|
+
]
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'raises an error if primary key is violated' do
|
345
|
+
foo = Foo.new
|
346
|
+
foo.bars.build(id: 'wow')
|
347
|
+
expect { foo.bars.build(id: 'wow') }.to raise_error(NestedRecord::PrimaryKeyError)
|
348
|
+
end
|
328
349
|
end
|
329
350
|
|
330
351
|
describe 'attributes writer' do
|
@@ -632,4 +653,109 @@ RSpec.describe NestedRecord do
|
|
632
653
|
end
|
633
654
|
end
|
634
655
|
end
|
656
|
+
|
657
|
+
describe 'nested_accessors' do
|
658
|
+
active_model(:Foo) do
|
659
|
+
nested_accessors from: :bar, class_name: true do
|
660
|
+
attribute :x, :integer
|
661
|
+
has_one_nested :one, class_name: true do
|
662
|
+
attribute :y, :integer
|
663
|
+
end
|
664
|
+
has_many_nested :things, class_name: true, attributes_writer: { strategy: :rewrite } do
|
665
|
+
attribute :z, :integer
|
666
|
+
end
|
667
|
+
end
|
668
|
+
end
|
669
|
+
|
670
|
+
it 'delegates attributes writers to the inner attribute' do
|
671
|
+
foo = Foo.new(
|
672
|
+
x: 1,
|
673
|
+
one_attributes: { y: 2 },
|
674
|
+
things_attributes: [{ z: 3 }, { z: 4 }]
|
675
|
+
)
|
676
|
+
expect(foo.bar).to match an_object_having_attributes(
|
677
|
+
x: 1,
|
678
|
+
one: an_object_having_attributes(
|
679
|
+
y: 2
|
680
|
+
),
|
681
|
+
things: [
|
682
|
+
an_object_having_attributes(z: 3),
|
683
|
+
an_object_having_attributes(z: 4)
|
684
|
+
]
|
685
|
+
)
|
686
|
+
end
|
687
|
+
|
688
|
+
it 'delegates readers to the inner attribute' do
|
689
|
+
foo = Foo.new(
|
690
|
+
x: 1,
|
691
|
+
one_attributes: { y: 2 },
|
692
|
+
things_attributes: [{ z: 3 }, { z: 4 }]
|
693
|
+
)
|
694
|
+
expect(foo).to match an_object_having_attributes(
|
695
|
+
x: 1,
|
696
|
+
one: an_object_having_attributes(
|
697
|
+
y: 2
|
698
|
+
),
|
699
|
+
things: [
|
700
|
+
an_object_having_attributes(z: 3),
|
701
|
+
an_object_having_attributes(z: 4)
|
702
|
+
]
|
703
|
+
)
|
704
|
+
end
|
705
|
+
|
706
|
+
it 'delegates writers for associations' do
|
707
|
+
foo = Foo.new(x: 1)
|
708
|
+
foo.one = Foo::Bar::One.new(y: 2)
|
709
|
+
foo.things = [Foo::Bar::Thing.new(z: 3), Foo::Bar::Thing.new(z: 4)]
|
710
|
+
expect(foo.bar).to match an_object_having_attributes(
|
711
|
+
x: 1,
|
712
|
+
one: an_object_having_attributes(
|
713
|
+
y: 2
|
714
|
+
),
|
715
|
+
things: [
|
716
|
+
an_object_having_attributes(z: 3),
|
717
|
+
an_object_having_attributes(z: 4)
|
718
|
+
]
|
719
|
+
)
|
720
|
+
end
|
721
|
+
|
722
|
+
context 'with concerns' do
|
723
|
+
nested_concern(:X) { attribute :x, :integer }
|
724
|
+
nested_concern(:Y) do
|
725
|
+
has_one_nested :one, class_name: true do
|
726
|
+
attribute :y, :integer
|
727
|
+
end
|
728
|
+
end
|
729
|
+
nested_concern(:Z) do
|
730
|
+
has_many_nested :things, class_name: true, attributes_writer: { strategy: :rewrite } do
|
731
|
+
attribute :z, :integer
|
732
|
+
end
|
733
|
+
end
|
734
|
+
active_model(:FooXYZ) do
|
735
|
+
nested_accessors from: :bar, class_name: true do
|
736
|
+
include X
|
737
|
+
include Y
|
738
|
+
include Z
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
it 'delegates methods from included modules' do
|
743
|
+
foo = FooXYZ.new(
|
744
|
+
x: 1,
|
745
|
+
one_attributes: { y: 2 },
|
746
|
+
things_attributes: [{ z: 3 }, { z: 4 }]
|
747
|
+
)
|
748
|
+
expect(foo).to match an_object_having_attributes(
|
749
|
+
x: 1,
|
750
|
+
one: an_object_having_attributes(
|
751
|
+
y: 2
|
752
|
+
),
|
753
|
+
things: [
|
754
|
+
an_object_having_attributes(z: 3),
|
755
|
+
an_object_having_attributes(z: 4)
|
756
|
+
]
|
757
|
+
)
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
635
761
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/model.rb
CHANGED
@@ -5,18 +5,33 @@ module TestModel
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
def nested_concern(name, &block)
|
9
|
+
let!(:"concern_#{name.to_s.gsub('::', '_')}") do
|
10
|
+
test_consts = (@test_consts ||= [])
|
11
|
+
namespace, sname = test_const_dig_name!(name)
|
12
|
+
Module.new do
|
13
|
+
extend NestedRecord::Concern
|
14
|
+
|
15
|
+
namespace.const_set(sname, self)
|
16
|
+
test_consts << self
|
17
|
+
|
18
|
+
class_eval(&block) if block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
8
23
|
def nested_model(name, superclass = NestedRecord::Base, &block)
|
9
24
|
let!(:"model_#{name.to_s.gsub('::', '_')}") do
|
10
|
-
|
25
|
+
test_consts = (@test_consts ||= [])
|
11
26
|
if superclass.is_a?(Symbol) || superclass.is_a?(String)
|
12
27
|
sclass = public_send("model_#{superclass.to_s.gsub('::', '_')}")
|
13
28
|
else
|
14
29
|
sclass = superclass
|
15
30
|
end
|
16
|
-
namespace, sname =
|
31
|
+
namespace, sname = test_const_dig_name!(name)
|
17
32
|
Class.new(sclass) do
|
18
33
|
namespace.const_set(sname, self)
|
19
|
-
|
34
|
+
test_consts << self
|
20
35
|
|
21
36
|
class_eval(&block) if block
|
22
37
|
end
|
@@ -25,32 +40,36 @@ module TestModel
|
|
25
40
|
|
26
41
|
module Build
|
27
42
|
def new_active_model(name, &block)
|
28
|
-
|
43
|
+
test_consts = (@test_consts ||= [])
|
29
44
|
|
30
|
-
namespace, sname =
|
45
|
+
namespace, sname = test_const_dig_name!(name)
|
31
46
|
|
32
47
|
Class.new do
|
33
48
|
namespace.const_set(sname, self)
|
34
|
-
|
49
|
+
test_consts << self
|
35
50
|
|
36
51
|
include ActiveModel::Model
|
37
52
|
include ActiveModel::Attributes
|
38
53
|
include NestedRecord::Macro
|
39
54
|
|
55
|
+
def read_attribute(attr)
|
56
|
+
attribute(attr)
|
57
|
+
end
|
58
|
+
|
40
59
|
class_eval(&block) if block
|
41
60
|
end
|
42
61
|
end
|
43
62
|
end
|
44
63
|
|
45
64
|
module Erase
|
46
|
-
def
|
47
|
-
Array(@
|
65
|
+
def erase_test_consts
|
66
|
+
Array(@test_consts).reverse_each do |klass|
|
48
67
|
ActiveSupport::Dependencies.remove_constant(klass.name)
|
49
68
|
end
|
50
69
|
ActiveSupport::Dependencies.clear
|
51
70
|
end
|
52
71
|
|
53
|
-
def
|
72
|
+
def test_const_dig_name!(name)
|
54
73
|
parts = name.to_s.split('::')
|
55
74
|
name = parts.pop
|
56
75
|
namespace = Object
|
@@ -60,7 +79,7 @@ module TestModel
|
|
60
79
|
namespace = namespace.const_get(part, false)
|
61
80
|
else
|
62
81
|
sub = Module.new
|
63
|
-
(@
|
82
|
+
(@test_consts ||= []) << sub
|
64
83
|
namespace = namespace.const_set(part, sub)
|
65
84
|
end
|
66
85
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nested_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vladimir Kochnev
|
@@ -84,16 +84,22 @@ dependencies:
|
|
84
84
|
name: rails
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '5.2'
|
90
|
+
- - "<="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '6.0'
|
90
93
|
type: :runtime
|
91
94
|
prerelease: false
|
92
95
|
version_requirements: !ruby/object:Gem::Requirement
|
93
96
|
requirements:
|
94
|
-
- - "
|
97
|
+
- - ">="
|
95
98
|
- !ruby/object:Gem::Version
|
96
99
|
version: '5.2'
|
100
|
+
- - "<="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '6.0'
|
97
103
|
description:
|
98
104
|
email:
|
99
105
|
- hashtable@yandex.ru
|
@@ -105,21 +111,27 @@ files:
|
|
105
111
|
- ".rspec"
|
106
112
|
- ".travis.yml"
|
107
113
|
- Gemfile
|
108
|
-
- Gemfile.lock
|
109
114
|
- LICENSE.txt
|
110
115
|
- README.md
|
111
116
|
- Rakefile
|
112
117
|
- bin/console
|
113
118
|
- bin/setup
|
119
|
+
- gemfiles/rails_5.2.gemfile
|
120
|
+
- gemfiles/rails_6.0.gemfile
|
114
121
|
- lib/nested_record.rb
|
115
122
|
- lib/nested_record/base.rb
|
116
123
|
- lib/nested_record/collection.rb
|
124
|
+
- lib/nested_record/collection_proxy.rb
|
125
|
+
- lib/nested_record/concern.rb
|
117
126
|
- lib/nested_record/errors.rb
|
118
127
|
- lib/nested_record/lookup_const.rb
|
119
128
|
- lib/nested_record/macro.rb
|
129
|
+
- lib/nested_record/macro_recorder.rb
|
120
130
|
- lib/nested_record/methods.rb
|
121
131
|
- lib/nested_record/methods/many.rb
|
122
132
|
- lib/nested_record/methods/one.rb
|
133
|
+
- lib/nested_record/nested_accessors_setup.rb
|
134
|
+
- lib/nested_record/primary_key_check.rb
|
123
135
|
- lib/nested_record/setup.rb
|
124
136
|
- lib/nested_record/type.rb
|
125
137
|
- lib/nested_record/type/many.rb
|
@@ -128,6 +140,7 @@ files:
|
|
128
140
|
- nested_record.gemspec
|
129
141
|
- spec/nested_record/base_spec.rb
|
130
142
|
- spec/nested_record/collection_spec.rb
|
143
|
+
- spec/nested_record/concern_spec.rb
|
131
144
|
- spec/nested_record/type/many_spec.rb
|
132
145
|
- spec/nested_record/type/one_spec.rb
|
133
146
|
- spec/nested_record_spec.rb
|
@@ -148,9 +161,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
161
|
version: '0'
|
149
162
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
163
|
requirements:
|
151
|
-
- - "
|
164
|
+
- - ">="
|
152
165
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
166
|
+
version: '0'
|
154
167
|
requirements: []
|
155
168
|
rubyforge_project:
|
156
169
|
rubygems_version: 2.7.6
|
data/Gemfile.lock
DELETED
@@ -1,152 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
nested_record (1.0.0.beta)
|
5
|
-
rails (~> 5.2)
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
actioncable (5.2.3)
|
11
|
-
actionpack (= 5.2.3)
|
12
|
-
nio4r (~> 2.0)
|
13
|
-
websocket-driver (>= 0.6.1)
|
14
|
-
actionmailer (5.2.3)
|
15
|
-
actionpack (= 5.2.3)
|
16
|
-
actionview (= 5.2.3)
|
17
|
-
activejob (= 5.2.3)
|
18
|
-
mail (~> 2.5, >= 2.5.4)
|
19
|
-
rails-dom-testing (~> 2.0)
|
20
|
-
actionpack (5.2.3)
|
21
|
-
actionview (= 5.2.3)
|
22
|
-
activesupport (= 5.2.3)
|
23
|
-
rack (~> 2.0)
|
24
|
-
rack-test (>= 0.6.3)
|
25
|
-
rails-dom-testing (~> 2.0)
|
26
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
27
|
-
actionview (5.2.3)
|
28
|
-
activesupport (= 5.2.3)
|
29
|
-
builder (~> 3.1)
|
30
|
-
erubi (~> 1.4)
|
31
|
-
rails-dom-testing (~> 2.0)
|
32
|
-
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
33
|
-
activejob (5.2.3)
|
34
|
-
activesupport (= 5.2.3)
|
35
|
-
globalid (>= 0.3.6)
|
36
|
-
activemodel (5.2.3)
|
37
|
-
activesupport (= 5.2.3)
|
38
|
-
activerecord (5.2.3)
|
39
|
-
activemodel (= 5.2.3)
|
40
|
-
activesupport (= 5.2.3)
|
41
|
-
arel (>= 9.0)
|
42
|
-
activestorage (5.2.3)
|
43
|
-
actionpack (= 5.2.3)
|
44
|
-
activerecord (= 5.2.3)
|
45
|
-
marcel (~> 0.3.1)
|
46
|
-
activesupport (5.2.3)
|
47
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
48
|
-
i18n (>= 0.7, < 2)
|
49
|
-
minitest (~> 5.1)
|
50
|
-
tzinfo (~> 1.1)
|
51
|
-
arel (9.0.0)
|
52
|
-
builder (3.2.3)
|
53
|
-
byebug (11.0.1)
|
54
|
-
coderay (1.1.2)
|
55
|
-
concurrent-ruby (1.1.5)
|
56
|
-
crass (1.0.5)
|
57
|
-
diff-lcs (1.3)
|
58
|
-
erubi (1.9.0)
|
59
|
-
globalid (0.4.2)
|
60
|
-
activesupport (>= 4.2.0)
|
61
|
-
i18n (1.7.0)
|
62
|
-
concurrent-ruby (~> 1.0)
|
63
|
-
loofah (2.3.1)
|
64
|
-
crass (~> 1.0.2)
|
65
|
-
nokogiri (>= 1.5.9)
|
66
|
-
mail (2.7.1)
|
67
|
-
mini_mime (>= 0.1.1)
|
68
|
-
marcel (0.3.3)
|
69
|
-
mimemagic (~> 0.3.2)
|
70
|
-
method_source (0.9.2)
|
71
|
-
mimemagic (0.3.3)
|
72
|
-
mini_mime (1.0.2)
|
73
|
-
mini_portile2 (2.4.0)
|
74
|
-
minitest (5.13.0)
|
75
|
-
nio4r (2.5.2)
|
76
|
-
nokogiri (1.10.4)
|
77
|
-
mini_portile2 (~> 2.4.0)
|
78
|
-
pry (0.12.2)
|
79
|
-
coderay (~> 1.1.0)
|
80
|
-
method_source (~> 0.9.0)
|
81
|
-
pry-byebug (3.7.0)
|
82
|
-
byebug (~> 11.0)
|
83
|
-
pry (~> 0.10)
|
84
|
-
rack (2.0.7)
|
85
|
-
rack-test (1.1.0)
|
86
|
-
rack (>= 1.0, < 3)
|
87
|
-
rails (5.2.3)
|
88
|
-
actioncable (= 5.2.3)
|
89
|
-
actionmailer (= 5.2.3)
|
90
|
-
actionpack (= 5.2.3)
|
91
|
-
actionview (= 5.2.3)
|
92
|
-
activejob (= 5.2.3)
|
93
|
-
activemodel (= 5.2.3)
|
94
|
-
activerecord (= 5.2.3)
|
95
|
-
activestorage (= 5.2.3)
|
96
|
-
activesupport (= 5.2.3)
|
97
|
-
bundler (>= 1.3.0)
|
98
|
-
railties (= 5.2.3)
|
99
|
-
sprockets-rails (>= 2.0.0)
|
100
|
-
rails-dom-testing (2.0.3)
|
101
|
-
activesupport (>= 4.2.0)
|
102
|
-
nokogiri (>= 1.6)
|
103
|
-
rails-html-sanitizer (1.3.0)
|
104
|
-
loofah (~> 2.3)
|
105
|
-
railties (5.2.3)
|
106
|
-
actionpack (= 5.2.3)
|
107
|
-
activesupport (= 5.2.3)
|
108
|
-
method_source
|
109
|
-
rake (>= 0.8.7)
|
110
|
-
thor (>= 0.19.0, < 2.0)
|
111
|
-
rake (10.5.0)
|
112
|
-
rspec (3.8.0)
|
113
|
-
rspec-core (~> 3.8.0)
|
114
|
-
rspec-expectations (~> 3.8.0)
|
115
|
-
rspec-mocks (~> 3.8.0)
|
116
|
-
rspec-core (3.8.1)
|
117
|
-
rspec-support (~> 3.8.0)
|
118
|
-
rspec-expectations (3.8.4)
|
119
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
120
|
-
rspec-support (~> 3.8.0)
|
121
|
-
rspec-mocks (3.8.1)
|
122
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
123
|
-
rspec-support (~> 3.8.0)
|
124
|
-
rspec-support (3.8.2)
|
125
|
-
sprockets (4.0.0)
|
126
|
-
concurrent-ruby (~> 1.0)
|
127
|
-
rack (> 1, < 3)
|
128
|
-
sprockets-rails (3.2.1)
|
129
|
-
actionpack (>= 4.0)
|
130
|
-
activesupport (>= 4.0)
|
131
|
-
sprockets (>= 3.0.0)
|
132
|
-
thor (0.20.3)
|
133
|
-
thread_safe (0.3.6)
|
134
|
-
tzinfo (1.2.5)
|
135
|
-
thread_safe (~> 0.1)
|
136
|
-
websocket-driver (0.7.1)
|
137
|
-
websocket-extensions (>= 0.1.0)
|
138
|
-
websocket-extensions (0.1.4)
|
139
|
-
|
140
|
-
PLATFORMS
|
141
|
-
ruby
|
142
|
-
|
143
|
-
DEPENDENCIES
|
144
|
-
bundler (~> 2.0)
|
145
|
-
nested_record!
|
146
|
-
pry
|
147
|
-
pry-byebug
|
148
|
-
rake (~> 10.0)
|
149
|
-
rspec (~> 3.0)
|
150
|
-
|
151
|
-
BUNDLED WITH
|
152
|
-
2.0.1
|