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.
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