scrivito_sdk 0.66.0 → 0.70.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/scrivito/blobs_controller.rb +5 -0
  3. data/app/controllers/scrivito/objs_controller.rb +2 -1
  4. data/app/controllers/scrivito/ui_controller.rb +33 -3
  5. data/app/helpers/scrivito_helper.rb +14 -20
  6. data/app/views/scrivito/objs/update.json.jbuilder +1 -1
  7. data/app/views/scrivito/ui/index.html.erb +1 -1
  8. data/app/views/scrivito/webservice/_workspace.json.jbuilder +2 -1
  9. data/config/ca-bundle.crt +1 -1
  10. data/config/precedence_routes.rb +3 -2
  11. data/lib/assets/javascripts/scrivito.js +46 -0
  12. data/lib/assets/javascripts/scrivito_ui.js +931 -501
  13. data/lib/assets/stylesheets/scrivito.css +1 -0
  14. data/lib/assets/stylesheets/scrivito_ui.css +1 -1
  15. data/lib/generators/scrivito/install/templates/app/views/page/index.html.erb +3 -3
  16. data/lib/generators/scrivito/page/page_generator.rb +3 -0
  17. data/lib/generators/scrivito/page/templates/model.erb +3 -0
  18. data/lib/generators/scrivito/widget/templates/model.erb +3 -0
  19. data/lib/generators/scrivito/widget/widget_generator.rb +3 -0
  20. data/lib/scrivito/attribute_content.rb +93 -60
  21. data/lib/scrivito/attribute_definition.rb +3 -3
  22. data/lib/scrivito/attribute_serializer.rb +2 -2
  23. data/lib/scrivito/backend/obj_data_cache.rb +22 -9
  24. data/lib/scrivito/basic_obj.rb +238 -130
  25. data/lib/scrivito/basic_widget.rb +32 -20
  26. data/lib/scrivito/binary.rb +74 -45
  27. data/lib/scrivito/binary_routing.rb +52 -0
  28. data/lib/scrivito/cache_middleware.rb +0 -5
  29. data/lib/scrivito/client_attribute_serializer.rb +134 -0
  30. data/lib/scrivito/cms_backend.rb +51 -33
  31. data/lib/scrivito/cms_data_cache.rb +1 -0
  32. data/lib/scrivito/cms_dispatch_controller.rb +1 -1
  33. data/lib/scrivito/cms_env.rb +1 -11
  34. data/lib/scrivito/cms_field_tag.rb +10 -7
  35. data/lib/scrivito/cms_rest_api/rate_limit.rb +5 -5
  36. data/lib/scrivito/cms_rest_api/request_timer.rb +27 -0
  37. data/lib/scrivito/cms_rest_api/widget_extractor.rb +8 -6
  38. data/lib/scrivito/cms_rest_api.rb +107 -54
  39. data/lib/scrivito/cms_routing.rb +13 -25
  40. data/lib/scrivito/configuration.rb +5 -2
  41. data/lib/scrivito/controller_actions.rb +68 -7
  42. data/lib/scrivito/controller_runtime.rb +8 -0
  43. data/lib/scrivito/date_attribute.rb +8 -0
  44. data/lib/scrivito/errors.rb +6 -3
  45. data/lib/scrivito/generator_helper.rb +13 -0
  46. data/lib/scrivito/image_tag.rb +24 -30
  47. data/lib/scrivito/layout_tags.rb +12 -6
  48. data/lib/scrivito/link.rb +46 -30
  49. data/lib/scrivito/log_subscriber.rb +15 -0
  50. data/lib/scrivito/membership.rb +3 -3
  51. data/lib/scrivito/membership_collection.rb +10 -10
  52. data/lib/scrivito/meta_data_collection.rb +22 -0
  53. data/lib/scrivito/model_library.rb +7 -1
  54. data/lib/scrivito/obj_collection.rb +18 -16
  55. data/lib/scrivito/obj_params_parser.rb +1 -1
  56. data/lib/scrivito/obj_search_enumerator.rb +35 -35
  57. data/lib/scrivito/page_config.rb +55 -0
  58. data/lib/scrivito/request_homepage.rb +23 -0
  59. data/lib/scrivito/routing_helper.rb +8 -8
  60. data/lib/scrivito/sdk_engine.rb +2 -3
  61. data/lib/scrivito/ui_config.rb +85 -0
  62. data/lib/scrivito/user.rb +30 -23
  63. data/lib/scrivito/user_definition.rb +48 -47
  64. data/lib/scrivito/widget_tag.rb +11 -0
  65. data/lib/scrivito/workspace.rb +93 -35
  66. data/lib/scrivito/workspace_selection_middleware.rb +8 -1
  67. data/lib/scrivito_sdk.rb +24 -24
  68. metadata +24 -33
  69. data/lib/assets/javascripts/scrivito_sdk.js +0 -57
  70. data/lib/assets/stylesheets/scrivito_sdk.css +0 -1
  71. data/lib/scrivito/client_config.rb +0 -113
  72. data/lib/scrivito/editing_context_helper.rb +0 -19
  73. data/lib/scrivito/named_link.rb +0 -39
