nested_record 1.0.0.beta → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|