releaf-content 0.2.1 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -21
  3. data/app/assets/javascripts/{releaf/controllers → controllers}/releaf/content/nodes.js +0 -0
  4. data/app/assets/stylesheets/{releaf/controllers → controllers}/releaf/content/nodes.scss +0 -0
  5. data/app/builders/releaf/content/builders/action_dialog.rb +9 -1
  6. data/app/builders/releaf/content/builders/tree.rb +1 -1
  7. data/app/builders/releaf/content/content_type_dialog_builder.rb +2 -2
  8. data/app/builders/releaf/content/nodes/form_builder.rb +3 -3
  9. data/app/builders/releaf/content/nodes/index_builder.rb +1 -1
  10. data/app/builders/releaf/content/nodes/toolbox_builder.rb +0 -5
  11. data/app/controllers/releaf/content/nodes_controller.rb +109 -129
  12. data/app/middleware/releaf/content/routes_reloader.rb +12 -4
  13. data/app/services/releaf/content/node/copy.rb +90 -0
  14. data/app/services/releaf/content/node/move.rb +21 -0
  15. data/app/services/releaf/content/node/save_under_parent.rb +22 -0
  16. data/app/services/releaf/content/node/service.rb +17 -0
  17. data/app/validators/releaf/content/node/singleness_validator.rb +1 -24
  18. data/lib/releaf-content.rb +85 -6
  19. data/lib/releaf/content/acts_as_node.rb +0 -5
  20. data/lib/releaf/content/acts_as_node/active_record/acts/node.rb +2 -8
  21. data/lib/releaf/content/configuration.rb +61 -0
  22. data/lib/releaf/content/engine.rb +1 -34
  23. data/lib/releaf/content/node.rb +30 -101
  24. data/lib/releaf/content/node_mapper.rb +28 -2
  25. data/lib/releaf/content/route.rb +37 -25
  26. data/spec/builders/content/nodes/action_dialog_spec.rb +39 -0
  27. data/spec/builders/content/nodes/form_builder_spec.rb +47 -3
  28. data/spec/builders/content/nodes/toolbox_builder_spec.rb +1 -32
  29. data/spec/controllers/releaf/content/nodes_controller_spec.rb +42 -11
  30. data/spec/features/nodes_services_spec.rb +207 -0
  31. data/spec/features/nodes_spec.rb +328 -30
  32. data/spec/lib/releaf/content/acts_as_node_spec.rb +4 -32
  33. data/spec/lib/releaf/content/configuration_spec.rb +159 -0
  34. data/spec/lib/releaf/content/engine_spec.rb +149 -0
  35. data/spec/lib/releaf/content/node_spec.rb +66 -324
  36. data/spec/lib/releaf/content/route_spec.rb +223 -34
  37. data/spec/middleware/routes_reloader_spec.rb +62 -14
  38. data/spec/routing/node_mapper_spec.rb +223 -55
  39. data/spec/services/releaf/content/node/copy_spec.rb +115 -0
  40. data/spec/services/releaf/content/node/move_spec.rb +20 -0
  41. data/spec/services/releaf/content/node/save_under_parent_spec.rb +49 -0
  42. data/spec/services/releaf/content/node/service_spec.rb +19 -0
  43. metadata +38 -21
  44. data/app/builders/releaf/content/go_to_dialog_builder.rb +0 -9
  45. data/app/views/releaf/content/nodes/go_to_dialog.ruby +0 -1
  46. data/lib/releaf/content/builders_autoload.rb +0 -18
  47. data/releaf-content.gemspec +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a8d9437be334b0617ec00d8c8e3e4ea14b35dc6
4
- data.tar.gz: 095e4645884b45c38afb81fa0157b80454e14027
3
+ metadata.gz: a92a25dd1de2d98500cd222715e05dd21e94cb18
4
+ data.tar.gz: a7848332149b045a748fc54c0902b196bdd5fa78
5
5
  SHA512:
