lib/scrivito/attribute_content.rb in scrivito_sdk-0.60.0 vs lib/scrivito/attribute_content.rb in scrivito_sdk-0.65.0.rc1
- old
+ new
@@ -27,10 +27,15 @@
%w[string html],
%w[string text],
%w[widgetlist widget],
]
+ #
+ # @api public
+ #
+ # Default attribute values.
+ #
DEFAULT_ATTRIBUTE_VALUES = {
'binary' => nil,
'date' => nil,
'enum' => nil,
'html' => '',
@@ -38,11 +43,10 @@
'linklist' => [],
'multienum' => [],
'reference' => nil,
'referencelist' => [],
'string' => '',
- 'text' => '',
'widgetlist' => [],
}
delegate :attribute_definitions, to: :class
@@ -109,20 +113,24 @@
end
def valid_widget_ruby_classes_for(field)
computed_classes = valid_widget_classes_for(field)
- return Scrivito.models.widgets.to_a unless computed_classes
+ ruby_classes =
+ if computed_classes
+ type_computer = Widget.type_computer
+ self.class.convert_to_obj_classes(computed_classes, type_computer) do |name|
+ Rails.logger.warn("Invalid widget class #{name} returned by #valid_widget_classes_for.")
+ end
+ else
+ Scrivito.models.widgets.to_a
+ end
- type_computer = Widget.type_computer
- ruby_classes = self.class.convert_to_obj_classes(computed_classes, type_computer) do |name|
- Rails.logger.warn("Invalid widget class #{name} returned by #valid_widget_classes_for.")
- end
-
ruby_classes.select { |ruby_class| ruby_class.valid_inside_container?(self.class) }
end
+
def modification_for_attribute(attribute_name, revision=Workspace.current.base_revision)
return Modification::UNMODIFIED unless revision
if new?(revision)
Modification::NEW
@@ -288,15 +296,11 @@
end
end
def extract_obj_class_from_attributes(attributes)
if special_class? && (obj_class_name = attributes[:_obj_class] || attributes['_obj_class'])
- if obj_class = type_computer.compute_type_without_fallback(obj_class_name)
- obj_class
- else
- raise ObjClassNotFound
- end
+ type_computer.compute_type_without_fallback(obj_class_name)
end
end
#
# Defines an attribute.
@@ -317,19 +321,24 @@
# +html+, +enum+, +multienum+, +widgetlist+, +reference+, +referencelist+ and +binary+.
# @param [Hash] options definition options.
#
# @option options [Symbol, String] :values allowed values for types +enum+ and +multienum+.
# If no values are given for that types, then an empty array will be assumed.
+ # @option options [Symbol, String] :default custom default value.
+ # See {Scrivito::AttributeContent::DEFAULT_ATTRIBUTE_VALUES} for factory defaults.
+ # See {Scrivito::AttributeContent::ClassMethods#default_for} for more advanced default settings.
#
# @return nil
# @raise [Scrivito::ScrivitoError] if the +type+ is unknown
+ # @see Scrivito::AttributeContent::DEFAULT_ATTRIBUTE_VALUES
+ # @see Scrivito::AttributeContent::ClassMethods#default_for
#
# @example Defining attributes
# class Page < ::Obj
# attribute :my_string, :string
# attribute 'my_html', 'my_html'
- # attribute :my_enum, :enum, values: %w[a b c]
+ # attribute :my_enum, :enum, values: %w[a b c], default: 'a'
# attribute :my_multienum, :multienum
# end
#
# Page.attribute_definitions
# #=> #<Scrivito::AttributeDefinitionCollection>
@@ -381,15 +390,119 @@
#
def attribute(name, type, options = {})
name, type, options = name, type, options
assert_valid_attribute_name(name.to_s)
assert_valid_attribute_type(type.to_s)
+ default = options.delete(:default) || options.delete('default')
own_attribute_definitions[name.to_s] = AttributeDefinition.new(name, type, options)
+ default_for(name) { default } if default
nil
end
#
+ # Sets a default for an attribute.
+ #
+ # @api public
+ #
+ # If {Scrivito::BasicObj.create Obj.create} or {Scrivito::BasicWidget.new Widget.new} are called
+ # with no value provided for attribute +attribute_name+, then the +block+ will be called and its
+ # return value will be set as the attribute's value.
+ #
+ # The +block+ will be called with two parameters.
+ #
+ # The first parameter is an +ActiveSupport::HashWithIndifferentAccess+ containing attributes,
+ # which were provided to {Scrivito::BasicObj.create Obj.create} or
+ # {Scrivito::BasicWidget.new Widget.new}.
+ #
+ # The second parameter is a +Hash+ containing the context, which were provided to
+ # {Scrivito::BasicObj.create Obj.create} or {Scrivito::BasicWidget.new Widget.new}. If there is
+ # a current visitor of type {Scrivito::User}, then it can be accessed under the key
+ # +:scrivito_user+ in the provided context.
+ #
+ # @param [Symbol, String] attribute_name the name of the attribute.
+ # @param [Proc] block that returns the default value.
+ # @raise [Scrivito::ScrivitoError] if no block given
+ # @return nil
+ #
+ # @see Scrivito::BasicObj.create
+ # @see Scrivito::BasicWidget.new
+ # @see Scrivito::Configuration.editing_auth
+ #
+ # @example A simple default
+ # class MyPage < Obj
+ # attribute :title, :string
+ # default_for(:title) { 'Spam' }
+ # end
+ #
+ # my_page = MyPage.create
+ # my_page.title # => 'Spam'
+ #
+ # @example A default depending on the given attributes
+ # class MyPage < Obj
+ # attribute :title, :string
+ #
+ # default_for :title do |attributes|
+ # if (path = attributes[:_path]) && path.starts_with('/de')
+ # 'Hier den Titel eingeben'
+ # else
+ # 'Your title here'
+ # end
+ # end
+ # end
+ #
+ # my_page = MyPage.create(_path: '/en/test')
+ # my_page.title # => 'Your title here'
+ #
+ # my_page = MyPage.create(_path: '/de/test')
+ # my_page.title # => 'Hier den Titel eingeben'
+ #
+ # @example A more complex default depending on the given attributes and the current user
+ # class MyPage < Obj
+ # attribute :title, :string
+ #
+ # default_for :title do |attributes, context|
+ # if use_german_title?(context[:scrivito_user], attributes[:_path])
+ # 'Hier den Titel eingeben'
+ # else
+ # 'Your title here'
+ # end
+ # end
+ #
+ # private
+ #
+ # #
+ # # Assuming there is a model +MyUser+ with a +preferences+ method returning the preferences
+ # # of the current user.
+ # # The +email+ of a +MyUser+ is the +id+ of the corresponding +Scrivito::User+ as set in
+ # # +Scrivito::Configuration.editing_auth+.
+ # #
+ # def use_german_title?(scrivito_user, path)
+ # scrivito_user && MyUser.find_by(email: scrivito_user.id).preferences[:locale] == 'de' ||
+ # path && path.starts_with?('/de')
+ # end
+ # end
+ #
+ # alice = Scrivito::User.define('alice@scrivito.com')
+ # alice.preferences[:locale] # => 'en'
+ #
+ # my_page = MyPage.create({_path: '/de/test'}, alice)
+ # my_page.title # => 'Your title here'
+ #
+ # bob = Scrivito::User.define('bob@scrivito.com')
+ # bob.preferences[:locale] # => 'de'
+ #
+ # my_page = MyPage.create({_path: '/en/test'}, bob)
+ # my_page.title # => 'Hier den Titel eingeben'
+ #
+ def default_for(attribute_name, &block)
+ attribute_name = attribute_name.to_s
+ raise ScrivitoError, 'No block given' unless block_given?
+ attribute_defaults[attribute_name] = block
+ nil
+ end
+
+ #
# This method determines the description that is shown in the UI
# and defaults to class name. It can be overriden by a custom value.
#
# @api public
#
@@ -406,10 +519,36 @@
#
def attribute_definitions
AttributeDefinitionCollection.new(all_attribute_definitions)
end
+ # For test purpose only.
+ def reset_attribute_defaults!
+ @attribute_defaults = nil
+ end
+
+ def build_attributes_with_defaults(attributes = {}, context = {})
+ attributes_with_indifferent_access = attributes.with_indifferent_access
+ attributes_with_defaults = attributes_with_indifferent_access.dup
+
+ attribute_definitions.each do |attribute_definition|
+ attribute_name = attribute_definition.name
+ attribute_default = attribute_defaults[attribute_name]
+ if !attributes_with_indifferent_access.has_key?(attribute_name) && attribute_default
+ attributes_with_defaults[attribute_name] =
+ attribute_default.call(attributes_with_indifferent_access, context)
+ end
+ end
+
+ if superclass.respond_to?(:build_attributes_with_defaults)
+ attributes_with_defaults.merge!(
+ superclass.build_attributes_with_defaults(attributes_with_defaults, context))
+ end
+
+ attributes_with_defaults
+ end
+
# @api public
#
# This method disables the creation of +Objs+ or +Widgets+ of the given type using the UI.
# It does not prevent adding these objects programatically.
#
@@ -436,15 +575,14 @@
type_computer.special_class?(self)
end
def convert_to_obj_classes(class_names, type_computer)
class_names.inject([]) do |classes, class_name|
- widget_class = type_computer.compute_type_without_fallback(class_name.to_s)
-
- if widget_class
+ begin
+ widget_class = type_computer.compute_type_without_fallback(class_name.to_s)
classes << widget_class
- else
+ rescue ObjClassNotFound
yield class_name
end
classes
end
end
@@ -486,9 +624,13 @@
def assert_valid_obj_class(obj_class)
unless obj_class == to_s
raise ScrivitoError, "Cannot set _obj_class to #{obj_class.inspect} when creating #{self}"
end
+ end
+
+ def attribute_defaults
+ @attribute_defaults ||= {}
end
end
end
end