@@ -20,10 +20,11 @@ module ControllerActions
20
20
  :show_widget,
21
21
  :widget_details,
22
22
  :page_details,
23
- :resource_details,
24
23
  ]
25
24
 
26
25
  before_filter :load_object
26
+
27
+ helper_method :scrivito_in_editable_view?
27
28
  end
28
29
 
29
30
  #
@@ -60,11 +61,70 @@ module ControllerActions
60
61
  render 'scrivito/page_details', layout: 'scrivito_dialog'
61
62
  end
62
63
 
63
- def resource_details
64
- assert_dialog_layout
65
- @scrivito_resource = editing_context.selected_workspace.objs
66
- .find_including_deleted(params[:resource_id])
67
- render text: '', layout: 'scrivito_dialog'
64
+ # How to handle widget errors in +production+.
65
+ #
66
+ # When an exception is raised from within a widget, the entire page is not available.
67
+ # Often, this is the case in production if the developer has forgotten to handle specific
68
+ # content scenarios such as empty date attributes, missing titles, unmanaged enum values, etc.
69
+ # but also for simple coding mistakes during development.
70
+ #
71
+ # Override this method to prevent the entire page from being unavailable due to widget errors.
72
+ #
73
+ # The overridden method allows to:
74
+ # * catch a widget error and replace it with an HTML placeholder.
75
+ # * report a widget error to an external service like Honeybadger or Airbrake.
76
+ #
77
+ # This method is _not_ called if Rails is in the +development+ or +test+ environment.
78
+ # In those environments, all widget errors are just raised.
79
+ #
80
+ # By default, this method just reraises the given error.
81
+ #
82
+ # @param widget [Scrivito::BasicWidget] the flawed widget
83
+ # @param error [StandardError] the error that occurred
84
+ # @raise [StandardError] if this method is not overridden, the +error+ passed to it is reraised.
85
+ #
86
+ # @example Notify external service about widget error and render an HTML placeholder:
87
+ # def on_scrivito_widget_error(widget, error)
88
+ # # Report error to external service (e.g. Honeybadger or Airbrake):
89
+ # Honeybadger.notify(error)
90
+ # notify_airbrake(error)
91
+ #
92
+ # # Replace corrupted widget output with a placeholder:
93
+ # message = "Rendering #{widget.description_for_editor} failed with #{error.message}"
94
+ # return "<!--#{message}-->".html_safe if Rails.env.production?
95
+ # "<p>#{message}</p>".html_safe
96
+ # end
97
+ # @api public
98
+ def on_scrivito_widget_error(widget, error)
99
+ raise error
100
+ end
101
+
102
+ #
103
+ # Returns whether the GUI is in the +editable+ view.
104
+ #
105
+ # +scrivito_in_editable_view?+ is also a helper method.
106
+ #
107
+ # @api public
108
+ # @return +true+ if the current visitor is an authenticated editor, the selected workspace is
109
+ # editable and the display mode is +editing+.
110
+ # @return +false+ otherwise.
111
+ #
112
+ def scrivito_in_editable_view?
113
+ editing_context.editable_display_mode?
114
+ end
115
+
116
+ #
117
+ # Returns the current user.
118
+ #
119
+ # +scrivito_user+ is also a helper method.
120
+ #
121
+ # @api public
122
+ # @return [Scrivito::User] if the {Scrivito::Configuration.editing_auth} callback returns an
123
+ # instance of {Scrivito::User}.
124
+ # @return +nil+ otherwise.
125
+ #
126
+ def scrivito_user
127
+ editing_context.editor
68
128
  end
69
129
 
70
130
  module ClassMethods