6
- metadata.gz: ce189035ffd84c6abdea56c57ad9d3a5d55b7074ab458d746b4faa7374b67b1e37739db42d71609b449fd1ae34c85d072cf1eeeb73c8c4da627ceb48e41980d9
7
- data.tar.gz: 65a20465658ed0a1edef3723b909fe6d5522e46546dfbb321176a888428b7ed6956ee97d9ebb5141ff75cf306d938949be98f0da0057a77d725f93cfe507dca6
6
+ metadata.gz: 88f2a678dd20c27235a12788e5b35df3472afc85770592b60f89fed4b4ce1986351b7ed1309228e02eeac60cd56cefe4b02c21dd4c20fe9e22d6baa9cdf8012b
7
+ data.tar.gz: c80f6a024ac41fccc540b88259485ddc7afd94dc40bba9e640aed34ce62926629156e19ab8225b18280b0c1c21984249ad44ce768c7b07e4cf5c7ce3cded7152
data/LICENSE CHANGED
@@ -1,24 +1,22 @@
1
1
  Copyright (c) 2012, CubeSystems <info@cubesystems.lv>
2
- All rights reserved.
3
2
 
4
- Redistribution and use in source and binary forms, with or without
5
- modification, are permitted provided that the following conditions are met:
6
- * Redistributions of source code must retain the above copyright
7
- notice, this list of conditions and the following disclaimer.
8
- * Redistributions in binary form must reproduce the above copyright
9
- notice, this list of conditions and the following disclaimer in the
10
- documentation and/or other materials provided with the distribution.
11
- * Neither the name of the CubeSystems nor the names of its contributors may
12
- be used to endorse or promote products derived from this software without
13
- specific prior written permission.
3
+ MIT License
14
4
 
15
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
- DISCLAIMED. IN NO EVENT SHALL CubeSystems BE LIABLE FOR ANY
19
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -53,8 +53,16 @@ module Releaf::Content::Builders
53
53
  super << confirm_button
54
54
  end
55
55
 
56
+ def confirm_button_text
57
+ t(action.to_s.humanize)
58
+ end
59
+
60
+ def confirm_button_attributes
61
+ {class: "primary", type: "submit", data: { type: 'ok', disable: true}}
62
+ end
63
+
56
64
  def confirm_button
57
- button(t(action), "check", class: "primary", type: "submit", data: { type: 'ok', disable: true })
65
+ button(confirm_button_text, "check", confirm_button_attributes)
58
66
  end
59
67
  end
60
68
  end
@@ -56,7 +56,7 @@ module Releaf::Content::Builders
56
56
  def tree_resource_collapser(resource, expanded)
57
57
  return if resource.children.empty?
58
58
  tag(:div, class: "collapser-cell") do
59
- button(nil, (expanded ? 'chevron-down' : 'chevron-right'), class: %w(secondary collapser trigger), title: t(expanded ? "collapse" : "expand"))
59
+ button(nil, (expanded ? 'chevron-down' : 'chevron-right'), class: %w(secondary collapser trigger), title: t(expanded ? "Collapse" : "Expand"))
60
60
  end
61
61
  end
62
62
 
@@ -54,7 +54,7 @@ module Releaf::Content
54
54
  end
55
55
 
56
56
  def content_type_item(content_type)
57
- url = url_for(controller: "/releaf/content/nodes", action: "new", parent_id: params[:parent_id], content_type: content_type.name)
57
+ url = url_for(controller: controller.controller_path, action: "new", parent_id: params[:parent_id], content_type: content_type.name)
58
58
  tag(:li) do
59
59
  link_to(I18n.t(content_type.name.underscore, scope: 'admin.content_types'), url)
60
60
  end
@@ -68,7 +68,7 @@ module Releaf::Content
68
68
  end
69
69
 
70
70
  def section_header_text
71
- t("Add new node")
71
+ t("Create new resource")
72
72
  end
73
73
  end
74
74
  end
@@ -54,7 +54,7 @@ module Releaf::Content::Nodes
54
54
  end
55
55
 
56
56
  def render_slug
57
- url = url_for(controller: "/releaf/content/nodes", action: "generate_url", parent_id: object.parent_id, id: object.id)
57
+ url = url_for(controller: controller.controller_path, action: "generate_url", parent_id: object.parent_id, id: object.id)
58
58
  input = {
59
59
  data: {'generator-url' => url}
60
60
  }
