contentful_model 0.2.0 → 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 +5 -5
- data/lib/contentful_model.rb +17 -13
- data/lib/contentful_model/asset.rb +30 -0
- data/lib/contentful_model/asset_dimension_query.rb +70 -0
- data/lib/contentful_model/asset_dimensions.rb +54 -0
- data/lib/contentful_model/associations/associations.rb +10 -4
- data/lib/contentful_model/associations/belongs_to.rb +9 -4
- data/lib/contentful_model/associations/belongs_to_many.rb +22 -45
- data/lib/contentful_model/associations/has_many.rb +15 -13
- data/lib/contentful_model/associations/has_many_nested.rb +39 -37
- data/lib/contentful_model/associations/has_one.rb +22 -14
- data/lib/contentful_model/base.rb +115 -74
- data/lib/contentful_model/client.rb +14 -3
- data/lib/contentful_model/errors.rb +0 -1
- data/lib/contentful_model/manageable.rb +49 -21
- data/lib/contentful_model/management.rb +1 -0
- data/lib/contentful_model/migrations/content_type.rb +24 -24
- data/lib/contentful_model/migrations/content_type_factory.rb +9 -6
- data/lib/contentful_model/migrations/migration.rb +9 -4
- data/lib/contentful_model/queries.rb +60 -9
- data/lib/contentful_model/query.rb +148 -5
- data/lib/contentful_model/validations/lambda_validation.rb +17 -0
- data/lib/contentful_model/validations/validates_presence_of.rb +4 -1
- data/lib/contentful_model/validations/validations.rb +56 -8
- data/lib/contentful_model/version.rb +1 -1
- data/spec/asset_spec.rb +141 -0
- data/spec/associations/belongs_to_many_spec.rb +38 -0
- data/spec/associations/belongs_to_spec.rb +22 -0
- data/spec/associations/has_many_nested_spec.rb +321 -0
- data/spec/associations/has_many_spec.rb +33 -0
- data/spec/associations/has_one_spec.rb +32 -0
- data/spec/base_spec.rb +199 -0
- data/spec/chainable_queries_spec.rb +199 -0
- data/spec/client_spec.rb +11 -0
- data/spec/contentful_model_spec.rb +25 -0
- data/spec/fixtures/vcr_cassettes/asset/all.yml +161 -0
- data/spec/fixtures/vcr_cassettes/asset/find.yml +118 -0
- data/spec/fixtures/vcr_cassettes/association/belongs_to_many.yml +439 -0
- data/spec/fixtures/vcr_cassettes/association/has_many.yml +161 -0
- data/spec/fixtures/vcr_cassettes/association/has_one.yml +161 -0
- data/spec/fixtures/vcr_cassettes/association/nested_with_root_root.yml +240 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_child_higher_include.yml +123 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_child_parent.yml +609 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_childless.yml +688 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_middle.yml +319 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_middle_higher_include.yml +161 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_middle_parent.yml +489 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_parentless.yml +331 -0
- data/spec/fixtures/vcr_cassettes/association/nested_without_root_parentless_higher_include.yml +82 -0
- data/spec/fixtures/vcr_cassettes/associations/nested_without_root_middle.yml +161 -0
- data/spec/fixtures/vcr_cassettes/associations/nested_without_root_middle_higher_include.yml +82 -0
- data/spec/fixtures/vcr_cassettes/base/content_type.yml +185 -0
- data/spec/fixtures/vcr_cassettes/base/return_nil_for_empty.yml +202 -0
- data/spec/fixtures/vcr_cassettes/base/return_nil_for_empty_with_value.yml +124 -0
- data/spec/fixtures/vcr_cassettes/client.yml +98 -0
- data/spec/fixtures/vcr_cassettes/dog.yml +161 -0
- data/spec/fixtures/vcr_cassettes/human.yml +199 -0
- data/spec/fixtures/vcr_cassettes/management/client.yml +1362 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat.yml +1449 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat_2.yml +1449 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat_publish.yml +481 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat_refetch_and_fail.yml +558 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat_refetch_and_save.yml +620 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat_save.yml +479 -0
- data/spec/fixtures/vcr_cassettes/management/nyancat_save_2.yml +479 -0
- data/spec/fixtures/vcr_cassettes/nyancat.yml +196 -0
- data/spec/fixtures/vcr_cassettes/playground/nyancat.yml +177 -0
- data/spec/fixtures/vcr_cassettes/query/each_entry.yml +319 -0
- data/spec/fixtures/vcr_cassettes/query/each_page.yml +319 -0
- data/spec/fixtures/vcr_cassettes/query/empty.yml +180 -0
- data/spec/fixtures/vcr_cassettes/query/load.yml +197 -0
- data/spec/fixtures/vcr_cassettes/query/manual_pagination.yml +161 -0
- data/spec/fixtures/vcr_cassettes/query/nyancat_invalid_elements.yml +247 -0
- data/spec/manageable_spec.rb +186 -0
- data/spec/management_spec.rb +17 -0
- data/spec/migrations/content_type_factory_spec.rb +41 -0
- data/spec/migrations/content_type_spec.rb +176 -0
- data/spec/migrations/migration_spec.rb +75 -0
- data/spec/queries_spec.rb +85 -0
- data/spec/query_spec.rb +126 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/validations/validations_spec.rb +182 -0
- metadata +213 -19
- data/lib/contentful_model/chainable_queries.rb +0 -104
@@ -0,0 +1,17 @@
|
|
1
|
+
module Contentful
|
2
|
+
module Validations
|
3
|
+
# Class to define lambda validations
|
4
|
+
class LambdaValidation
|
5
|
+
def initialize(name, fn)
|
6
|
+
@name = name
|
7
|
+
fail "#{name}: Validator function or Proc is required" unless fn.is_a?(Proc)
|
8
|
+
@validator = fn
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate(entry)
|
12
|
+
return ["#{@name}: validation not met"] unless @validator[entry]
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
1
|
module Contentful
|
2
2
|
module Validations
|
3
|
+
# Module to check presence of a field
|
3
4
|
module PresenceOf
|
4
5
|
def self.included(base)
|
5
6
|
base.extend(ClassMethods)
|
6
7
|
end
|
7
8
|
|
9
|
+
# Class method
|
8
10
|
module ClassMethods
|
9
11
|
def validates_presence_of(*args)
|
10
12
|
@validations ||= []
|
@@ -13,6 +15,7 @@ module Contentful
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
18
|
+
# Actual validation
|
16
19
|
class PresenceValidation
|
17
20
|
attr_reader :fields
|
18
21
|
|
@@ -24,7 +27,7 @@ module Contentful
|
|
24
27
|
errors = []
|
25
28
|
|
26
29
|
fields.each do |field|
|
27
|
-
errors << "#{field} is required" unless entry.respond_to?(field)
|
30
|
+
errors << "#{field} is required" unless entry.respond_to?(field) && entry.public_send(field).present?
|
28
31
|
end
|
29
32
|
|
30
33
|
errors
|
@@ -1,6 +1,10 @@
|
|
1
|
+
require_relative 'validates_presence_of'
|
2
|
+
require_relative 'lambda_validation'
|
3
|
+
|
1
4
|
# A module to validate entries. A little like ActiveRecord. We don't think this should be necessary, really,
|
2
5
|
# because Contentful should be doing the validating, but they expose invalid entries through the preview API.
|
3
6
|
module ContentfulModel
|
7
|
+
# Defines general validation methods
|
4
8
|
module Validations
|
5
9
|
def self.included(base)
|
6
10
|
base.extend ClassMethods
|
@@ -8,23 +12,28 @@ module ContentfulModel
|
|
8
12
|
attr_reader :errors
|
9
13
|
end
|
10
14
|
|
11
|
-
def valid?
|
12
|
-
validate
|
15
|
+
def valid?(save = false)
|
16
|
+
validate(save)
|
13
17
|
end
|
14
18
|
|
15
|
-
def invalid?
|
16
|
-
!valid?
|
19
|
+
def invalid?(save = false)
|
20
|
+
!valid?(save)
|
17
21
|
end
|
18
22
|
|
19
|
-
def validate
|
23
|
+
def validate(save = false)
|
20
24
|
@errors = []
|
21
|
-
unless
|
25
|
+
unless respond_to?(:fields)
|
22
26
|
@errors.push("Entity doesn't respond to the fields() method")
|
23
27
|
return false
|
24
28
|
end
|
25
29
|
|
26
|
-
|
27
|
-
|
30
|
+
validation_kinds = [:validations]
|
31
|
+
validation_kinds << :save_validations if save
|
32
|
+
|
33
|
+
validation_kinds.each do |validation_kind|
|
34
|
+
validations = self.class.send(validation_kind)
|
35
|
+
next if validations.nil?
|
36
|
+
|
28
37
|
validations.each do |validation|
|
29
38
|
@errors += validation.validate(self)
|
30
39
|
end
|
@@ -33,10 +42,49 @@ module ContentfulModel
|
|
33
42
|
@errors.empty?
|
34
43
|
end
|
35
44
|
|
45
|
+
# Class methods for Validations
|
36
46
|
module ClassMethods
|
37
47
|
def validations
|
38
48
|
@validations
|
39
49
|
end
|
50
|
+
|
51
|
+
def save_validations
|
52
|
+
@save_validations
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_with(validation, on_load: false)
|
56
|
+
if validation.is_a?(Class)
|
57
|
+
validation = validation.new
|
58
|
+
elsif validation.respond_to?(:validate)
|
59
|
+
validation = validation
|
60
|
+
else
|
61
|
+
fail '::validate_with requires a Class or object that responds to #validate(entry)'
|
62
|
+
end
|
63
|
+
|
64
|
+
if on_load
|
65
|
+
@validations ||= []
|
66
|
+
@validations << validation
|
67
|
+
else
|
68
|
+
@save_validations ||= []
|
69
|
+
@save_validations << validation
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate(name, fn = nil, on_load: false, &block)
|
74
|
+
vs = []
|
75
|
+
fail '::validate requires either a function or a block sent as a validation' if fn.nil? && block.nil?
|
76
|
+
|
77
|
+
vs << ::Contentful::Validations::LambdaValidation.new(name, fn) unless fn.nil?
|
78
|
+
vs << ::Contentful::Validations::LambdaValidation.new(name, block) unless block.nil?
|
79
|
+
|
80
|
+
if on_load
|
81
|
+
@validations ||= []
|
82
|
+
@validations += vs
|
83
|
+
else
|
84
|
+
@save_validations ||= []
|
85
|
+
@save_validations += vs
|
86
|
+
end
|
87
|
+
end
|
40
88
|
end
|
41
89
|
end
|
42
90
|
end
|
data/spec/asset_spec.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ContentfulModel::Asset do
|
4
|
+
before :each do
|
5
|
+
ContentfulModel.configure do |config|
|
6
|
+
config.space = 'cfexampleapi'
|
7
|
+
config.access_token = 'b4c0n73n7fu1'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'class methods' do
|
12
|
+
it '.all' do
|
13
|
+
vcr('asset/all') {
|
14
|
+
assets = described_class.all
|
15
|
+
expect(assets).to be_a ::Contentful::Array
|
16
|
+
expect(assets.first).to be_a described_class
|
17
|
+
expect(assets.first.id).to eq 'nyancat'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
it '.find' do
|
22
|
+
vcr('asset/find') {
|
23
|
+
asset = described_class.find('nyancat')
|
24
|
+
expect(asset).to be_a described_class
|
25
|
+
expect(asset.id).to eq 'nyancat'
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'queries' do
|
31
|
+
it '#resize' do
|
32
|
+
vcr('asset/find') {
|
33
|
+
asset = described_class.find('nyancat')
|
34
|
+
url = asset.resize(10, 20).load
|
35
|
+
|
36
|
+
expect(url).to include('w=10')
|
37
|
+
expect(url).to include('h=20')
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
it '#width' do
|
42
|
+
vcr('asset/find') {
|
43
|
+
asset = described_class.find('nyancat')
|
44
|
+
url = asset.width(20).load
|
45
|
+
|
46
|
+
expect(url).to include('w=20')
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
it '#height' do
|
51
|
+
vcr('asset/find') {
|
52
|
+
asset = described_class.find('nyancat')
|
53
|
+
url = asset.height(10).load
|
54
|
+
|
55
|
+
expect(url).to include('h=10')
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
it '#format' do
|
60
|
+
vcr('asset/find') {
|
61
|
+
asset = described_class.find('nyancat')
|
62
|
+
url = asset.format('jpg').load
|
63
|
+
|
64
|
+
expect(url).to include('fm=jpg')
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
it '#jpeg_quality' do
|
69
|
+
vcr('asset/find') {
|
70
|
+
asset = described_class.find('nyancat')
|
71
|
+
url = asset.jpeg_quality(80).load
|
72
|
+
|
73
|
+
expect(url).to include('fm=jpg')
|
74
|
+
expect(url).to include('q=80')
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
it '#png_8bit' do
|
79
|
+
vcr('asset/find') {
|
80
|
+
asset = described_class.find('nyancat')
|
81
|
+
url = asset.png_8bit.load
|
82
|
+
|
83
|
+
expect(url).to include('fm=png')
|
84
|
+
expect(url).to include('fl=png8')
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
it '#resize_behavior' do
|
89
|
+
vcr('asset/find') {
|
90
|
+
asset = described_class.find('nyancat')
|
91
|
+
url = asset.resize_behavior('thumb').load
|
92
|
+
|
93
|
+
expect(url).to include('fit=thumb')
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
it '#thumbnail_focused_on' do
|
98
|
+
vcr('asset/find') {
|
99
|
+
asset = described_class.find('nyancat')
|
100
|
+
url = asset.thumbnail_focused_on('face').load
|
101
|
+
|
102
|
+
expect(url).to include('fit=thumb')
|
103
|
+
expect(url).to include('f=face')
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
it '#rounded_corners' do
|
108
|
+
vcr('asset/find') {
|
109
|
+
asset = described_class.find('nyancat')
|
110
|
+
url = asset.rounded_corners(20).load
|
111
|
+
|
112
|
+
expect(url).to include('r=20')
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
it '#padded_background_color' do
|
117
|
+
vcr('asset/find') {
|
118
|
+
asset = described_class.find('nyancat')
|
119
|
+
url = asset.padded_background_color('rgb:ffffff').load
|
120
|
+
|
121
|
+
expect(url).to include('fit=pad')
|
122
|
+
expect(url).to include('bg=rgb%3Affffff') # '%3A' is the urlencoded hexcode for ':'
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'chaining' do
|
127
|
+
vcr('asset/find') {
|
128
|
+
asset = described_class.find('nyancat')
|
129
|
+
url = asset.resize(10, 20).rounded_corners(30).png_8bit.thumbnail_focused_on('face').load
|
130
|
+
|
131
|
+
expect(url).to include('w=10')
|
132
|
+
expect(url).to include('h=20')
|
133
|
+
expect(url).to include('r=30')
|
134
|
+
expect(url).to include('fm=png')
|
135
|
+
expect(url).to include('fl=png8')
|
136
|
+
expect(url).to include('fit=thumb')
|
137
|
+
expect(url).to include('f=face')
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class BelongsToManyParent < ContentfulModel::Base
|
4
|
+
self.content_type_id = 'belongsToManyParent'
|
5
|
+
|
6
|
+
has_many :children, class_name: 'BelongsToManyChild'
|
7
|
+
end
|
8
|
+
|
9
|
+
class BelongsToManyChild < ContentfulModel::Base
|
10
|
+
self.content_type_id = 'belongsToManyChild'
|
11
|
+
|
12
|
+
belongs_to_many :parents, inverse_of: :children, class_name: 'BelongsToManyParent'
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ContentfulModel::Associations::BelongsToMany do
|
16
|
+
before :each do
|
17
|
+
ContentfulModel.configure do |c|
|
18
|
+
c.space = 'a22o2qgm356c'
|
19
|
+
c.access_token = '60229df5876f14f499870d0d26f37b1323764ed637289353a5f74a5ea190c214'
|
20
|
+
c.entry_mapping = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
BelongsToManyParent.add_entry_mapping
|
24
|
+
BelongsToManyChild.add_entry_mapping
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'defines reverse relationship' do
|
28
|
+
vcr('association/belongs_to_many') {
|
29
|
+
child = BelongsToManyChild.first
|
30
|
+
expect(child.name).to eq 'BelongsToMany - Child'
|
31
|
+
|
32
|
+
expect(child.parents).to be_a ::Array
|
33
|
+
expect(child.parents.size).to eq 2
|
34
|
+
expect(child.parents.first).to be_a BelongsToManyParent
|
35
|
+
expect(child.parents.first.name).to eq 'BelongsToMany - Parent 1'
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ContentfulModel::Associations::BelongsTo do
|
4
|
+
before :each do
|
5
|
+
ContentfulModel.configure do |c|
|
6
|
+
c.default_locale = 'en-US'
|
7
|
+
c.access_token = '4d0f55d940975f78139daae5d965b463c0816e88ad16062d2c1ee3d6cb930521'
|
8
|
+
c.space = 'facgnwwgj5fe'
|
9
|
+
c.entry_mapping = {}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'is not implemented - on purpose' do
|
14
|
+
expect {
|
15
|
+
class BelongsToCat < ContentfulModel::Base
|
16
|
+
self.content_type_id = 'cat'
|
17
|
+
|
18
|
+
belongs_to :best_friend, class: Cat
|
19
|
+
end
|
20
|
+
}.to raise_error "Contentful doesn't have a singular belongs_to relationship. Use belongs_to_many instead."
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,321 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Nested < ContentfulModel::Base
|
4
|
+
self.content_type_id = 'nested'
|
5
|
+
|
6
|
+
has_many_nested :related
|
7
|
+
end
|
8
|
+
|
9
|
+
class RootNested < ContentfulModel::Base
|
10
|
+
self.content_type_id = 'rootNested'
|
11
|
+
|
12
|
+
has_many_nested :related, root: -> { RootNested.find('4UnXECdw6AAwyeCUcIuqCy') }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ContentfulModel::Associations::HasManyNested do
|
16
|
+
before :each do
|
17
|
+
ContentfulModel.configure do |c|
|
18
|
+
c.space = 'a22o2qgm356c'
|
19
|
+
c.access_token = '60229df5876f14f499870d0d26f37b1323764ed637289353a5f74a5ea190c214'
|
20
|
+
c.entry_mapping = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
Nested.add_entry_mapping
|
24
|
+
RootNested.add_entry_mapping
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'without root' do
|
28
|
+
context 'only has children' do
|
29
|
+
it 'parent is nil' do
|
30
|
+
vcr('association/nested_without_root_parentless') {
|
31
|
+
nested = Nested.find('5CNbxcca5OwgI68eSuWso2')
|
32
|
+
expect(nested.parent).to be_nil
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'has children' do
|
37
|
+
vcr('association/nested_without_root_parentless') {
|
38
|
+
nested = Nested.find('5CNbxcca5OwgI68eSuWso2')
|
39
|
+
|
40
|
+
expect(nested.children?).to be_truthy
|
41
|
+
expect(nested.children).to be_a ::Array
|
42
|
+
expect(nested.children.size).to eq 1
|
43
|
+
expect(nested.children.first).to be_a Nested
|
44
|
+
expect(nested.children.first.name).to eq 'Nested 2'
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'is the root' do
|
49
|
+
vcr('association/nested_without_root_parentless') {
|
50
|
+
nested = Nested.find('5CNbxcca5OwgI68eSuWso2')
|
51
|
+
expect(nested.root?).to be_truthy
|
52
|
+
expect(nested.root).to eq nested
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'has no ancestors' do
|
57
|
+
vcr('association/nested_without_root_parentless') {
|
58
|
+
nested = Nested.find('5CNbxcca5OwgI68eSuWso2')
|
59
|
+
expect(nested.ancestors).to be_empty
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'find ancestors will return self - if passed a block and no ancestors are found' do
|
64
|
+
vcr('association/nested_without_root_parentless') {
|
65
|
+
nested = Nested.find('5CNbxcca5OwgI68eSuWso2')
|
66
|
+
expect(nested.find_ancestors { |a| a }).to eq nested
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'find ancestors will be an empty array - if no ancestors and found and no block given' do
|
71
|
+
vcr('association/nested_without_root_parentless') {
|
72
|
+
nested = Nested.find('5CNbxcca5OwgI68eSuWso2')
|
73
|
+
expect(nested.find_ancestors.to_a).to eq []
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'creates a tree of children relationships' do
|
78
|
+
vcr('association/nested_without_root_parentless_higher_include') {
|
79
|
+
nested = Nested.query.params('sys.id' => '5CNbxcca5OwgI68eSuWso2', include: 3).load.first
|
80
|
+
child_1 = nested.children.first
|
81
|
+
child_2 = child_1.children.first
|
82
|
+
expect(nested.nested_children).not_to be_empty
|
83
|
+
expect(nested.nested_children).to eq(
|
84
|
+
child_1 => {
|
85
|
+
child_2 => nil
|
86
|
+
}
|
87
|
+
)
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'creates a tree of children by field' do
|
92
|
+
vcr('association/nested_without_root_parentless_higher_include') {
|
93
|
+
nested = Nested.query.params('sys.id' => '5CNbxcca5OwgI68eSuWso2', include: 3).load.first
|
94
|
+
child_1 = nested.children.first
|
95
|
+
child_2 = child_1.children.first
|
96
|
+
expect(nested.nested_children_by(:name)).not_to be_empty
|
97
|
+
expect(nested.nested_children_by(:name)).to eq(
|
98
|
+
child_1.name => {
|
99
|
+
child_2.name => nil
|
100
|
+
}
|
101
|
+
)
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'generates flattened structures to use as paths for entries' do
|
106
|
+
vcr('association/nested_without_root_parentless_higher_include') {
|
107
|
+
nested = Nested.query.params('sys.id' => '5CNbxcca5OwgI68eSuWso2', include: 3).load.first
|
108
|
+
child_1 = nested.children.first
|
109
|
+
child_2 = child_1.children.first
|
110
|
+
expect(nested.all_child_paths_by(:name)).not_to be_empty
|
111
|
+
expect(nested.all_child_paths_by(:id)).to eq [[child_1.id, child_2.id]]
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'generates a path for entries that match a value' do
|
116
|
+
vcr('association/nested_without_root_parentless_higher_include') {
|
117
|
+
nested = Nested.query.params('sys.id' => '5CNbxcca5OwgI68eSuWso2', include: 3).load.first
|
118
|
+
child_1 = nested.children.first
|
119
|
+
child_2 = child_1.children.first
|
120
|
+
expect(nested.find_child_path_by(:name, child_2.name)).not_to be_empty
|
121
|
+
expect(nested.find_child_path_by(:name, child_2.name)).to eq [[child_1.name, child_2.name]]
|
122
|
+
|
123
|
+
expect(nested.find_child_path_by(:name, 'foo')).to be_empty
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'has parent and child' do
|
129
|
+
it 'parent exists' do
|
130
|
+
vcr('association/nested_without_root_middle') {
|
131
|
+
nested = Nested.find('3gpECMvSLSOC8wgkQ6GQ2q')
|
132
|
+
expect(nested.parent).to be_a Nested
|
133
|
+
expect(nested.parent.name).to eq 'Nested 1'
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'has children' do
|
138
|
+
vcr('association/nested_without_root_middle') {
|
139
|
+
nested = Nested.find('3gpECMvSLSOC8wgkQ6GQ2q')
|
140
|
+
|
141
|
+
expect(nested.children?).to be_truthy
|
142
|
+
expect(nested.children).to be_a ::Array
|
143
|
+
expect(nested.children.size).to eq 1
|
144
|
+
expect(nested.children.first).to be_a Nested
|
145
|
+
expect(nested.children.first.name).to eq 'Nested 3'
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'is not the root' do
|
150
|
+
vcr('association/nested_without_root_middle_parent') {
|
151
|
+
nested = Nested.find('3gpECMvSLSOC8wgkQ6GQ2q')
|
152
|
+
expect(nested.root?).to be_falsey
|
153
|
+
expect(nested.root).to eq nested.parent
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'has ancestors' do
|
158
|
+
vcr('association/nested_without_root_middle_parent') {
|
159
|
+
nested = Nested.find('3gpECMvSLSOC8wgkQ6GQ2q')
|
160
|
+
expect(nested.ancestors).not_to be_empty
|
161
|
+
expect(nested.ancestors.size).to eq 1
|
162
|
+
expect(nested.ancestors.first.name).to eq 'Nested 1'
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'find ancestors will return an enumerator of ancestors - if no block given' do
|
167
|
+
vcr('association/nested_without_root_middle_parent') {
|
168
|
+
nested = Nested.find('3gpECMvSLSOC8wgkQ6GQ2q')
|
169
|
+
expect(nested.find_ancestors.to_a.map(&:name)).to eq ['Nested 1']
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'find ancestors executes the block for each ancestor - if block given' do
|
174
|
+
vcr('association/nested_without_root_middle_parent') {
|
175
|
+
nested = Nested.find('3gpECMvSLSOC8wgkQ6GQ2q')
|
176
|
+
|
177
|
+
ancestor_names = []
|
178
|
+
nested.find_ancestors { |a| ancestor_names << a.name }
|
179
|
+
|
180
|
+
expect(ancestor_names).to eq ['Nested 1']
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'creates a tree of children relationships' do
|
185
|
+
vcr('association/nested_without_root_middle_higher_include') {
|
186
|
+
nested = Nested.query.params('sys.id' => '3gpECMvSLSOC8wgkQ6GQ2q', include: 3).load.first
|
187
|
+
child_1 = nested.children.first
|
188
|
+
expect(nested.nested_children).not_to be_empty
|
189
|
+
expect(nested.nested_children).to eq(
|
190
|
+
child_1 => nil
|
191
|
+
)
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'creates a tree of children by field' do
|
196
|
+
vcr('association/nested_without_root_middle_higher_include') {
|
197
|
+
nested = Nested.query.params('sys.id' => '3gpECMvSLSOC8wgkQ6GQ2q', include: 3).load.first
|
198
|
+
child_1 = nested.children.first
|
199
|
+
expect(nested.nested_children_by(:name)).not_to be_empty
|
200
|
+
expect(nested.nested_children_by(:name)).to eq(
|
201
|
+
child_1.name => nil
|
202
|
+
)
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'generates flattened structures to use as paths for entries' do
|
207
|
+
vcr('association/nested_without_root_middle_higher_include') {
|
208
|
+
nested = Nested.query.params('sys.id' => '3gpECMvSLSOC8wgkQ6GQ2q', include: 3).load.first
|
209
|
+
child_1 = nested.children.first
|
210
|
+
expect(nested.all_child_paths_by(:name)).not_to be_empty
|
211
|
+
expect(nested.all_child_paths_by(:id)).to eq [[child_1.id]]
|
212
|
+
}
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'generates a path for entries that match a value' do
|
216
|
+
vcr('association/nested_without_root_middle_higher_include') {
|
217
|
+
nested = Nested.query.params('sys.id' => '3gpECMvSLSOC8wgkQ6GQ2q', include: 3).load.first
|
218
|
+
child_1 = nested.children.first
|
219
|
+
expect(nested.find_child_path_by(:name, child_1.name)).not_to be_empty
|
220
|
+
expect(nested.find_child_path_by(:name, child_1.name)).to eq [[child_1.name]]
|
221
|
+
|
222
|
+
expect(nested.find_child_path_by(:name, 'foo')).to be_empty
|
223
|
+
}
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context 'only has parent' do
|
228
|
+
it 'parent exists' do
|
229
|
+
vcr('association/nested_without_root_childless') {
|
230
|
+
nested = Nested.find('2pFubMJuVqMSisGeCmoqem')
|
231
|
+
expect(nested.parent).to be_a Nested
|
232
|
+
expect(nested.parent.name).to eq 'Nested 2'
|
233
|
+
}
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'has no children' do
|
237
|
+
vcr('association/nested_without_root_childless') {
|
238
|
+
nested = Nested.find('2pFubMJuVqMSisGeCmoqem')
|
239
|
+
|
240
|
+
expect(nested.children?).to be_falsey
|
241
|
+
expect(nested.children).to be_a ::Array
|
242
|
+
expect(nested.children.size).to eq 0
|
243
|
+
}
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'is not the root' do
|
247
|
+
vcr('association/nested_without_root_childless') {
|
248
|
+
nested = Nested.find('2pFubMJuVqMSisGeCmoqem')
|
249
|
+
expect(nested.root?).to be_falsey
|
250
|
+
expect(nested.root.name).to eq 'Nested 1'
|
251
|
+
}
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'has ancestors' do
|
255
|
+
vcr('association/nested_without_root_childless') {
|
256
|
+
nested = Nested.find('2pFubMJuVqMSisGeCmoqem')
|
257
|
+
expect(nested.ancestors).not_to be_empty
|
258
|
+
expect(nested.ancestors.size).to eq 2
|
259
|
+
}
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'find ancestors will return an enumerator of ancestors - if no block given' do
|
263
|
+
vcr('association/nested_without_root_child_parent') {
|
264
|
+
nested = Nested.find('2pFubMJuVqMSisGeCmoqem')
|
265
|
+
expect(nested.find_ancestors.to_a.map(&:name)).to eq ['Nested 2', 'Nested 1']
|
266
|
+
}
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'find ancestors executes the block for each ancestor - if block given' do
|
270
|
+
vcr('association/nested_without_root_child_parent') {
|
271
|
+
nested = Nested.find('2pFubMJuVqMSisGeCmoqem')
|
272
|
+
|
273
|
+
ancestor_names = []
|
274
|
+
nested.find_ancestors { |a| ancestor_names << a.name }
|
275
|
+
|
276
|
+
expect(ancestor_names).to eq ['Nested 2', 'Nested 1']
|
277
|
+
}
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'creates a tree of children relationships' do
|
281
|
+
vcr('association/nested_without_root_child_higher_include') {
|
282
|
+
nested = Nested.query.params('sys.id' => '2pFubMJuVqMSisGeCmoqem', include: 3).load.first
|
283
|
+
expect(nested.nested_children).to be_empty
|
284
|
+
}
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'creates a tree of children by field' do
|
288
|
+
vcr('association/nested_without_root_child_higher_include') {
|
289
|
+
nested = Nested.query.params('sys.id' => '2pFubMJuVqMSisGeCmoqem', include: 3).load.first
|
290
|
+
expect(nested.nested_children_by(:name)).to be_empty
|
291
|
+
}
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'generates flattened structures to use as paths for entries' do
|
295
|
+
vcr('association/nested_without_root_child_higher_include') {
|
296
|
+
nested = Nested.query.params('sys.id' => '2pFubMJuVqMSisGeCmoqem', include: 3).load.first
|
297
|
+
expect(nested.all_child_paths_by(:name)).to be_empty
|
298
|
+
}
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'generates a path for entries that match a value' do
|
302
|
+
vcr('association/nested_without_root_child_higher_include') {
|
303
|
+
nested = Nested.query.params('sys.id' => '2pFubMJuVqMSisGeCmoqem', include: 3).load.first
|
304
|
+
expect(nested.find_child_path_by(:name, 'foo')).to be_empty
|
305
|
+
}
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context 'with root' do
|
311
|
+
it 'has root method defined' do
|
312
|
+
vcr('association/nested_with_root_root') {
|
313
|
+
non_root_id = '5sdA4Fv5o4SOeyq4Sq8qmG'
|
314
|
+
root_nested = RootNested.find(non_root_id)
|
315
|
+
|
316
|
+
expect(root_nested.name).to eq 'RootNested - 1'
|
317
|
+
expect(root_nested.root_root_nested.name).to eq 'RootNested - Root'
|
318
|
+
}
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|