releaf-content 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +24 -0
  3. data/app/assets/javascripts/releaf/controllers/releaf/content/nodes.js +88 -0
  4. data/app/assets/stylesheets/releaf/controllers/releaf/content/nodes.scss +234 -0
  5. data/app/builders/releaf/content/builders/action_dialog.rb +60 -0
  6. data/app/builders/releaf/content/builders/dialog.rb +15 -0
  7. data/app/builders/releaf/content/builders/tree.rb +84 -0
  8. data/app/builders/releaf/content/content_type_dialog_builder.rb +74 -0
  9. data/app/builders/releaf/content/copy_dialog_builder.rb +9 -0
  10. data/app/builders/releaf/content/go_to_dialog_builder.rb +9 -0
  11. data/app/builders/releaf/content/move_dialog_builder.rb +9 -0
  12. data/app/builders/releaf/content/nodes/content_form_builder.rb +7 -0
  13. data/app/builders/releaf/content/nodes/form_builder.rb +108 -0
  14. data/app/builders/releaf/content/nodes/index_builder.rb +24 -0
  15. data/app/builders/releaf/content/nodes/toolbox_builder.rb +33 -0
  16. data/app/controllers/releaf/content/nodes_controller.rb +166 -0
  17. data/app/middleware/releaf/content/routes_reloader.rb +25 -0
  18. data/app/validators/releaf/content/node/parent_validator.rb +48 -0
  19. data/app/validators/releaf/content/node/root_validator.rb +43 -0
  20. data/app/validators/releaf/content/node/singleness_validator.rb +102 -0
  21. data/app/views/releaf/content/nodes/content_type_dialog.ruby +1 -0
  22. data/app/views/releaf/content/nodes/copy_dialog.ruby +1 -0
  23. data/app/views/releaf/content/nodes/go_to_dialog.ruby +1 -0
  24. data/app/views/releaf/content/nodes/move_dialog.ruby +1 -0
  25. data/lib/releaf-content.rb +6 -0
  26. data/lib/releaf/content/acts_as_node.rb +73 -0
  27. data/lib/releaf/content/acts_as_node/action_controller/acts/node.rb +17 -0
  28. data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +55 -0
  29. data/lib/releaf/content/builders_autoload.rb +18 -0
  30. data/lib/releaf/content/engine.rb +40 -0
  31. data/lib/releaf/content/node.rb +280 -0
  32. data/lib/releaf/content/node_mapper.rb +9 -0
  33. data/lib/releaf/content/route.rb +93 -0
  34. data/lib/releaf/content/router_proxy.rb +23 -0
  35. data/releaf-content.gemspec +20 -0
  36. data/spec/builders/content/nodes/content_form_builder_spec.rb +24 -0
  37. data/spec/builders/content/nodes/form_builder_spec.rb +218 -0
  38. data/spec/builders/content/nodes/toolbox_builder_spec.rb +108 -0
  39. data/spec/controllers/releaf/content/nodes_controller_spec.rb +21 -0
  40. data/spec/features/nodes_spec.rb +239 -0
  41. data/spec/lib/releaf/content/acts_as_node_spec.rb +118 -0
  42. data/spec/lib/releaf/content/node_spec.rb +779 -0
  43. data/spec/lib/releaf/content/route_spec.rb +85 -0
  44. data/spec/middleware/routes_reloader_spec.rb +48 -0
  45. data/spec/routing/node_mapper_spec.rb +142 -0
  46. data/spec/validators/content/node/parent_validator_spec.rb +56 -0
  47. data/spec/validators/content/node/root_validator_spec.rb +69 -0
  48. data/spec/validators/content/node/singleness_validator_spec.rb +145 -0
  49. metadata +145 -0