@@ -90,13 +90,13 @@ module Releaf::Content::Nodes
90
90
  end
91
91
 
92
92
  def slug_base_url
93
- "#{request.protocol}#{request.host_with_port}#{object.parent.try(:path)}/"
93
+ "#{request.protocol}#{request.host_with_port}#{object.parent.try(:path)}" + (object.trailing_slash_for_path? ? "" : "/")
94
94
  end
95
95
 
96
96
  def slug_link
97
97
  link_to(object.path) do
98
98
  safe_join do
99
- [slug_base_url, tag(:span, object.slug), '/']
99
+ [slug_base_url, tag(:span, object.slug), (object.trailing_slash_for_path? ? "/" : "")]
100
100
  end
101
101
  end
102
102
  end
@@ -9,7 +9,7 @@ module Releaf::Content::Nodes
9
9
 
10
10
  def tree_resource_toolbox(resource)
11
11
  tag(:div, class: "only-icon toolbox-cell") do
12
- toolbox(resource, index_url: index_url)
12
+ toolbox(resource, index_path: index_path)
13
13
  end
14
14
  end
15
15
 
@@ -5,7 +5,6 @@ module Releaf::Content::Nodes
5
5
 
6
6
  unless resource.new_record?
7
7
  list << add_child_button
8
- list << go_to_button unless params[:context] == "index"
9
8
  list << copy_button
10
9
  list << move_button
11
10
  end
@@ -17,10 +16,6 @@ module Releaf::Content::Nodes
17
16
  button(t('Add child'), nil, class: "ajaxbox", href: url_for(action: "content_type_dialog", parent_id: resource.id))
18
17
  end
19
18
 
20
- def go_to_button
21
- button(t('Go to'), nil, class: "ajaxbox", href: url_for(action: "go_to_dialog"))
22
- end
23
-
24
19
  def copy_button
25
20
  button(t('Copy'), nil, class: "ajaxbox", href: url_for(action: "copy_dialog", id: resource.id))
26
21
  end
@@ -1,166 +1,146 @@
1
- module Releaf::Content
2
- class NodesController < Releaf::BaseController
3
- respond_to :json, only: [:create, :update, :copy, :move]
1
+ class Releaf::Content::NodesController < Releaf::ActionController
2
+ respond_to :json, only: [:create, :update, :copy, :move]
4
3
 
5
- def setup
6
- super
7
- @features[:create_another] = false
8
- end
9
-
10
- def generate_url
11
- tmp_resource = prepare_resource
12
- tmp_resource.name = params[:name]
13
- tmp_resource.reasign_slug
14
-
15
- respond_to do |format|
16
- format.js { render text: tmp_resource.slug }
17
- end
18
- end
4
+ def features
5
+ super - [:create_another, :search]
6
+ end
19
7
 
20
- def content_type_dialog
21
- @content_types = resource_class.valid_node_content_classes(params[:parent_id]).sort do |a, b|
22
- I18n.t(a.name.underscore, scope: 'admin.content_types') <=> I18n.t(b.name.underscore, scope: 'admin.content_types')
23
- end
24
- end
8
+ def generate_url
9
+ tmp_resource = prepare_resource
10
+ tmp_resource.name = params[:name]
11
+ tmp_resource.reasign_slug
25
12
 
26
- def builder_scopes
27
- [node_builder_scope] + super
13
+ respond_to do |format|
14
+ format.js { render text: tmp_resource.slug }
28
15
  end
16
+ end
29
17
 
30
- def node_builder_scope
31
- [application_scope, "Nodes"].reject(&:blank?).join("::")
18
+ def content_type_dialog
19
+ @content_types = resource_class.valid_node_content_classes(params[:parent_id]).sort do |a, b|
20
+ I18n.t(a.name.underscore, scope: 'admin.content_types') <=> I18n.t(b.name.underscore, scope: 'admin.content_types')
32
21
  end
22
+ end
33
23
 
34
- def copy_dialog
35
- copy_move_dialog_common
36
- end
24
+ def copy_dialog
25
+ copy_move_dialog_common
26
+ end
37
27
 