@@ -126,7 +186,8 @@ module ControllerActions
126
186
  # @api public
127
187
  #
128
188
  def deliver_file
129
- if url = @obj.binary_url
189
+ if binary = @obj.binary
190
+ url = BinaryRouting.new(request, scrivito_engine).resolved_binary_obj_url(@obj, binary)
130
191
  redirect_to CmsRouting.match_protocol(url, request)
131
192
  else
132
193
  render text: 'Empty Blob', status: 404
@@ -9,6 +9,14 @@ protected
9
9
 
10
10
  attr_internal :scrivito_runtime
11
11
 
12
+ def process_action(action, *args)
13
+ # We also need to reset the runtime before each action
14
+ # because of queries in middleware or in cases we are streaming
15
+ # and it won't be cleaned up by the method below.
16
+ reset_scrivito_runtime
17
+ super
18
+ end
19
+
12
20
  def cleanup_view_runtime
13
21
  self.scrivito_runtime = reset_scrivito_runtime
14
22
 
@@ -12,6 +12,14 @@ module Scrivito
12
12
  end
13
13
  end
14
14
 
15
+ def self.parse_iso8601(iso8601_date_time)
16
+ return unless iso8601_date_time
17
+
18
+ DateTime.iso8601(iso8601_date_time).in_time_zone
19
+ rescue ArgumentError
20
+ raise "The value is not a valid ISO 8601 date time: #{iso8601_date_time.inspect}"
21
+ end
22
+
15
23
  def self.serialize(attribute_value)
16
24
  attribute_value = case attribute_value
17
25
  when Date then attribute_value.to_time
@@ -17,7 +17,7 @@ end
17
17
  class InternalError < ScrivitoError
18
18
  end
19
19
 
20
- # @api beta
20
+ # @api public
21
21
  class TransformationError < ScrivitoError
22
22
  attr_reader :code
23
23
 
@@ -27,12 +27,15 @@ class TransformationError < ScrivitoError
27
27
  end
28
28
  end
29
29
 
30
- # @api beta
30
+ # @api public
31
31
  class TransformationSourceError < TransformationError
32
32
  end
33
33
 
34
- # @api beta
34
+ # @api public
35
35
  class TransformationDefinitionError < TransformationError
36
36
  end
37
37
 
38
+ class TransferModificationsError < ScrivitoError
39
+ end
40
+
38
41
  end # module Scrivito
@@ -0,0 +1,13 @@
1
+ module Scrivito
2
+ module GeneratorHelper
3
+ class << self
4
+ def attribute_definition(arg)
5
+ if %w(enum multienum).include?(arg.type.to_s)
6
+ "attribute :#{arg.name}, :#{arg.type}, values: []"
7
+ else
8
+ "attribute :#{arg.name}, :#{arg.type}"
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,51 +1,45 @@
1
1
  module Scrivito
2
2
 
3
3
  class ImageTag < Struct.new(:view_context)
4
- def options(obj, attribute_name, tag_options, editing_options)
4
+ def options(obj_or_widget, attribute_name, tag_options, editing_options)
5
5
  tag_options.reverse_merge(
6
- src: src(obj, attribute_name, editing_options),
7
- alt: alt(obj, attribute_name)
6
+ src: src(obj_or_widget, attribute_name, editing_options),
7
+ alt: alt(obj_or_widget, attribute_name)
8
8
  )
9
9
  end
10
10
 
11
11
  private
12
12
 
13
- def src(obj, attribute_name, editing_options)
14
- path(obj, attribute_name, editing_options) ||
13
+ def src(obj_or_widget, attribute_name, editing_options)
14
+ path(obj_or_widget, attribute_name, editing_options) ||
15
15
  editing_options[:placeholder] ||
16
16
  view_context.image_path('scrivito/image_placeholder.png')
17
17
  end
18
18
 
19
- def alt(obj, attribute_name)
20
- case target = obj[attribute_name]
21
- when Binary then obj.try(:alt_description)
19
+ def alt(obj_or_widget, attribute_name)
20
+ case target = obj_or_widget[attribute_name]
21
+ when Binary then obj_or_widget.try(:alt_description)
22
22
  when Enumerable then target.first.try(:alt_description)
23
23
  else target.try(:alt_description)
24
24
  end
25
25
  end
26
26
 