@@ -0,0 +1,85 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Content::Route do
4
+ let(:node_route) { FactoryGirl.build(:node_route, node_id: 12, locale: "en", path: "/en") }
5
+
6
+ describe ".node_class" do
7
+ it "returns ::Node" do
8
+ expect( described_class.node_class ).to eq ::Node
9
+ end
10
+ end
11
+
12
+ describe ".node_class_default_controller" do
13
+ context "when given node class inherits `ActionController::Base`" do
14
+ it "returns undercored, stripped down controller class" do
15
+ expect(described_class.node_class_default_controller(HomePagesController)).to eq("home_pages")
16
+ end
17
+ end
18
+
19
+ context "when given node class does not inherit `ActionController::Base`" do
20
+ it "returns pluralized, underscorized class" do
21
+ expect(described_class.node_class_default_controller(TextPage)).to eq("text_pages")
22
+ end
23
+ end
24
+ end
25
+
26
+ describe ".for" do
27
+ before do
28
+ create(:home_page_node)
29
+ end
30
+
31
+ it "returns an array" do
32
+ expect(described_class.for(HomePage, 'foo').class).to eq(Array)
33
+ end
34
+
35
+ context "when databse doesn't exists" do
36
+ it "returns an empty array" do
37
+ allow(described_class.node_class).to receive(:where).and_raise(ActiveRecord::NoDatabaseError.new("xxx"))
38
+ expect(described_class.for(HomePage, 'foo')).to eq([])
39
+ end
40
+ end
41
+
42
+ context "when releaf_nodes table doesn't exists" do
43
+ it "returns an empty array" do
44
+ allow(described_class.node_class).to receive(:where).and_raise(ActiveRecord::StatementInvalid.new("xxx"))
45
+ expect(described_class.for(HomePage, 'foo')).to eq([])
46
+ end
47
+ end
48
+
49
+ context "when releaf_nodes table exists" do
50
+ it "returns an array of Node::Route objects" do
51
+ result = described_class.for(HomePage, 'foo')
52
+ expect(result.count).to eq(1)
53
+ expect(result.first.class).to eq(described_class)
54
+ end
55
+
56
+ context "when node is not available" do
57
+ it "does not include it in return" do
58
+ allow_any_instance_of(Node).to receive(:available?).and_return(false)
59
+ expect(described_class.for(HomePage, 'foo')).to eq([])
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#params' do
66
+ it "returns params for router method" do
67
+ expect(node_route.params("home#index")).to eq ['/en', {:node_id=>"12", :locale=>"en", :to=>"home#index"}]
68
+ end
69
+
70
+ context "when :as given in args" do
71
+ context "when node has a locale" do
72
+ it "prepends locale to :as" do
73
+ expect(node_route.params("home#index", as: "home").last).to match hash_including(as: "en_home")
74
+ end
75
+ end
76
+
77
+ context "when node does not have a locale" do
78
+ it "doesn't modify :as option" do
79
+ node_route.locale = nil
80
+ expect(node_route.params("home#index", as: "home").last).to match hash_including(as: "home")
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,48 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Content::RoutesReloader do
4
+ let(:app) { ->(env) { [200, env, "app"] } }
5
+
6
+ let :request do
7
+ described_class.new(app).call(Rack::MockRequest.env_for('http://example.com'))
8
+ end
9
+
10
+ describe "on application startup" do
11
+ it "sets routes loading time" do
12
+ expect(described_class.routes_loaded).to_not be_nil
13
+ end
14
+ end
15
+
16
+ describe "on each request" do
17
+ it "compares latest updates" do
18
+ expect(described_class).to receive(:reload_if_expired)
19
+ request
20
+ end
21
+ end
22
+
23
+ describe ".reload_if_expired" do
24
+ context "when no nodes exists" do
25
+ it "does not reload routes" do
26
+ allow(Node).to receive(:updated_at).and_return(nil)
27
+ expect(Rails.application).to_not receive(:reload_routes!)
28
+ described_class.reload_if_expired
29
+ end
30
+ end
31
+
32
+ context "when routes is up to date" do
33
+ it "does not reload routes" do
34
+ allow(Node).to receive(:updated_at).and_return(Time.parse("1991-01-01"))
35
+ expect(Rails.application).to_not receive(:reload_routes!)
36
+ described_class.reload_if_expired
37
+ end
38
+ end
39
+
40
+ context "when routes is outdated" do
41
+ it "reloads routes" do
42
+ allow(Node).to receive(:updated_at).and_return(Time.now)
43
+ expect(Rails.application).to receive(:reload_routes!)
44
+ described_class.reload_if_expired
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,142 @@
1
+ require "rails_helper"
2
+
3
+ describe Releaf::Content::NodeMapper do
4
+ after(:all) do
5
+ # reset dummy app routes
6
+ Dummy::Application.reload_routes!
7
+ end
8
+
9
+ before do
10
+ Dummy::Application.reload_routes!
11
+ @text_page = FactoryGirl.create(:text_page)
12
+ @lv_root = create(:home_page_node, name: "lv", locale: "lv", slug: 'lv')
13
+ @node = FactoryGirl.create(:node, slug: 'test-page', content: @text_page, parent: @lv_root)
14
+ end
15
+
16
+ describe "#releaf_routes_for" do
17
+
18
+ describe "using current node path" do
19
+ example "using default controller " do
20
+ routes.draw do
21
+ releaf_routes_for(TextPage) do |route|
22
+ get 'show'
23
+ delete :destroy
24
+ end
25
+ end
26
+
27
+ expect(get: '/lv/test-page').to route_to(
28
+ "controller" => "text_pages",
29
+ "action" => "show",
30
+ "node_id" => @node.id.to_s,
31
+ "locale" => 'lv'
32
+ )
33
+
34
+ expect(delete: '/lv/test-page').to route_to(
35
+ "controller" => "text_pages",
36
+ "action" => "destroy",
37
+ "node_id" => @node.id.to_s,
38
+ "locale" => 'lv'
39
+ )
40
+ end
41
+
42
+ example "overriding default controller" do
43
+ routes.draw do
44
+ releaf_routes_for(TextPage, controller: 'home_pages') do |route|
45
+ get 'show'
46
+ delete :destroy
47
+ end
48
+ end
49
+
50
+ expect(get: '/lv/test-page').to route_to(
51
+ "controller" => "home_pages",
52
+ "action" => "show",
53
+ "node_id" => @node.id.to_s,
54
+ "locale" => 'lv'
55
+ )
56
+ end
57
+
58
+ example "specifying different than default controller for route" do
59
+ routes.draw do
60
+ releaf_routes_for(TextPage, controller: 'doesnt_matter') do |route|
61
+ get 'home_pages#hide'
62
+ end
63
+ end
64
+
65
+ expect(get: '/lv/test-page').to route_to(
66
+ "controller" => "home_pages",
67
+ "action" => "hide",
68
+ "node_id" => @node.id.to_s,
69
+ "locale" => 'lv'
70
+ )
71
+ end
72
+ end
73
+
74
+ describe "adding uri parts to node path" do
75
+ example "speciffying custom controller and action" do
76
+ routes.draw do
77
+ releaf_routes_for(TextPage) do |route|
78
+ get ':my_id/list', to: 'home_pages#list'
79
+ end
80
+ end
81
+
82
+ expect(get: '/lv/test-page/8888/list').to route_to(
83
+ "controller" => "home_pages",
84
+ "action" => "list",
85
+ "node_id" => @node.id.to_s,
86
+ "locale" => 'lv',
87
+ "my_id" => '8888'
88
+ )
89
+ end
90
+
91
+ example "speciffying action" do
92
+ routes.draw do
93
+ releaf_routes_for(TextPage) do |route|
94
+ get ':my_id/list', to: '#list'
95
+ end
96
+ end
97
+
98
+ expect(get: '/lv/test-page/8888/list').to route_to(
99
+ "controller" => "text_pages",
100
+ "action" => "list",
101
+ "node_id" => @node.id.to_s,
102
+ "locale" => 'lv',
103
+ "my_id" => '8888'
104
+ )
105
+ end
106
+
107
+ example "speciffying action and overriding default controller" do
108
+ routes.draw do
109
+ releaf_routes_for(TextPage, controller: 'home_pages') do |route|
110
+ get ':my_id/list', to: '#list'
111
+ end
112
+ end
113
+
114
+ expect(get: '/lv/test-page/8888/list').to route_to(
115
+ "controller" => "home_pages",
116
+ "action" => "list",
117
+ "node_id" => @node.id.to_s,
118
+ "locale" => 'lv',
119
+ "my_id" => '8888'
120
+ )
121
+ end
122
+
123
+ example "speciffying custom path" do
124
+ routes.draw do
125
+ releaf_routes_for(TextPage) do |route|
126
+ get 'home_pages#show', path: "#{route.path}/abc/:my_id"
127
+ end
128
+ end
129
+
130
+ expect(get: '/lv/test-page/abc/333').to route_to(
131
+ "controller" => "home_pages",
132
+ "action" => "show",
133
+ "node_id" => @node.id.to_s,
134
+ "locale" => 'lv',
135
+ "my_id" => '333'
136
+ )
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,56 @@
1
+ require 'rails_helper'
2
+
3
+ describe Releaf::Content::Node::ParentValidator do
4
+ let!(:root_node) { FactoryGirl.create(:node, content_type: 'HomePage') }
5
+
6
+ class DummyNodeParentValidatorModel < ActiveRecord::Base
7
+ acts_as_node
8
+ self.table_name = 'texts'
9
+ end
10
+
11
+ class DummyNodeParentValidator1Controller < ActionController::Base
12
+ acts_as_node
13
+ end
14
+
15
+ class DummyNodeParentValidator2Controller < ActionController::Base
16
+ acts_as_node
17
+ end
18
+
19
+
20
+ class DummyNodeParentValidatorNode < ActiveRecord::Base
21
+ self.table_name = 'nodes'
22
+ include Releaf::Content::Node
23
+ validates_with Releaf::Content::Node::ParentValidator, for: DummyNodeParentValidatorModel, under: DummyNodeParentValidator1Controller
24
+ end
25
+
26
+ context "when parent is valid" do
27
+ it "doesn't add error" do
28
+ parent = DummyNodeParentValidatorNode.create!( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator1Controller') )
29
+ child = DummyNodeParentValidatorNode.new( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidatorModel', parent_id: parent.id) )
30
+
31
+ expect( child ).to be_valid
32
+ end
33
+ end
34
+
35
+ context "when parent is invalid" do
36
+ it "adds error on content_type" do
37
+ parent = DummyNodeParentValidatorNode.create!( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator2Controller') )
38
+ child = DummyNodeParentValidatorNode.new( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidatorModel', parent_id: parent.id) )
39
+
40
+ expect( child ).to be_invalid
41
+ expect( child.errors[:content_type].size ).to eq(1)
42
+ expect( child.errors[:content_type] ).to include("invalid parent node")
43
+ end
44
+
45
+ end
46
+
47
+ context "when content_type is not in child list" do
48
+ it "doesn't add error" do
49
+ parent = DummyNodeParentValidatorNode.create!( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator1Controller') )
50
+ child = DummyNodeParentValidatorNode.new( FactoryGirl.attributes_for(:node, content_type: 'DummyNodeParentValidator2Controller', parent_id: parent.id) )
51
+
52
+ expect( child ).to be_valid
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,69 @@
1
+ require 'rails_helper'
2
+
3
+ describe Releaf::Content::Node::RootValidator do
4
+
5
+
6
+ class DummyRootValidatorController < ActionController::Base
7
+ acts_as_node
8
+ end
9
+
10
+ class DummyRootValidator2Controller < ActionController::Base
11
+ acts_as_node
12
+ end
13
+
14
+ class DummyRootValidatorNode < ActiveRecord::Base
15
+ self.table_name = 'nodes'
16
+ include Releaf::Content::Node
17
+ validates_with Releaf::Content::Node::RootValidator, allow: DummyRootValidatorController
18
+ end
19
+
20
+
21
+ def create_node *params
22
+ DummyRootValidatorNode.create!( FactoryGirl.attributes_for(:node, *params) )
23
+ end
24
+
25
+ def build_node *params
26
+ DummyRootValidatorNode.new( FactoryGirl.attributes_for(:node, *params) )
27
+ end
28
+
29
+ context "when node is allowed to be root node" do
30
+ context "when node is a root node" do
31
+ it "doesn't add an error" do
32
+ root_node = build_node(content_type: 'DummyRootValidatorController')
33
+ expect( root_node ).to be_valid
34
+ end
35
+ end
36
+
37
+ context "when node is not a root node" do
38
+ it "adds an error" do
39
+ root_node = create_node(content_type: 'DummyRootValidatorController')
40
+ subnode = build_node(content_type: 'DummyRootValidatorController', parent: root_node)
41
+ expect( subnode ).to be_invalid
42
+ expect( subnode.errors[:content_type].size ).to eq(1)
43
+ expect( subnode.errors[:content_type] ).to include("can't be subnode")
44
+ end
45
+ end
46
+ end
47
+
48
+ context "when node is not allowed to be a root node" do
49
+ context "when node is a root node" do
50
+ it "adds an error" do
51
+ root_node = build_node(content_type: 'DummyRootValidator2Controller')
52
+ expect( root_node ).to be_invalid
53
+ expect( root_node.errors[:content_type].size ).to eq(1)
54
+ expect( root_node.errors[:content_type] ).to include("can't be root node")
55
+ end
56
+ end
57
+
58
+ context "when node is not a root node" do
59
+ it "doesn't add an error" do
60
+ root_node = create_node(content_type: 'DummyRootValidatorController')
61
+ subnode = build_node(content_type: 'DummyRootValidator2Controller', parent: root_node)
62
+
63
+ expect( subnode ).to be_valid
64
+ end
65
+ end
66
+ end
67
+
68
+
69
+ end
@@ -0,0 +1,145 @@
1
+ require 'rails_helper'
2
+
3
+ describe Releaf::Content::Node::SinglenessValidator do
4
+
5
+
6
+ class DummySinglenessValidatorModel < ActiveRecord::Base
7
+ acts_as_node
8
+ self.table_name = 'text_pages'
9
+ end
10
+
11
+ class DummySinglenessValidator2Model < ActiveRecord::Base
12
+ acts_as_node
13
+ self.table_name = 'text_pages'
14
+ end
15
+
16
+ class DummySinglenessValidatorController < ActionController::Base
17
+ acts_as_node
18
+ end
19
+
20
+ class DummySinglenessValidator2Controller < ActionController::Base
21
+ acts_as_node
22
+ end
23
+
24
+ class DummySinglenessValidatorNode < ActiveRecord::Base
25
+ self.table_name = 'nodes'
26
+ include Releaf::Content::Node
27
+ validates_with Releaf::Content::Node::SinglenessValidator, for: DummySinglenessValidatorModel
28
+ validates_with Releaf::Content::Node::SinglenessValidator, for: DummySinglenessValidator2Controller, under: TextPage
29
+ end
30
+
31
+
32
+ def create_node *params
33
+ DummySinglenessValidatorNode.create!( FactoryGirl.attributes_for(:node, *params) )
34
+ end
35
+
36
+ def build_node *params
37
+ DummySinglenessValidatorNode.new( FactoryGirl.attributes_for(:node, *params) )
38
+ end
39
+
40
+ let!(:root_node) { create_node(content_type: 'HomePage') }
41
+
42
+
43
+ context "when scope is entire page" do
44
+
45
+ context "When node not mentioned in list" do
46
+ it "doesn't add error" do
47
+ node = build_node(content_type: 'DummySinglenessValidatorController', parent_id: root_node.id)
48
+ expect( node ).to be_valid
49
+ end
50
+ end
51
+
52
+
53
+ context "when node with given content doesn't exist in tree" do
54
+ it "doesn't add error" do
55
+ node = build_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
56
+ expect( node ).to be_valid
57
+ end
58
+ end
59
+
60
+ context "when node with given content exists in tree" do
61
+ before do
62
+ create_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
63
+ end
64
+
65
+ it "adds error to #content_type" do
66
+ node = build_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
67
+ expect( node ).to be_invalid
68
+ expect( node.errors[:content_type].size ).to eq(1)
69
+ expect( node.errors[:content_type] ).to include("node exists")
70
+ end
71
+
72
+ end
73
+
74
+ context "when node is saved, and is only one in the tree" do
75
+ it "doesn't add error" do
76
+ node = create_node(content_type: 'DummySinglenessValidatorModel', parent_id: root_node.id)
77
+ expect( node ).to be_valid
78
+ end
79
+ end
80
+ end
81
+
82
+ context "when scope is subtree" do
83
+ context "when has ancestor in :under list" do
84
+ let!(:grand_parent_node) { create_node(content_type: 'TextPage', parent_id: root_node.id) }
85
+ let!(:parent_node) { create_node(content_type: 'DummySinglenessValidator2Model', parent_id: grand_parent_node.id) }
86
+
87
+ context "when node with given content doesn't exist in subtree" do
88
+ it "doesn't add error" do
89
+ node = build_node(content_type: 'DummySinglenessValidator2Controller', parent_id: parent_node.id)
90
+ expect( node ).to be_valid
91
+ end
92
+ end
93
+
94
+ context "when node with given content exists in subtree" do
95
+ before do
96
+ create_node(content_type: 'DummySinglenessValidator2Controller', parent_id: grand_parent_node.id)
97
+ end
98
+
99
+ it "adds error to #content_type" do
100
+ node = build_node(content_type: 'DummySinglenessValidator2Controller', parent_id: parent_node.id)
101
+ expect( node ).to be_invalid
102
+ expect( node.errors[:content_type].size ).to eq(1)
103
+ expect( node.errors[:content_type] ).to include("node exists")
104
+ end
105
+
106
+ end
107
+
108
+ context "when node is saved, and is only one in subtree" do
109
+ it "doesn't add error" do
110
+ node = create_node(content_type: 'DummySinglenessValidator2Controller', parent_id: parent_node.id)
111
+ expect( node ).to be_valid
112
+ end
113
+ end
114
+
115
+
116
+ end
117
+
118
+ context "when node has no ancestor in :under list" do
119
+ it "doesn't add error" do
120
+ node = create_node(content_type: 'DummySinglenessValidator2Controller', parent_id: root_node.id)
121
+ expect( node ).to be_valid
122
+ end
123
+ end
124
+ end
125
+
126
+ context "regression tests" do
127
+ context "@node.parent.self_and_ancestors bug" do
128
+ it "works correctly / is worked around" do
129
+ # for details see Releaf::Content::Node::SinglenessValidator#base_relation_for_subtree
130
+ @node1 = create_node(content_type: 'TextPage', locale: 'en')
131
+ @node2 = create_node(content_type: 'TextPage', locale: 'lv')
132
+ @node3 = create_node(content_type: 'TextPage', locale: 'ru')
133
+ @node4 = create_node(content_type: 'TextPage', locale: 'sp')
134
+
135
+ @node1_1 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node1)
136
+ expect do
137
+ @node1_2 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node2)
138
+ @node1_3 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node3)
139
+ @node1_4 = create_node(content_type: 'DummySinglenessValidator2Controller', parent: @node4)
140
+ end.to_not raise_error
141
+ end
142
+ end
143
+ end
144
+
145
+ end