releaf-content 0.2.1 → 1.0.3
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/LICENSE +19 -21
- data/app/assets/javascripts/{releaf/controllers → controllers}/releaf/content/nodes.js +0 -0
- data/app/assets/stylesheets/{releaf/controllers → controllers}/releaf/content/nodes.scss +0 -0
- data/app/builders/releaf/content/builders/action_dialog.rb +9 -1
- data/app/builders/releaf/content/builders/tree.rb +1 -1
- data/app/builders/releaf/content/content_type_dialog_builder.rb +2 -2
- data/app/builders/releaf/content/nodes/form_builder.rb +3 -3
- data/app/builders/releaf/content/nodes/index_builder.rb +1 -1
- data/app/builders/releaf/content/nodes/toolbox_builder.rb +0 -5
- data/app/controllers/releaf/content/nodes_controller.rb +109 -129
- data/app/middleware/releaf/content/routes_reloader.rb +12 -4
- data/app/services/releaf/content/node/copy.rb +90 -0
- data/app/services/releaf/content/node/move.rb +21 -0
- data/app/services/releaf/content/node/save_under_parent.rb +22 -0
- data/app/services/releaf/content/node/service.rb +17 -0
- data/app/validators/releaf/content/node/singleness_validator.rb +1 -24
- data/lib/releaf-content.rb +85 -6
- data/lib/releaf/content/acts_as_node.rb +0 -5
- data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +2 -8
- data/lib/releaf/content/configuration.rb +61 -0
- data/lib/releaf/content/engine.rb +1 -34
- data/lib/releaf/content/node.rb +30 -101
- data/lib/releaf/content/node_mapper.rb +28 -2
- data/lib/releaf/content/route.rb +37 -25
- data/spec/builders/content/nodes/action_dialog_spec.rb +39 -0
- data/spec/builders/content/nodes/form_builder_spec.rb +47 -3
- data/spec/builders/content/nodes/toolbox_builder_spec.rb +1 -32
- data/spec/controllers/releaf/content/nodes_controller_spec.rb +42 -11
- data/spec/features/nodes_services_spec.rb +207 -0
- data/spec/features/nodes_spec.rb +328 -30
- data/spec/lib/releaf/content/acts_as_node_spec.rb +4 -32
- data/spec/lib/releaf/content/configuration_spec.rb +159 -0
- data/spec/lib/releaf/content/engine_spec.rb +149 -0
- data/spec/lib/releaf/content/node_spec.rb +66 -324
- data/spec/lib/releaf/content/route_spec.rb +223 -34
- data/spec/middleware/routes_reloader_spec.rb +62 -14
- data/spec/routing/node_mapper_spec.rb +223 -55
- data/spec/services/releaf/content/node/copy_spec.rb +115 -0
- data/spec/services/releaf/content/node/move_spec.rb +20 -0
- data/spec/services/releaf/content/node/save_under_parent_spec.rb +49 -0
- data/spec/services/releaf/content/node/service_spec.rb +19 -0
- metadata +38 -21
- data/app/builders/releaf/content/go_to_dialog_builder.rb +0 -9
- data/app/views/releaf/content/nodes/go_to_dialog.ruby +0 -1
- data/lib/releaf/content/builders_autoload.rb +0 -18
- data/releaf-content.gemspec +0 -20
@@ -1,9 +1,35 @@
|
|
1
1
|
module Releaf::Content
|
2
2
|
module NodeMapper
|
3
|
-
|
4
|
-
|
3
|
+
attr_accessor :default_node_class
|
4
|
+
|
5
|
+
def node_routes_for(
|
6
|
+
node_content_class,
|
7
|
+
controller: Releaf::Content::Route.default_controller(node_content_class),
|
8
|
+
node_class: default_node_class || Releaf::Content.default_model,
|
9
|
+
&block
|
10
|
+
)
|
11
|
+
Releaf::Content::Route.for(node_class, node_content_class, controller).each do |route|
|
5
12
|
Releaf::Content::RouterProxy.new(self, route).draw(&block)
|
6
13
|
end
|
7
14
|
end
|
15
|
+
|
16
|
+
def for_node_class( node_class )
|
17
|
+
previous_node_class = self.default_node_class
|
18
|
+
self.default_node_class = node_class
|
19
|
+
yield if block_given?
|
20
|
+
self.default_node_class = previous_node_class
|
21
|
+
end
|
22
|
+
|
23
|
+
# expects Releaf::Content.routing hash or a subset of it as an argument
|
24
|
+
def node_routing( routing )
|
25
|
+
routing.each_pair do | node_class_name, node_class_routing |
|
26
|
+
constraints node_class_routing[:constraints] do
|
27
|
+
for_node_class(node_class_name.constantize) do
|
28
|
+
yield
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
8
34
|
end
|
9
35
|
end
|
data/lib/releaf/content/route.rb
CHANGED
@@ -1,17 +1,12 @@
|
|
1
1
|
module Releaf::Content
|
2
2
|
class Route
|
3
|
-
attr_accessor :path, :node, :locale, :node_id, :default_controller
|
3
|
+
attr_accessor :path, :node, :locale, :node_class, :node_id, :default_controller, :site
|
4
4
|
|
5
|
-
def self.
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.node_class_default_controller(node_class)
|
11
|
-
if node_class <= ActionController::Base
|
12
|
-
node_class.name.underscore.sub(/_controller$/, '')
|
5
|
+
def self.default_controller(node_content_class)
|
6
|
+
if node_content_class <= ActionController::Base
|
7
|
+
node_content_class.name.underscore.sub(/_controller$/, '')
|
13
8
|
else
|
14
|
-
|
9
|
+
node_content_class.name.pluralize.underscore
|
15
10
|
end
|
16
11
|
end
|
17
12
|
|
@@ -22,29 +17,47 @@ module Releaf::Content
|
|
22
17
|
# @return [Hash] route options. Will return at least node "node_id" and "locale" keys.
|
23
18
|
def params(method_or_path, options = {})
|
24
19
|
method_or_path = method_or_path.to_s
|
25
|
-
|
26
|
-
|
20
|
+
[
|
21
|
+
path_for(method_or_path, options),
|
22
|
+
options_for(method_or_path, options)
|
23
|
+
]
|
24
|
+
end
|
27
25
|
|
26
|
+
def options_for( method_or_path, options )
|
28
27
|
route_options = options.merge({
|
29
|
-
|
30
|
-
|
28
|
+
to: controller_and_action_for(method_or_path, options),
|
29
|
+
node_class: node_class.name,
|
30
|
+
node_id: node_id.to_s,
|
31
|
+
locale: locale
|
31
32
|
})
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
route_options[:as] = "#{locale}_#{route_options[:as]}"
|
36
|
-
end
|
34
|
+
route_options[:site] = site if site.present?
|
35
|
+
route_options[:as] = name( route_options )
|
37
36
|
|
38
|
-
|
37
|
+
route_options
|
38
|
+
end
|
39
|
+
|
40
|
+
def name( route_options )
|
41
|
+
return nil unless route_options[:as].present?
|
42
|
+
|
43
|
+
# prepend :as with locale and site to prevent duplicate route names
|
44
|
+
name_parts = [ route_options[:as] ]
|
45
|
+
|
46
|
+
name_parts.unshift( route_options[:locale] ) if route_options[:locale].present?
|
47
|
+
name_parts.unshift( route_options[:site] ) if route_options[:site].present?
|
48
|
+
|
49
|
+
name_parts.join('_')
|
39
50
|
end
|
40
51
|
|
41
52
|
# Return routes for given class that implement ActsAsNode
|
42
53
|
#
|
43
|
-
# @param
|
54
|
+
# @param node_class [Class] class name to load related nodes
|
55
|
+
# @param node_content_class [Class] class name to load related nodes
|
44
56
|
# @param default_controller [String]
|
45
57
|
# @return [Array] array of Content::Route objects
|
46
|
-
def self.for(
|
47
|
-
node_class
|
58
|
+
def self.for(node_class, node_content_class, default_controller)
|
59
|
+
node_class = node_class.constantize if node_class.is_a? String
|
60
|
+
node_class.where(content_type: node_content_class).each.inject([]) do |routes, node|
|
48
61
|
routes << build_route_object(node, default_controller) if node.available?
|
49
62
|
routes
|
50
63
|
end
|
@@ -55,16 +68,15 @@ module Releaf::Content
|
|
55
68
|
# Build Content::Route from Node object
|
56
69
|
def self.build_route_object(node, default_controller)
|
57
70
|
route = new
|
71
|
+
route.node_class = node.class
|
58
72
|
route.node_id = node.id.to_s
|
59
73
|
route.path = node.path
|
60
74
|
route.locale = node.root.locale
|
61
75
|
route.default_controller = default_controller
|
62
|
-
|
76
|
+
route.site = Releaf::Content.routing[node.class.name][:site]
|
63
77
|
route
|
64
78
|
end
|
65
79
|
|
66
|
-
private
|
67
|
-
|
68
80
|
def path_for(method_or_path, options)
|
69
81
|
if method_or_path.include?('#')
|
70
82
|
path
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "rails_helper"
|
2
|
+
|
3
|
+
describe Releaf::Content::Builders::ActionDialog, type: :class do
|
4
|
+
class ConfirmDestroyDialogTestHelper < ActionView::Base
|
5
|
+
include Releaf::ApplicationHelper
|
6
|
+
end
|
7
|
+
|
8
|
+
class ActionDialogIncluder
|
9
|
+
include Releaf::Content::Builders::ActionDialog
|
10
|
+
def action; end
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:template){ ConfirmDestroyDialogTestHelper.new }
|
14
|
+
let(:object){ Book.new }
|
15
|
+
let(:subject){ ActionDialogIncluder.new(template) }
|
16
|
+
|
17
|
+
describe "#confirm_button_text" do
|
18
|
+
it "returns translation for humanized builder action" do
|
19
|
+
allow(subject).to receive(:action).and_return(:move_to_the_right)
|
20
|
+
allow(subject).to receive(:t).with("Move to the right").and_return("to the left")
|
21
|
+
expect(subject.confirm_button_text).to eq("to the left")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#confirm_button_attributes" do
|
26
|
+
it "returns hash with confirm button attributes" do
|
27
|
+
expect(subject.confirm_button_attributes).to be_instance_of Hash
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#confirm_button" do
|
32
|
+
it "returns confirm button" do
|
33
|
+
allow(subject).to receive(:confirm_button_text).and_return("Yess")
|
34
|
+
allow(subject).to receive(:confirm_button_attributes).and_return(a: "b")
|
35
|
+
allow(subject).to receive(:button).with("Yess", "check", a: "b").and_return("x")
|
36
|
+
expect(subject.confirm_button).to eq("x")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -159,12 +159,56 @@ describe Releaf::Content::Nodes::FormBuilder, type: :class do
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
|
162
|
+
describe "#slug_base_url" do
|
163
|
+
before do
|
164
|
+
request = double(:request, protocol: "http:://", host_with_port: "somehost:8080")
|
165
|
+
allow(subject).to receive(:request).and_return(request)
|
166
|
+
allow(object).to receive(:parent).and_return(Node.new)
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when trailing slash for path enabled" do
|
170
|
+
it "returns absolute url without extra slash added" do
|
171
|
+
allow(object).to receive(:trailing_slash_for_path?).and_return(true)
|
172
|
+
allow(object.parent).to receive(:path).and_return("/parent/path/")
|
173
|
+
expect(subject.slug_base_url).to eq("http:://somehost:8080/parent/path/")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when trailing slash for path disabled" do
|
178
|
+
it "returns absolute url with extra slash added" do
|
179
|
+
allow(object).to receive(:trailing_slash_for_path?).and_return(false)
|
180
|
+
allow(object.parent).to receive(:path).and_return("/parent/path")
|
181
|
+
expect(subject.slug_base_url).to eq("http:://somehost:8080/parent/path/")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#slug_link" do
|
187
|
+
before do
|
188
|
+
allow(subject).to receive(:slug_base_url).and_return("http://some.host/parent/path/")
|
189
|
+
end
|
190
|
+
|
191
|
+
context "when trailing slash for path enabled" do
|
192
|
+
it "returns absolute url without extra slash added" do
|
193
|
+
allow(object).to receive(:trailing_slash_for_path?).and_return(true)
|
194
|
+
expect(subject.slug_link).to eq('<a href="/a/b/">http://some.host/parent/path/<span>b</span>/</a>')
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "when trailing slash for path disabled" do
|
199
|
+
it "returns absolute url with extra slash added" do
|
200
|
+
allow(object).to receive(:trailing_slash_for_path?).and_return(false)
|
201
|
+
expect(subject.slug_link).to eq('<a href="/a/b">http://some.host/parent/path/<span>b</span></a>')
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
162
206
|
describe "#render_slug" do
|
163
207
|
it "renders customized field" do
|
164
|
-
controller =
|
208
|
+
controller = Admin::NodesController.new
|
165
209
|
allow(subject).to receive(:controller).and_return(controller)
|
166
210
|
allow(subject).to receive(:slug_base_url).and_return("http://localhost/parent")
|
167
|
-
allow(subject).to receive(:url_for).with(controller: "/
|
211
|
+
allow(subject).to receive(:url_for).with(controller: "admin/nodes", action: "generate_url", parent_id: 1, id: 2)
|
168
212
|
.and_return("http://localhost/slug-generation-url")
|
169
213
|
|
170
214
|
content = '
|
@@ -178,7 +222,7 @@ describe Releaf::Content::Nodes::FormBuilder, type: :class do
|
|
178
222
|
<i class="fa fa-keyboard-o"></i>
|
179
223
|
</button>
|
180
224
|
<div class="link">
|
181
|
-
<a href="/a/b">http://localhost/parent<span>b</span
|
225
|
+
<a href="/a/b">http://localhost/parent<span>b</span></a>
|
182
226
|
</div>
|
183
227
|
</div>
|
184
228
|
</div>
|
@@ -27,7 +27,6 @@ describe Releaf::Content::Nodes::ToolboxBuilder, type: :class do
|
|
27
27
|
before do
|
28
28
|
allow(subject).to receive(:params).and_return({})
|
29
29
|
allow(subject).to receive(:add_child_button).and_return( :add_child_item )
|
30
|
-
allow(subject).to receive(:go_to_button).and_return( :go_to_item )
|
31
30
|
allow(subject).to receive(:copy_button).and_return( :copy_item )
|
32
31
|
allow(subject).to receive(:move_button).and_return( :move_item )
|
33
32
|
end
|
@@ -41,26 +40,6 @@ describe Releaf::Content::Nodes::ToolboxBuilder, type: :class do
|
|
41
40
|
|
42
41
|
end
|
43
42
|
|
44
|
-
context "when applied to an existing record" do
|
45
|
-
|
46
|
-
before do
|
47
|
-
allow(node).to receive(:new_record?).and_return false
|
48
|
-
end
|
49
|
-
|
50
|
-
it "prepends add child, go to, copy and move items to the list returned by parent class" do
|
51
|
-
expect(subject.items).to eq([ :add_child_item, :go_to_item, :copy_item, :move_item, :super_item ])
|
52
|
-
end
|
53
|
-
|
54
|
-
context "when in index context" do
|
55
|
-
|
56
|
-
it "does not include go_to_item" do
|
57
|
-
allow(subject).to receive(:params).and_return({ context: "index" })
|
58
|
-
expect(subject.items).to eq([ :add_child_item, :copy_item, :move_item, :super_item ])
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
43
|
end
|
65
44
|
|
66
45
|
describe "item methods" do
|
@@ -74,16 +53,6 @@ describe Releaf::Content::Nodes::ToolboxBuilder, type: :class do
|
|
74
53
|
end
|
75
54
|
end
|
76
55
|
|
77
|
-
describe "#go_to_button" do
|
78
|
-
it "returns an ajaxbox link to go to dialog" do
|
79
|
-
allow(subject).to receive(:t).with('Go to').and_return('gotoxx')
|
80
|
-
allow(subject).to receive(:url_for).with(action: 'go_to_dialog').and_return('dialogurl')
|
81
|
-
html = '<a class="button ajaxbox" title="gotoxx" href="dialogurl">gotoxx</a>'
|
82
|
-
expect(subject.go_to_button).to eq(html)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
|
87
56
|
describe "#copy_button" do
|
88
57
|
it "returns an ajaxbox link to copy dialog" do
|
89
58
|
allow(subject).to receive(:t).with('Copy').and_return('copyxx')
|
@@ -105,4 +74,4 @@ describe Releaf::Content::Nodes::ToolboxBuilder, type: :class do
|
|
105
74
|
|
106
75
|
end
|
107
76
|
|
108
|
-
end
|
77
|
+
end
|
@@ -1,21 +1,52 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
3
|
describe Releaf::Content::NodesController do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
expect(subject.builder_scopes).to eq(["xx", "Releaf::Content::Nodes", "Admin::Builders"])
|
4
|
+
|
5
|
+
describe "#features"do
|
6
|
+
it "excludes `create another` and `search` features" do
|
7
|
+
expect(subject.features).to_not include(:create_another, :search)
|
9
8
|
end
|
10
9
|
end
|
11
10
|
|
12
|
-
describe "#
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
describe "#ancestor_nodes" do
|
12
|
+
let(:node){ Node.new }
|
13
|
+
let(:ancestors){ Node.where(id: 1212) }
|
14
|
+
|
15
|
+
before do
|
16
|
+
allow(ancestors).to receive(:reorder).with(:depth).and_return(["depth_ordered_ancestors"])
|
17
|
+
end
|
18
|
+
|
19
|
+
context "when new node" do
|
20
|
+
context "when node has parent" do
|
21
|
+
it "returns parent ancestors ordered by depth alongside parent ancestor" do
|
22
|
+
parent_node = Node.new
|
23
|
+
node.parent = parent_node
|
24
|
+
allow(parent_node).to receive(:ancestors).and_return(ancestors)
|
25
|
+
expect(subject.ancestor_nodes(node)).to eq(["depth_ordered_ancestors", parent_node])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when node has no parent" do
|
30
|
+
it "returns empty array" do
|
31
|
+
expect(subject.ancestor_nodes(node)).to eq([])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when persisted node" do
|
37
|
+
it "returns resource ancestors ordered by depth" do
|
38
|
+
allow(node).to receive(:persisted?).and_return(true)
|
39
|
+
allow(node).to receive(:ancestors).and_return(ancestors)
|
40
|
+
expect(subject.ancestor_nodes(node)).to eq(["depth_ordered_ancestors"])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
16
44
|
|
17
|
-
|
18
|
-
|
45
|
+
describe ".resource_class" do
|
46
|
+
it "looks up node class in releaf content resource configuration" do
|
47
|
+
config = { 'OtherSite::OtherNode' => { controller: 'Releaf::Content::NodesController' } }
|
48
|
+
allow( Releaf::Content ).to receive(:resources).and_return(config)
|
49
|
+
expect( described_class.resource_class ).to be OtherSite::OtherNode
|
19
50
|
end
|
20
51
|
end
|
21
52
|
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
describe "Nodes services (copy, move)" do
|
3
|
+
|
4
|
+
describe "Moving" do
|
5
|
+
before do
|
6
|
+
@home_page_node = create(:home_page_node, locale: "lv")
|
7
|
+
@home_page_node_2 = create(:home_page_node, locale: "en")
|
8
|
+
@text_page_node_3 = create(:text_page_node, parent_id: @home_page_node_2.id)
|
9
|
+
@text_page_node_4 = create(:text_page_node, parent_id: @text_page_node_3.id)
|
10
|
+
|
11
|
+
# it is important to reload nodes, otherwise associations will return empty set
|
12
|
+
@home_page_node.reload
|
13
|
+
@home_page_node_2.reload
|
14
|
+
@text_page_node_3.reload
|
15
|
+
@text_page_node_4.reload
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when one of children becomes invalid" do
|
19
|
+
before do
|
20
|
+
@text_page_node_4.name = nil
|
21
|
+
@text_page_node_4.save(validate: false)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises ActiveRecord::RecordInvalid" do
|
25
|
+
expect { @text_page_node_3.move(@home_page_node.id) }.to raise_error ActiveRecord::RecordInvalid
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises error on node being moved, even tought descendant has error" do
|
29
|
+
begin
|
30
|
+
@text_page_node_3.move(@home_page_node.id)
|
31
|
+
rescue ActiveRecord::RecordInvalid => e
|
32
|
+
expect( e.record ).to eq @text_page_node_3
|
33
|
+
end
|
34
|
+
|
35
|
+
expect(@text_page_node_3.errors.messages).to eq(name: [], base: ["descendant invalid"])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when moving existing node to other nodes child's position" do
|
40
|
+
it "changes parent_id" do
|
41
|
+
expect{ @text_page_node_3.move(@home_page_node.id) }.to change{ Node.find(@text_page_node_3.id).parent_id }.from(@home_page_node_2.id).to(@home_page_node.id)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when moving to self child's position" do
|
46
|
+
it "raises ActiveRecord::RecordInvalid" do
|
47
|
+
expect{ @text_page_node_3.move(@text_page_node_3.id) }.to raise_error(ActiveRecord::RecordInvalid)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when passing nil as target node" do
|
52
|
+
it "updates parent_id" do
|
53
|
+
allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
|
54
|
+
@home_page_node.destroy
|
55
|
+
expect{ @text_page_node_3.move(nil) }.to change { Node.find(@text_page_node_3.id).parent_id }.to(nil)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when passing nonexistent target node's id" do
|
60
|
+
it "raises ActiveRecord::RecordInvalid" do
|
61
|
+
expect{ @text_page_node_3.move(998123) }.to raise_error(ActiveRecord::RecordInvalid)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "Copying", create_nodes: true do
|
68
|
+
before create_nodes: true do
|
69
|
+
@home_page_node = create(:home_page_node, locale: "lv")
|
70
|
+
@home_page_node_2 = create(:home_page_node, locale: "en")
|
71
|
+
@text_page_node_3 = create(:text_page_node, parent_id: @home_page_node_2.id)
|
72
|
+
@text_page_node_4 = create(:text_page_node, parent_id: @text_page_node_3.id)
|
73
|
+
@text_page_node_5 = create(:text_page_node, parent_id: @text_page_node_4.id)
|
74
|
+
|
75
|
+
# it is important to reload nodes, otherwise associations will return empty set
|
76
|
+
@home_page_node.reload
|
77
|
+
@home_page_node_2.reload
|
78
|
+
@text_page_node_3.reload
|
79
|
+
@text_page_node_4.reload
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when one of children becomes invalid" do
|
83
|
+
before do
|
84
|
+
@text_page_node_4.name = nil
|
85
|
+
@text_page_node_4.save(validate: false)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "raises ActiveRecord::RecordInvalid" do
|
89
|
+
expect { @text_page_node_3.copy(@home_page_node.id) }.to raise_error ActiveRecord::RecordInvalid
|
90
|
+
end
|
91
|
+
|
92
|
+
it "raises error on node being copied" do
|
93
|
+
begin
|
94
|
+
@text_page_node_3.copy(@home_page_node.id)
|
95
|
+
rescue ActiveRecord::RecordInvalid => e
|
96
|
+
expect( e.record ).to eq @text_page_node_3
|
97
|
+
end
|
98
|
+
expect(@text_page_node_3.errors.messages).to eq(base: ["descendant invalid"])
|
99
|
+
end
|
100
|
+
|
101
|
+
it "doesn't create any new nodes" do
|
102
|
+
expect do
|
103
|
+
begin
|
104
|
+
@text_page_node_3.copy(@home_page_node.id)
|
105
|
+
rescue ActiveRecord::RecordInvalid
|
106
|
+
end
|
107
|
+
end.to_not change { Node.count }
|
108
|
+
end
|
109
|
+
|
110
|
+
it "doesn't update settings timestamp" do
|
111
|
+
expect( Node ).to_not receive(:updated)
|
112
|
+
begin
|
113
|
+
@text_page_node_3.copy(@home_page_node.id)
|
114
|
+
rescue ActiveRecord::RecordInvalid
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
context "with corect parent_id" do
|
122
|
+
it "creates node along with descendant nodes" do
|
123
|
+
expect{ @text_page_node_3.copy(@home_page_node.id) }.to change{ Node.count }.by( @text_page_node_3.descendants.size + 1 )
|
124
|
+
end
|
125
|
+
|
126
|
+
it "correctly copies attributes" do
|
127
|
+
allow( @text_page_node_3 ).to receive(:children).and_return([@text_page_node_4])
|
128
|
+
allow( @text_page_node_4 ).to receive(:children).and_return([@text_page_node_5])
|
129
|
+
|
130
|
+
@text_page_node_3.update_attribute(:active, false)
|
131
|
+
@text_page_node_4.update_attribute(:active, false)
|
132
|
+
|
133
|
+
allow( @text_page_node_3 ).to receive(:attributes_to_copy).and_return(["name", "parent_id", "content_type"])
|
134
|
+
|
135
|
+
@text_page_node_3.copy(@home_page_node.id)
|
136
|
+
|
137
|
+
@node_2_copy = @home_page_node.children.first
|
138
|
+
@node_3_copy = @node_2_copy.children.first
|
139
|
+
@node_4_copy = @node_3_copy.children.first
|
140
|
+
|
141
|
+
# new nodes by default are active, however we stubbed
|
142
|
+
# #attributes_to_copy of @test_node_2 to not return active attribute
|
143
|
+
# Also we updated @test_node_2#active to be false.
|
144
|
+
# However copy is active, because active attribute wasn't copied
|
145
|
+
expect( @node_2_copy ).to be_active
|
146
|
+
# for copy of @text_page_node_3 active attribute was copied however, as it
|
147
|
+
# should have been
|
148
|
+
expect( @node_3_copy ).to_not be_active
|
149
|
+
expect( @node_4_copy ).to be_active
|
150
|
+
|
151
|
+
expect( @node_2_copy.name ).to eq @text_page_node_3.name
|
152
|
+
expect( @node_3_copy.name ).to eq @text_page_node_4.name
|
153
|
+
expect( @node_4_copy.name ).to eq @text_page_node_5.name
|
154
|
+
end
|
155
|
+
|
156
|
+
it "updates settings timestamp only once" do
|
157
|
+
expect( Node ).to receive(:updated).once.and_call_original
|
158
|
+
@text_page_node_3.copy(@home_page_node.id)
|
159
|
+
end
|
160
|
+
|
161
|
+
context "when parent_id is nil" do
|
162
|
+
it "creates new node" do
|
163
|
+
allow_any_instance_of(Releaf::Content::Node::RootValidator).to receive(:validate)
|
164
|
+
expect{ @text_page_node_3.copy(nil) }.to change{ Node.count }.by(3)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context "when copying root nodes", create_nodes: false do
|
169
|
+
context "when root locale uniqueness is validated" do
|
170
|
+
it "resets locale to nil" do
|
171
|
+
@text_page_node = create(:home_page_node, locale: 'en')
|
172
|
+
allow_any_instance_of(Node).to receive(:validate_root_locale_uniqueness?).and_return(true)
|
173
|
+
@text_page_node.copy(nil)
|
174
|
+
expect( Node.last.locale ).to eq nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "when root locale uniqueness is not validated" do
|
179
|
+
it "doesn't reset locale to nil" do
|
180
|
+
@text_page_node = create(:home_page_node, locale: 'en')
|
181
|
+
allow_any_instance_of(Node).to receive(:validate_root_locale_uniqueness?).and_return(false)
|
182
|
+
@text_page_node.copy(nil)
|
183
|
+
expect( Node.last.locale ).to eq 'en'
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
context "with nonexistent parent_id" do
|
190
|
+
it "raises ActiveRecord::RecordInvalid" do
|
191
|
+
expect { @text_page_node_3.copy(99991) }.to raise_error(ActiveRecord::RecordInvalid)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
context "with same parent_id as node.id" do
|
196
|
+
it "raises ActiveRecord::RecordInvalid" do
|
197
|
+
expect{ @text_page_node_3.copy(@text_page_node_3.id) }.to raise_error(ActiveRecord::RecordInvalid)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
context "when copying to child node" do
|
202
|
+
it "raises ActiveRecord::RecordInvalid" do
|
203
|
+
expect{ @text_page_node_3.copy(@text_page_node_4.id) }.to raise_error(ActiveRecord::RecordInvalid)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|