Class: Alchemy::Element
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Alchemy::Element
- Includes:
- Definitions, Presenters, Hints, Logger, Touching
- Defined in:
- app/models/alchemy/element.rb
Defined Under Namespace
Modules: Definitions, Presenters
Constant Summary collapse
- FORBIDDEN_DEFINITION_ATTRIBUTES =
%w(contents available_contents amount picture_gallery taggable hint)
- SKIPPED_ATTRIBUTES_ON_COPY =
%w(id position folded created_at updated_at creator_id updater_id cached_tag_list)
Instance Attribute Summary collapse
-
#create_contents_after_create ⇒ Object
Returns the value of attribute create_contents_after_create.
Class Method Summary collapse
- .all_from_clipboard(clipboard) ⇒ Object
-
.all_from_clipboard_for_page(clipboard, page) ⇒ Object
All elements in clipboard that could be placed on page.
-
.copy(source, differences = {}) ⇒ Object
This methods does a copy of source and all depending contents and all of their depending essences.
-
.create_from_scratch(attributes) ⇒ Object
Creates a new element as described in
/config/alchemy/elements.yml
. -
.new_from_scratch(attributes = {}) ⇒ Object
Builds a new element as described in
/config/alchemy/elements.yml
.
Instance Method Summary collapse
- #all_contents_by_name(name) ⇒ Object
- #all_contents_by_type(essence_type) ⇒ Object
-
#available_content_description_for(content_name) ⇒ Object
Returns the definition for given content_name inside the available_contents.
-
#available_contents ⇒ Object
returns the collection of available essence_types that can be created for this element depending on its description in elements.yml.
-
#available_page_cell_names(page) ⇒ Object
The names of all cells from given page this element could be placed in.
-
#cache_key ⇒ Object
Returns the key that’s taken for cache path.
- #content_by_name(name) ⇒ Object
- #content_by_type(essence_type) ⇒ Object
-
#content_description_for(content_name) ⇒ Object
Returns the definition for given content_name.
-
#content_descriptions ⇒ Object
Returns the array with the hashes for all element contents in the elements.yml file.
-
#content_for_rss_description ⇒ Object
Returns the content that is marked as rss description.
-
#content_for_rss_title ⇒ Object
Returns the content that is marked as rss title.
- #contents_with_errors ⇒ Object
-
#essence_error_messages ⇒ Object
Essence validation errors.
-
#essence_errors ⇒ Object
Returns all essence_errors in the format:.
- #essences ⇒ Object
- #has_ingredient?(name) ⇒ Boolean
- #has_validations? ⇒ Boolean
-
#ingredient(name) ⇒ Object
Returns the contents ingredient for passed content name.
-
#next(name = nil) ⇒ Object
Returns next public element from same page.
-
#prev(name = nil) ⇒ Object
Returns previous public element from same page.
-
#richtext_contents_ids ⇒ Object
Returns an array of all EssenceRichtext contents ids.
- #rtf_contents ⇒ Object (also: #richtext_contents)
-
#store_page(page) ⇒ Object
Stores the page into
touchable_pages
(Pages that have to be touched after updating the element). -
#taggable? ⇒ Boolean
Returns true if the definition of this element has a taggable true value.
-
#to_partial_path ⇒ Object
The element’s view partial is dependent from its name.
-
#trash! ⇒ Object
Trashing an element means nullifying its position, folding and unpublishing it.
- #trashed? ⇒ Boolean
-
#update_contents(contents_attributes) ⇒ Boolean
Updates all related contents by calling
update_essence
on each of them.
Methods included from Presenters
#display_name, #display_name_with_preview_text, #dom_id, #preview_text
Methods included from Definitions
Methods included from Hints
Methods included from Touching
Methods included from Logger
Instance Attribute Details
#create_contents_after_create ⇒ Object
Returns the value of attribute create_contents_after_create.
45 46 47 |
# File 'app/models/alchemy/element.rb', line 45 def create_contents_after_create @create_contents_after_create end |
Class Method Details
.all_from_clipboard(clipboard) ⇒ Object
124 125 126 127 |
# File 'app/models/alchemy/element.rb', line 124 def all_from_clipboard(clipboard) return [] if clipboard.nil? where(id: clipboard.collect { |e| e['id'] }) end |
.all_from_clipboard_for_page(clipboard, page) ⇒ Object
All elements in clipboard that could be placed on page
131 132 133 134 135 136 |
# File 'app/models/alchemy/element.rb', line 131 def all_from_clipboard_for_page(clipboard, page) return [] if clipboard.nil? || page.nil? all_from_clipboard(clipboard).select { |ce| page.available_element_names.include?(ce.name) } end |
.copy(source, differences = {}) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'app/models/alchemy/element.rb', line 111 def copy(source, differences = {}) source.attributes.stringify_keys! differences.stringify_keys! attributes = source.attributes.except(*SKIPPED_ATTRIBUTES_ON_COPY).merge(differences) element = self.create!(attributes.merge(:create_contents_after_create => false)) element.tag_list = source.tag_list source.contents.each do |content| new_content = Content.copy(content, :element_id => element.id) new_content.move_to_bottom end element end |
.create_from_scratch(attributes) ⇒ Object
Creates a new element as described in /config/alchemy/elements.yml
-
Returns a new Alchemy::Element object if no name is given in attributes, because the definition can not be found w/o name
-
Raises Alchemy::ElementDefinitionError if no definition for given attributes could be found
94 95 96 97 98 |
# File 'app/models/alchemy/element.rb', line 94 def create_from_scratch(attributes) element = new_from_scratch(attributes) element.save if element element end |
.new_from_scratch(attributes = {}) ⇒ Object
Builds a new element as described in /config/alchemy/elements.yml
-
Returns a new Alchemy::Element object if no name is given in attributes, because the definition can not be found w/o name
-
Raises Alchemy::ElementDefinitionError if no definition for given attributes could be found
78 79 80 81 82 83 84 85 |
# File 'app/models/alchemy/element.rb', line 78 def new_from_scratch(attributes = {}) attributes = attributes.dup.symbolize_keys return new if attributes[:name].blank? new_element_from_definition_by(attributes) || raise(ElementDefinitionError.new(attributes)) end |
Instance Method Details
#all_contents_by_name(name) ⇒ Object
198 199 200 |
# File 'app/models/alchemy/element.rb', line 198 def all_contents_by_name(name) self.contents.where(:name => name) end |
#all_contents_by_type(essence_type) ⇒ Object
202 203 204 |
# File 'app/models/alchemy/element.rb', line 202 def all_contents_by_type(essence_type) self.contents.where(:essence_type => Content.normalize_essence_type(essence_type)) end |
#available_content_description_for(content_name) ⇒ Object
Returns the definition for given content_name inside the available_contents
251 252 253 254 |
# File 'app/models/alchemy/element.rb', line 251 def available_content_description_for(content_name) return nil if available_contents.blank? available_contents.detect { |d| d['name'] == content_name } end |
#available_contents ⇒ Object
returns the collection of available essence_types that can be created for this element depending on its description in elements.yml
257 258 259 |
# File 'app/models/alchemy/element.rb', line 257 def available_contents definition['available_contents'] end |
#available_page_cell_names(page) ⇒ Object
The names of all cells from given page this element could be placed in.
410 411 412 413 414 415 416 417 |
# File 'app/models/alchemy/element.rb', line 410 def available_page_cell_names(page) cellnames = unique_available_page_cell_names(page) if cellnames.blank? || !page.has_cells? ['for_other_elements'] else cellnames end end |
#cache_key ⇒ Object
Returns the key that’s taken for cache path.
Uses the page’s published_at
value that’s updated when the user publishes the page.
If the page is the current preview it uses the element’s updated_at value as cache key.
448 449 450 451 452 453 454 |
# File 'app/models/alchemy/element.rb', line 448 def cache_key if Page.current_preview == self.page "alchemy/elements/#{id}-#{updated_at}" else "alchemy/elements/#{id}-#{page.published_at}" end end |
#content_by_name(name) ⇒ Object
190 191 192 |
# File 'app/models/alchemy/element.rb', line 190 def content_by_name(name) self.contents.find_by_name(name) end |
#content_by_type(essence_type) ⇒ Object
194 195 196 |
# File 'app/models/alchemy/element.rb', line 194 def content_by_type(essence_type) self.contents.find_by_essence_type(Content.normalize_essence_type(essence_type)) end |
#content_description_for(content_name) ⇒ Object
Returns the definition for given content_name
241 242 243 244 245 246 247 248 |
# File 'app/models/alchemy/element.rb', line 241 def content_description_for(content_name) if content_descriptions.blank? log_warning "Element #{self.name} is missing the content definition for #{content_name}" return nil else content_descriptions.detect { |d| d['name'] == content_name } end end |
#content_descriptions ⇒ Object
Returns the array with the hashes for all element contents in the elements.yml file
235 236 237 238 |
# File 'app/models/alchemy/element.rb', line 235 def content_descriptions return nil if definition.blank? definition['contents'] end |
#content_for_rss_description ⇒ Object
Returns the content that is marked as rss description.
Mark a content as rss description in your elements.yml
file:
- name: news
contents:
- name: body
type: EssenceRichtext
rss_description: true
230 231 232 |
# File 'app/models/alchemy/element.rb', line 230 def content_for_rss_description ('description') end |
#content_for_rss_title ⇒ Object
Returns the content that is marked as rss title.
Mark a content as rss title in your elements.yml
file:
- name: news
contents:
- name: headline
type: EssenceText
rss_title: true
216 217 218 |
# File 'app/models/alchemy/element.rb', line 216 def content_for_rss_title ('title') end |
#contents_with_errors ⇒ Object
389 390 391 |
# File 'app/models/alchemy/element.rb', line 389 def contents_with_errors contents.select(&:essence_validation_failed?) end |
#essence_error_messages ⇒ Object
Essence validation errors
Error messages are translated via I18n
Inside your translation file add translations like:
alchemy:
content_validations:
name_of_the_element:
name_of_the_content:
validation_error_type: Error Message
NOTE: validation_error_type
has to be one of:
* blank
* taken
* invalid
Example:
de:
alchemy:
content_validations:
contactform:
email:
invalid: 'Die Email hat nicht das richtige Format'
Error message translation fallbacks
In order to not translate every single content for every element you can provide default error messages per content name:
Example
en:
alchemy:
content_validations:
fields:
email:
invalid: E-Mail has wrong format
blank: E-Mail can't be blank
And even further you can provide general field agnostic error messages:
Example
en:
alchemy:
content_validations:
errors:
invalid: %{field} has wrong format
blank: %{field} can't be blank
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# File 'app/models/alchemy/element.rb', line 371 def = [] essence_errors.each do |content_name, errors| errors.each do |error| << I18n.t( "#{self.name}.#{content_name}.#{error}", scope: 'content_validations', default: [ "fields.#{content_name}.#{error}".to_sym, "errors.#{error}".to_sym ], field: Content.translated_label_for(content_name, name) ) end end end |
#essence_errors ⇒ Object
Returns all essence_errors in the format:
{
essence.content.name => [, ]
}
Get translated error messages with Element#essence_error_messages
308 309 310 311 312 313 314 315 316 |
# File 'app/models/alchemy/element.rb', line 308 def essence_errors essence_errors = {} essences.each do |essence| unless essence.errors.blank? essence_errors[essence.content.name] = essence.validation_errors end end essence_errors end |
#essences ⇒ Object
295 296 297 298 |
# File 'app/models/alchemy/element.rb', line 295 def essences return [] if contents.blank? contents.collect(&:essence) end |
#has_ingredient?(name) ⇒ Boolean
268 269 270 |
# File 'app/models/alchemy/element.rb', line 268 def has_ingredient?(name) self.ingredient(name).present? end |
#has_validations? ⇒ Boolean
393 394 395 |
# File 'app/models/alchemy/element.rb', line 393 def has_validations? !contents.detect(&:has_validations?).blank? end |
#ingredient(name) ⇒ Object
Returns the contents ingredient for passed content name.
262 263 264 265 266 |
# File 'app/models/alchemy/element.rb', line 262 def ingredient(name) content = content_by_name(name) return nil if content.blank? content.ingredient end |
#next(name = nil) ⇒ Object
Returns next public element from same page.
Pass an element name to get next of this kind.
158 159 160 |
# File 'app/models/alchemy/element.rb', line 158 def next(name = nil) previous_or_next('>', name) end |
#prev(name = nil) ⇒ Object
Returns previous public element from same page.
Pass an element name to get previous of this kind.
166 167 168 |
# File 'app/models/alchemy/element.rb', line 166 def prev(name = nil) previous_or_next('<', name) end |
#richtext_contents_ids ⇒ Object
Returns an array of all EssenceRichtext contents ids
404 405 406 |
# File 'app/models/alchemy/element.rb', line 404 def richtext_contents_ids contents.essence_richtexts.pluck("#{Content.table_name}.id") end |
#rtf_contents ⇒ Object Also known as: richtext_contents
397 398 399 |
# File 'app/models/alchemy/element.rb', line 397 def rtf_contents contents.essence_richtexts end |
#store_page(page) ⇒ Object
Stores the page into touchable_pages
(Pages that have to be touched after updating the element).
171 172 173 174 175 176 177 |
# File 'app/models/alchemy/element.rb', line 171 def store_page(page) return true if page.nil? unless self.touchable_pages.include? page self.touchable_pages << page self.save end end |
#taggable? ⇒ Boolean
Returns true if the definition of this element has a taggable true value.
420 421 422 |
# File 'app/models/alchemy/element.rb', line 420 def taggable? definition['taggable'] == true end |
#to_partial_path ⇒ Object
The element’s view partial is dependent from its name
Define elements
Elements are defined in the config/alchemy/elements.yml
file
- name: article
contents:
...
Override the view
Element partials live in app/views/alchemy/elements
438 439 440 |
# File 'app/models/alchemy/element.rb', line 438 def to_partial_path "alchemy/elements/#{name}_view" end |
#trash! ⇒ Object
Trashing an element means nullifying its position, folding and unpublishing it.
180 181 182 183 184 |
# File 'app/models/alchemy/element.rb', line 180 def trash! self.public = false self.folded = true self.remove_from_list end |
#trashed? ⇒ Boolean
186 187 188 |
# File 'app/models/alchemy/element.rb', line 186 def trashed? self.position.nil? end |
#update_contents(contents_attributes) ⇒ Boolean
Updates all related contents by calling update_essence
on each of them.
Example
@element.update_contents({1 => {ingredient: 'Title'}, 2 => {link: 'https://google.com'}})
286 287 288 289 290 291 292 293 |
# File 'app/models/alchemy/element.rb', line 286 def update_contents(contents_attributes) return true if contents_attributes.nil? contents.each do |content| content_hash = contents_attributes["#{content.id}"] || next content.update_essence(content_hash) || errors.add(:base, :essence_validation_failed) end errors.blank? end |