38
- def move_dialog
39
- copy_move_dialog_common
40
- end
28
+ def move_dialog
29
+ copy_move_dialog_common
30
+ end
41
31
 
42
- def copy
43
- copy_move_common do |resource|
44
- resource.copy params[:new_parent_id]
45
- end
32
+ def copy
33
+ copy_move_common do |resource|
34
+ resource.copy params[:new_parent_id]
46
35
  end
36
+ end
47
37
 
48
- def move
49
- copy_move_common do |resource|
50
- resource.move params[:new_parent_id]
51
- end
38
+ def move
39
+ copy_move_common do |resource|
40
+ resource.move params[:new_parent_id]
52
41
  end
42
+ end
53
43
 
54
- def go_to_dialog
55
- @collection = resource_class.roots
56
-
57
- respond_to do |format|
58
- format.html do
59
- render layout: nil
60
- end
61
- end
44
+ # override base_controller method for adding content tree ancestors
45
+ # to breadcrumbs
46
+ def add_resource_breadcrumb(resource)
47
+ ancestor_nodes(resource).each do |node|
48
+ @breadcrumbs << { name: node, url: url_for( action: :edit, id: node.id ) }
62
49
  end
50
+ super
51
+ end
63
52
 
64
- # override base_controller method for adding content tree ancestors
65
- # to breadcrumbs
66
- def add_resource_breadcrumb(resource)
67
- ancestors = []
68
- if resource.new_record?
69
- if resource.parent_id
70
- ancestors = resource.parent.ancestors
71
- ancestors += [resource.parent]
72
- end
73
- else
74
- ancestors = resource.ancestors
75
- end
76
-
77
- ancestors.each do |ancestor|
78
- @breadcrumbs << { name: ancestor, url: url_for( action: :edit, id: ancestor.id ) }
79
- end
80
-
81
- super
53
+ def ancestor_nodes(resource)
54
+ if resource.persisted?
55
+ resource.ancestors.reorder(:depth)
56
+ elsif resource.new_record? && resource.parent
57
+ resource.parent.ancestors.reorder(:depth) + [resource.parent]
58
+ else
59
+ []
82
60
  end
61
+ end
83
62
 
84
- def self.resource_class
85
- ::Node
86
- end
63
+ def self.resource_class
64
+ # find first key in content resources config that has this class as controller
65
+ model_class_name = Releaf::Content.resources.find{|_model_name, options| options[:controller] == name }.first
66
+ model_class_name.constantize
67
+ end
87
68
 
88
- protected
69
+ protected
89
70
 
90
- def prepare_index
91
- @collection = resource_class.roots
92
- end
71
+ def prepare_index
72
+ @collection = resource_class.roots
73
+ end
93
74
 
94
- private
75
+ private
95
76
 
96
- def copy_move_common(&block)
97
- @resource = resource_class.find(params[:id])
77
+ def copy_move_common
78
+ @resource = resource_class.find(params[:id])
98
79
 
99
- if params[:new_parent_id].nil?
100
- @resource.errors.add(:base, 'parent not selected')
101
- respond_with(@resource)
80
+ if params[:new_parent_id].nil?
81
+ @resource.errors.add(:base, 'parent not selected')
82
+ respond_with(@resource)
83
+ else
84
+ begin
85
+ @resource = yield(@resource)
86
+ rescue ActiveRecord::RecordInvalid => e
87
+ respond_with(e.record)
102
88
  else
103
- begin
104
- @resource = yield(@resource)
105
- rescue ActiveRecord::RecordInvalid => e
106
- respond_with(e.record)
107
- else
108
- resource_class.updated
109
- respond_with(@resource, redirect: true, location: url_for(action: :index))
110
- end
89
+ resource_class.updated
90
+ respond_with(@resource, redirect: true, location: url_for(action: :index))
111
91
  end
112
92
  end
93
+ end
113
94
 
114
- def action_responders
115
- super.merge(
116
- copy: Releaf::Core::Responders::AfterSaveResponder,
117
- move: Releaf::Core::Responders::AfterSaveResponder
118
- )
119
- end
95
+ def action_responders
96
+ super.merge(
97
+ copy: Releaf::Responders::AfterSaveResponder,
98
+ move: Releaf::Responders::AfterSaveResponder
99
+ )
100
+ end
120
101
 