27
- def path(obj, attribute_name, editing_options)
28
- if target = target(obj, attribute_name, editing_options)
29
- image_options = editing_options.slice(:transform)
30
- cms_routing = CmsRouting.new(request, main_app, scrivito_engine, image_options)
31
- cms_routing.path_or_url(target, :path)
32
- end
33
- end
34
-
35
- def target(obj, attribute_name, editing_options)
36
- target = obj[attribute_name]
37
- target = link_target(target) if target.is_a?(Link)
38
- target = linklist_target(target) if target.is_a?(Enumerable)
39
- target
40
- end
41
-
42
- def link_target(link)
43
- link.internal? ? link.obj : link
44
- end
45
-
46
- def linklist_target(linklist)
47
- if linklist.any?
48
- link_target(linklist.first)
27
+ def path(obj_or_widget, attribute_name, editing_options)
28
+ attribute_value = obj_or_widget[attribute_name]
29
+ return if attribute_value.blank?
30
+
31
+ image_options = editing_options.slice(:transform)
32
+
33
+ if attribute_value.is_a?(Binary)
34
+ if obj_or_widget.is_a?(Widget)
35
+ raise ScrivitoError, 'scrivito_image_tag does not accept widgets with binary attributes'
36
+ end
37
+ BinaryRouting.new(request, scrivito_engine)
38
+ .binary_obj_url(obj_or_widget, attribute_value, image_options)
39
+ else
40
+ path = CmsRouting.new(request, main_app, scrivito_engine, image_options)
41
+ .path_or_url(attribute_value, :path)
42
+ path unless path == CmsRouting::LINK_TO_EMPTY_BLOB
49
43
  end
50
44
  end
51
45
 
@@ -1,14 +1,14 @@
1
1
  module Scrivito
2
2
 
3
3
  class LayoutTags < Struct.new(:view)
4
- def client_config(obj, resource)
4
+ def page_config(obj)
5
5
  if view.scrivito_user
