# encoding: utf-8
module ConstructorPages
# Page model. Pages are core for company websites, blogs etc.
class Page < ActiveRecord::Base
# Adding has_many for all field types
Field::TYPES.each do |t|
class_eval %{
has_many :#{t}_types, dependent: :destroy, class_name: 'Types::#{t.titleize}Type'
}
end
has_many :fields, through: :template
belongs_to :template
default_scope -> { order :lft }
validate :template_check
before_save :friendly_url, :template_assign, :full_url_update
after_update :descendants_update
after_create :create_fields_values
acts_as_nested_set
# Used for find page by request. It return first page if no request given
# @param request for example '/conditioners/split-systems/zanussi'
def self.find_by_request_or_first(request = nil)
request.nil? ? Page.first : Page.where(full_url: request).first
end
# Generate full_url from parent id and url
# @param parent_id integer
# @param url should looks like 'hello-world-page' without leading slash
def self.full_url_generate(parent_id, url = '')
'/' + Page.find(parent_id).self_and_ancestors.map {|c| c.url}.append(url).join('/')
end
# Check code_name for field and template.
# When missing method Page find field or page in branch with plural and singular code_name so
# field and template code_name should be uniqueness for page methods
def self.check_code_name(code_name)
[code_name, code_name.pluralize, code_name.singularize].each do |name|
# TODO: replace Page.first
if Page.first.respond_to?(name)
return false
end
end
true
end
# Get field by code_name
def field(code_name)
Field.where(code_name: code_name, template_id: template_id).first
end
# Get value of field by code_name
def get_field_value(code_name)
field = field(code_name)
field.get_value_for(self) if field
end
# Set value of field by code_name and value
def set_field_value(code_name, value)
field = field(code_name)
field.set_value_for(self, value) if field
end
# Update all fields values with given params.
# @param params should looks like {price: 500, content: 'Hello'}
# @param reset_booleans reset all boolean fields to false before assign params
def update_fields_values(params, reset_booleans = true)
fields.each do |field|
value = params[field.code_name.to_sym]
_type_object = field.find_type_object(self)
if _type_object
_type_object.value = 0 if field.type_value == 'boolean' and reset_booleans
if value
_type_object.value = value
end
_type_object.save
end
end
end
# Create fields values
def create_fields_values; fields.each {|field| field.create_type_object(self) } end
# Remove all fields values
def remove_fields_values
fields.each {|f| f.remove_type_object self}
end
# Search page by template code_name in same branch of pages and templates.
# It allows to call page.category.brand.series.model etc.
#
# Return one page if founded in ancestors,
# and return array of pages if founded in descendants
#
# It determines if code_name is singular or nor
# @param code_name template code name
def find_page_in_branch(code_name)
_template = Template.where(code_name: code_name.singularize).first
if _template
result = []
result = descendants.where(template_id: _template.id) if code_name == code_name.pluralize
result = ancestors.where(template_id: _template.id).first if result.empty?
result
end
end
alias_method :find_pages_in_branch, :find_page_in_branch
def published?; active end
alias_method :active?, :published?
# Returns page hash attributes with fields.
#
# Default attributes are name and title. Options param allows to add more.
# @param options default merge name and title page attributes
def as_json(options = {})
options = {name: self.name, title: self.title}.merge options
fields.each do |field|
options.merge!({field.code_name.to_sym => field.get_value_for(self)})
end
options
end
# Check if link specified
def redirect?; url != link && !link.empty? end
# When method missing it get/set field value or get page in branch
#
# Examples:
# page.content = 'Hello world'
# puts page.price
# page.brand.models.each do...
def method_missing(name, *args, &block)
if new_record?
super
else
name = name.to_s
name[-1] == '=' ? set_field_value(name[0..-2], args[0]) : get_field_value(name) || find_pages_in_branch(name)
end
end
private
# if url has been changed by manually or url is empty
def friendly_url
self.url = ((auto_url || url.empty?) ? translit(name) : url).parameterize
end
# TODO: add more languages
# translit to english
def translit(str)
Russian.translit(str)
end
# Page is not valid if there is no template
def template_check
errors.add_on_empty(:template_id) if Template.count == 0
end
# If template_id is nil then get first template
def template_assign
self.template_id = Template.first.id unless template_id
end
# Update full_url
def full_url_update
self.full_url = (parent_id ? Page.full_url_generate(parent_id, url) : '/' + url)
end
# Reload all descendants
def descendants_update; descendants.map(&:save) end
end
end