121
- def copy_move_dialog_common
122
- @resource = resource_class.find params[:id]
123
- @collection = resource_class.roots
124
- end
102
+ def copy_move_dialog_common
103
+ @resource = resource_class.find params[:id]
104
+ @collection = resource_class.roots
105
+ end
125
106
 
126
- def prepare_resource
127
- if params[:id]
128
- resource_class.find(params[:id])
129
- elsif params[:parent_id].present?
130
- parent = resource_class.find(params[:parent_id])
131
- parent.children.new
132
- else
133
- resource_class.new
134
- end
107
+ def prepare_resource
108
+ if params[:id]
109
+ resource_class.find(params[:id])
110
+ elsif params[:parent_id].present?
111
+ parent = resource_class.find(params[:parent_id])
112
+ parent.children.new
113
+ else
114
+ resource_class.new
135
115
  end
116
+ end
136
117
 
137
- def new_resource
138
- super
139
- @resource.content_type = node_content_class.name
140
- @resource.parent_id = params[:parent_id]
141
- @resource.item_position ||= resource_class.children_max_item_position(@resource.parent) + 1
118
+ def new_resource
119
+ super
120
+ @resource.content_type = node_content_class.name
121
+ @resource.parent_id = params[:parent_id]
122
+ @resource.item_position ||= resource_class.children_max_item_position(@resource.parent) + 1
142
123
 
143
- if node_content_class < ActiveRecord::Base
144
- @resource.build_content
145
- @resource.content_id_will_change!
146
- end
124
+ if node_content_class < ActiveRecord::Base
125
+ @resource.build_content
126
+ @resource.content_id_will_change!
147
127
  end
128
+ end
148
129
 
149
- # Returns valid content type class
150
- def node_content_class
151
- raise ArgumentError, "invalid content_type" unless ActsAsNode.classes.include? params[:content_type]
152
- params[:content_type].constantize
153
- end
130
+ # Returns valid content type class
131
+ def node_content_class
132
+ raise ArgumentError, "invalid content_type" unless ActsAsNode.classes.include? params[:content_type]
133
+ params[:content_type].constantize
134
+ end
154
135
 
155
- def permitted_params
156
- list = super
157
- list += [{content_attributes: permitted_content_attributes}]
158
- list -= %w[content_type]
159
- list
160
- end
136
+ def permitted_params
137
+ list = super
138
+ list += [{content_attributes: permitted_content_attributes}]
139
+ list -= %w[content_type]
140
+ list
141
+ end
161
142
 
162
- def permitted_content_attributes
163
- @resource.content_class.acts_as_node_params if @resource.content_class.respond_to? :acts_as_node_params
164
- end
143
+ def permitted_content_attributes
144
+ @resource.content_class.acts_as_node_params if @resource.content_class.respond_to? :acts_as_node_params
165
145
  end
166
146
  end
@@ -6,20 +6,28 @@ module Releaf::Content
6
6
  end
7
7
 
8
8
  def call(env)
9
- self.class.reload_if_expired
9
+ self.class.reload_if_needed
10
10
  @app.call(env)
11
11
  end
12
12
 
13
+ def self.reset!
14
+ @updated_at = nil
15
+ end
16
+
13
17
  def self.routes_loaded
14
18
  @updated_at = Time.now
15
19
  end
16
20
 
17
- def self.reload_if_expired
18
- # TODO Node class should be configurable
19
- return unless ::Node.updated_at.present? && @updated_at && @updated_at < ::Node.updated_at
21
+ def self.reload_if_needed
22
+ return unless needs_reload?
20
23
  Rails.application.reload_routes!
21
24
  routes_loaded
22
25
  end
23
26
 
27
+ def self.needs_reload?
28
+ Releaf::Content.models.any? do | node_class |
29
+ node_class.updated_at.present? && (@updated_at.nil? || @updated_at < node_class.updated_at)
30
+ end
31
+ end
24
32
  end
25
33
  end