6
- editing_context = EditingContextMiddleware.from_request(view.request)
7
- config = ClientConfig.new(obj, editing_context, view.lookup_context,
8
- resource: resource,
9
- return_to: view.params[:return_to]
6
+ page_config = PageConfig.new(
7
+ obj: obj,
8
+ editing_context: editing_context,
9
+ lookup_context: view.lookup_context,
10
10
  ).to_json
11
- view.content_tag(:div, '', 'data-scrivito-private-config' => config)
11
+ view.content_tag(:div, '', 'data-scrivito-private-page-config' => page_config)
12
12
  end
13
13
  end
14
14
 
@@ -21,6 +21,12 @@ class LayoutTags < Struct.new(:view)
21
21
  content << "; Version #{GemInfo.version}" if view.scrivito_user
22
22
  view.tag(:meta, name: 'generator', content: content)
23
23
  end
24
+
25
+ private
26
+
27
+ def editing_context
28
+ EditingContextMiddleware.from_request(view.request)
29
+ end
24
30
  end
25
31
 
26
32
  end
data/lib/scrivito/link.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  require 'active_model/naming'
2
2
 
3
3
  module Scrivito
4
- # This class provides an interfaces for handling CMS Links.
5
- # To format a link for rendering in an html page, use the +scrivito_path+ or +scrivito_url+ methods.
4
+ # This class provides an interface for handling CMS links.
5
+ # To format a link for rendering on an HTML page, use the +scrivito_path+ or +scrivito_url+ methods.
6
6
  # @api public
7
7
  class Link
8
8
 
9
9
  extend ActiveModel::Naming
10
10
 
11
- # Parses a url and returns a {Link} object. Determines internal urls based on the given
11
+ # Parses a URL and returns a {Link} object. Determines internal URLs based on the given
12
12
  # +hostname+ and +port+.
13
13
  # @api public
14
14
  # @param [String] url
@@ -18,7 +18,9 @@ module Scrivito
18
18
  LinkParser.new(host, port).parse(url)
19
19
  end
20
20
 
21
- # Create a new link obj
21
+ # Create a new link object.
22
+ # @example Set the 'current_campaign' link of an object:
23
+ # obj.update(current_campaign: Scrivito::Link.new(obj: Obj.find('55dd1ce50ecc41c8')))
22
24
  # @api public
23
25
  # @param [Hash] link_data
24
26
  # @option link_data [String] url
@@ -31,57 +33,56 @@ module Scrivito
31
33
  @link_data = link_data.with_indifferent_access
32
34
  end
33
35
 
34
- # The link's external url. Only available for external links.
35
- # Warning: Do not output the url directly unless you know what you are doing.
36
- # Normally you want to use the +scrivito_path+ or +scrivito_url+ methods to format a link.
36
+ # The external URL of the link. Only available for external links.
37
+ # Warning: Do not output the URL directly unless you know what you are doing.
38
+ # Normally, you want to use the +scrivito_path+ or +scrivito_url+ methods to format a link.
37
39
  # @api public
38
40
  def url
39
41
  @link_data[:url]
40
42
  end
41
43
 
42
- # Set the link's external url. This will lead to an external link.
44
+ # Set the external URL of the link. This causes the link to become an external link.
43
45
  # @api public
44
46
  # @param value [String] the url of the link
45
47
  def url=(value)
46
48
  @link_data[:url] = value
47
49
  end
48
50
 
49
- # Returns the {BasicObj Obj} this link is referencing. May be nil if the
51
+ # Returns the {BasicObj Obj} this link references. May be +nil+ if the
50
52
  # link is external.
51
53
  # @api public
52
54
  def obj
53
55
  @link_data[:obj]
54
56
  end
55
57
 
56
- # Set the {BasicObj Obj} this link is referencing. May be nil if the
57
- # link is external.
58
+ # Set the {BasicObj Obj} this link should reference.
58
59
  # @api public
59
- # @param value [Obj] the obj this link should be referencing
60
+ # @param value [Obj] the obj this link should point to
60
61
  def obj=(value)
61
62
  @link_data[:obj] = value
62
63
  end
63
64
 
64
- # The link's title.
65
+ # Returns the title of the link.
65
66
  # @api public
66
67
  def title
67
68
  @link_data[:title]
68
69
  end
69
70
 
70
- # Set the link's title.
71
+ # Set the link title.
71
72
  # @api public
72
- # @param value [String] the link's title
73
+ # @param value [String] the title of the link
73
74
  def title=(value)
74
75
  @link_data[:title] = value
75
76
  end
76
77
 
77
- # Returns the link's query string as in "index.html?query_string".
78
+ # Returns the query string of the link as in "index.html?query_string".
78
79
  # See RFC3986 for details (http://www.ietf.org/rfc/rfc3986.txt).
79
80
  # @api public
80
81
  def query
81
82
  @link_data[:query]
82
83
  end
83
84
 
84
- # Set the link's query string as in "index.html?query_string".
85
+ # Set the query string of the link as in "index.html?query_string".
85
86
  # See RFC3986 for details (http://www.ietf.org/rfc/rfc3986.txt).
86
87
  # @api public
87
88
  # @param value [String] the query string of the link
@@ -89,31 +90,39 @@ module Scrivito
89
90
  @link_data[:query] = value
90
91
  end
91
92
 
92
- # Returns the link's anchor as in "index.html#anchor".
93
+ # Returns the anchor of the link as in "index.html#anchor".
93
94
  # See RFC3986 for details (http://www.ietf.org/rfc/rfc3986.txt).
94
95
  # @api public
95
96
  def fragment
96
97
  @link_data[:fragment]
97
98
  end
98
99
 
99
- # Set the link's anchor as in "index.html#anchor".
100
+ # Set the anchor of the link as in "index.html#anchor".
100
101
  # See RFC3986 for details (http://www.ietf.org/rfc/rfc3986.txt).
101
102
  # @api public
102
- # @param value [String] the anchor or fragement of the link
103
+ # @param value [String] the anchor or fragment of the link
103
104
  def fragment=(value)
104
105
  @link_data[:fragment] = value
105
106
  end
106
107
 
108
+ # Returns the link target. "target" refers to the equally-named HTML attribute,
109
+ # not the link destination.
110
+ # @api public
107
111
  def target
108
112
  @link_data[:target]
109
113
  end
110
114
 
115
+ # Set the link target. "target" refers to the equally-named HTML attribute,
116
+ # not the link destination.
117
+ # @api public
118
+ # @param value [String] the target of the link
111
119
  def target=(value)
112
120
  @link_data[:target] = value
113
121
  end
114
122
 
115
- # Returns the file extension (e.g. zip, pdf) of this link's (internal or external) target.
116
- # Returns an empty string if the file extension is can not be determined.
123
+ # Returns the file extension (e.g. zip, pdf) of the (internal or external) destination
124
+ # of this link.
125
+ # Returns an empty string if the file extension cannot be determined.
117
126
  # @api public
118
127
  def file_extension
119
128
  if internal?
@@ -124,9 +133,8 @@ module Scrivito
124
133
  end
125
134
  end
126
135
 
127
- # Returns the title of this Link if it is set.
128
- # Otherwise it returns the display_title of the destination object for internal Links
129
- # or the URL for external Links.
136
+ # Returns the title of this Link if it is set. If not, the +display_title+ of the destination
137
+ # object is returned for internal links, or the +url+ for external links.
130
138
  # @api public
131
139
  def display_title
132
140
  dt = title
@@ -135,10 +143,10 @@ module Scrivito
135
143
  dt
136
144
  end
137
145
 
138
- # The alt description of a +Link+ used for {ScrivitoHelper#scrivito_image_tag}.
146
+ # The alt description of a +Link+, used by {ScrivitoHelper#scrivito_image_tag}.
139
147
  #
140
- # By default this method returns the +title+ of this +Link+.
141
- # If +title+ is nil and this +Link+ references an {BasicObj Obj}
148
+ # By default, this method returns the +title+ of this +Link+.
149
+ # If +title+ is +nil+ and this +Link+ references an {BasicObj Obj}, the
142
150
  # +alt_description+ of that {BasicObj Obj} is used.
143
151
  #
144
152
  # @api public
@@ -147,13 +155,13 @@ module Scrivito
147
155
  obj.alt_description if internal?
148
156
  end
149
157
 
150
- # Returns true this Link links to a CMS Object.
158
+ # Returns true if this Link points to a CMS object.
151
159
  # @api public
152
160
  def internal?
153
161
  url.nil?
154
162
  end
155
163
 
156
- # Returns true if this Link links to an external URL.
164
+ # Returns true if this Link points to an external location.
157
165
  # @api public
158
166
  def external?
159
167
  !internal?
@@ -190,12 +198,20 @@ module Scrivito
190
198
  params[:title] = title if title
191
199
  params[:query] = query if query
192
200
  params[:fragment] = fragment if fragment
201
+ params[:target] = target if target
193
202
  else
194
203
  params[:url] = url
195
204
  params[:title] = title if title
205
+ params[:target] = target if target
196
206
  end
197
207
 
198
208
  params
199
209
  end
210
+
211
+ def ==(other)
212
+ return false unless other.is_a?(Link)
213
+
214
+ to_cms_api_linklist_params == other.to_cms_api_linklist_params
215
+ end
200
216
  end
201
217
  end
@@ -21,6 +21,21 @@ module Scrivito
21
21
  debug " #{name} #{event.payload[:index]} #{event.payload[:keys].inspect}"
22
22
  end
23
23
 
24
+ def backend_request(event)
25
+ self.class.runtime += event.duration
26
+ return unless logger.debug?
27
+
28
+ duration = '(%.1fms)' % [event.duration]
29
+
30
+ param = event.payload[:params]
31
+ param_text = " #{param}" if param
32
+
33
+ verb_text = event.payload[:verb].upcase
34
+ path_text = event.payload[:path]
35
+
36
+ debug " Scrivito #{verb_text} #{path_text}#{param_text} #{duration}"
37
+ end
38
+
24
39
  def logger
25
40
  Rails.logger
26
41
  end
@@ -1,7 +1,7 @@
1
1
  module Scrivito
2
2
 
3
3
  # @api public
4
- # Represents a Membership of a {User} in a {Workspace}
4
+ # Represents a Membership of a {User} in a {Workspace}.
5
5
  class Membership
6
6
 
7
7
  # @api public
@@ -15,7 +15,7 @@ module Scrivito
15
15
  #
16
16
  # @note Currently the only available role is "owner".
17
17
  #
18
- # @return [String] the name of role
18
+ # @return [String] the name of the role
19
19
  attr_reader :role
20
20
 
21
21
  def initialize(user_id, data)
@@ -28,7 +28,7 @@ module Scrivito
28
28
  # @api public
29
29
  # @return The value returned by the proc set in {Configuration.find_user}.
30
30
  # @return An unknown user if no proc is set in {Configuration.find_user} or the proc returns a
31
- # falsy value. The unknown user will have the id of the original user and no abilities.
31
+ # falsy value. The unknown user will have the id of the original user and no capabilities.
32
32
  # @see Scrivito::Configuration.find_user
33
33
  def user
34
34
  User.find(user_id) || User.unknown_user(user_id)
@@ -1,7 +1,7 @@
1
1
  module Scrivito
2
2
  # @api public
3
3
  # The MembershipCollection includes all members of a given {Workspace}.
4
- # You can access it using {Workspace#memberships} method.
4
+ # You can access it using the {Workspace#memberships} method.
5
5
  class MembershipCollection
6
6
  extend Forwardable
7
7
  include Enumerable
@@ -11,28 +11,28 @@ module Scrivito
11
11
  # @api public
12
12
  # @!method each
13
13
  # Iterate over all {Membership Memberships} of a specific {Workspace}. Allows
14
- # you to use all methods defined by ruby's Enumerable module.
14
+ # you to use all the methods defined by Ruby's Enumerable module.
15
15
  #
16
16
  # @yield [Membership]
17
17
  #
18
- # @return [Enumerator] if no block is given an Enumerator is returned
18
+ # @return [Enumerator] if no block is given, an Enumerator is returned
19
19
  #
20
20
  # @example
21
- # # Obtain all owners of a workspace
21
+ # # Obtain all owners of a workspace.
22
22
  # my_workspace.memberships.select do |membership|
23
23
  # membership.role == "owner"
24
24
  # end
25
25
  #
26
- # # Get an array of all the members' user_ids
26
+ # # Get an array of all the members' user_ids.
27
27
  # my_workspace.memberships.map { |membership| membership.user_id }
28
28
  #
29
- # # Or use it directly to iterate over all items
29
+ # # Or use it directly to iterate over all items.
30
30
  # my_workspace.memberships.each do |membership|
31
31
  # puts "User #{membership.user_id} has the role #{membership.role}"
32
32
  # end
33
33
  #
34
- # @note For a complete list of all provided methods please view the
35
- # documentation of the Enumerable module
34
+ # @note For a complete list of the provided methods, please refer to the
35
+ # documentation of the Enumerable module.
36
36
  def_delegator :memberships, :each
37
37
 
38
38
  def initialize(workspace)
@@ -40,7 +40,7 @@ module Scrivito
40
40
  end
41
41
 
42
42
  # @api public
43
- # return a hash where the keys are user_ids and the values are Membership-Instances
43
+ # Returns a hash where the keys are +user_id+s and the values are Membership instances.
44
44
  # @return [Hash<String, Membership>]
45
45
  def to_h
46
46
  memberships.inject(HashWithIndifferentAccess.new) do |hash, membership|
@@ -50,7 +50,7 @@ module Scrivito
50
50
  end
51
51
 
52
52
  # @api public
53
- # Returns the membership for a user or nil
53
+ # Returns the membership of a user or nil.
54
54
  #
55
55
  # @param [User, String] id_or_user
56
56
  # @return [Membership, nil]
@@ -0,0 +1,22 @@
1
+ module Scrivito
2
+ #
3
+ # This class represents a collection of meta data attributes.
4
+ #
5
+ # @api beta
6
+ #
7
+ class MetaDataCollection
8
+ def initialize(attributes)
9
+ @attributes = ActiveSupport::HashWithIndifferentAccess.new(attributes)
10
+ end
11
+
12
+ #
13
+ # Find value of a meta data attribute.
14
+ #
15
+ # @api beta
16
+ # @param name [Symbol, String] the name of the meta data attribute.
17
+ # @return [String, Array, Fixnum, Date, nil] meta data attribute value if found or +nil+ otherwise.
18
+ def [](name)
19
+ @attributes[name]
20
+ end
21
+ end
22
+ end
@@ -77,11 +77,17 @@ class ModelLibrary
77
77
  end
78
78
 
79
79
  def load_models_from_path(type, path, base_class)
80
- Dir["#{path}/**/*_#{type}.rb"].map do |file_path|
80
+ candidates_from_path(type, path).map do |file_path|
81
81
  load_model(file_path.gsub(path, '').gsub('.rb', '').classify, base_class)
82
82
  end
83
83
  end
84
84
 
85
+ def candidates_from_path(type, path)
86
+ result = Dir["#{path}/**/*_#{type}.rb"]
87
+ result += Dir["#{path}/**/page.rb"] if type == 'page'
88
+ result
89
+ end
90
+
85
91
  def load_model(class_name, base_class)
86
92
  model_class = class_name.constantize
87
93
  model_class if model_class.ancestors.include?(base_class)