class BrickLayer::DataSet include Mongoid::Document include Mongoid::Timestamps field :title, type: String field :meta_description, type: String field :meta_keywords, type: String field :route_id, type: Integer belongs_to :route, :class_name => "BrickLayer::Route" # Expose set methods based on data set, this also gives you the ability to pull from fields that an object has set. # This allows the methods to return the results with the method as the key in a hash to json conversion. # # Example: # expose_data :method_1, :method_2 # # def method_1 # list_of_results = result_1, result_2, result_3 # end # def self.expose_data(*field_array) class << self attr_accessor :collect_exposed_data_methods end @collect_exposed_data_methods = @collect_exposed_data_methods.blank? ? field_array : (@collect_exposed_data_methods + field_array) define_method :exposed_data_methods do; self.class.collect_exposed_data_methods; end end # Override as_json to return proper exposed data methods def as_json(options={}) original_attributes = super(options) complete_attributes = {} # This takes a generic data sets and preps it for to json conversion unless self.route.blank? obj = self.route_custom_template_class.new(self.attributes) else obj = self end if obj.respond_to?("exposed_data_methods") obj.exposed_data_methods.each { |vfield| complete_attributes[vfield] = obj.send(vfield) } end complete_attributes.merge(original_attributes) end ### INTROSPECTION ### # Display all fields in arry for the class def self.all_fields(options={}) if options[:added_only] results = self.fields.keys - BrickLayer::DataSet.fields.keys else results = self.fields.keys end results += options[:add_fields].map(&:to_s) if options[:add_fields] options[:ignore_fields].each { |f| results.delete(f) } if options[:ignore_fields] return results end # Display all exposed data methods in an array for the class def self.exposed_data_methods begin return self.new.exposed_data_methods.map(&:to_s).uniq rescue NoMethodError [] end end # Display both fields and exposed data methods for the class def self.all_fields_and_exposed_data_methods (all_fields + exposed_data_methods).uniq end # Grab the custom class if this class has a route def route_custom_template_class "PageDataSets::#{self.route.data_set_template}".constantize unless self.route.try(:data_set_template).blank? end ############################ ### CUSTOM FIELD SUPPORT ### class << self; attr_accessor :custom_fields, :custom_field_options; end # This allows a user to create a custom field that will be recognized as a field type for other uses in the application # like the UI def self.custom_field(field_name, field_type, options={}) @custom_fields ||= {} @custom_field_options ||= {} if field_type == :image @custom_fields[field_name.to_sym] = :file field "#{field_name}_uid", type: String field "#{field_name}_name", type: String image_accessor field_name # grab the sizes and expose it if sizes are set if !options[:sizes].blank? options[:sizes].each do |size_name, value| expose_data "#{field_name}_#{size_name}".to_sym define_method "#{field_name}_#{size_name}".to_sym do self.send(field_name.to_s).try(:thumb, value).try(:url) end end else @custom_fields[field_name.to_sym] = field_type field field_name, type: String end elsif field_type == :file @custom_fields[field_name.to_sym] = field_type field field_name, type: String mount_uploader field_name.to_sym, BrickLayer::FileUploader, :mount_on => "#{field_name}_filename".to_sym else @custom_fields[field_name.to_sym] = field_type field field_name, type: String end @custom_field_options[field_name.to_sym] = options unless options.blank? end # convert an array of ids to an array of objects def translate_string_ids_to_objects(array_of_ids) unless array_of_ids.blank? return BrickLayer::DataSet.find(array_of_ids) if array_of_ids.is_a?(String) return array_of_ids.map do |x| begin BrickLayer::DataSet.find(x) rescue x end end end end # convert an id string to an object def translate_obj_to_string(string_id) begin BrickLayer::DataSet.find(string_id) rescue string_id end end # overriding #update_attributes to support the passing of object ids as an array of strings (works better for forms) def update_attributes(update_hash) update_hash.each do |k,v| if self.relations.keys.include?(k.to_s) self.send("#{k}=", translate_string_ids_to_objects(v)) if relations[k].macro == :referenced_in self.send("#{k}=", translate_obj_to_string(v)) if relations[k].macro == :references_one self.send("#{k}=", translate_string_ids_to_objects(v)) if relations[k].macro == :references_and_referenced_in_many || relations[k].macro == :references_many else self.send("#{k}=",v) end end return self.save end ############################ ### SEARCH FUNCTIONALITY ### # using mongoid_fulltext - go to https://github.com/aaw/mongoid_fulltext for more details include Mongoid::FullTextSearch def self.fulltext_search_in(*args) search_in_hash = args.find { |x| x.is_a?(Hash) } # Inject a type index filter (this allows easy support for STI full search scoping) if search_in_hash unless search_in_hash[:filters].blank? new_hash = search_in_hash[:filters].merge({ :type_index => lambda { |x| x._type } }) else new_hash = search_in_hash.merge({:filters => { :type_index => lambda { |x| x._type } }}) end end args.delete(search_in_hash) if args.include?(search_in_hash) args << (new_hash || {:filters => { :type_index => lambda { |x| x._type } }}) super end # Override fulltext search to support inheritence def self.fulltext_search(query_string,options={}) options.merge!({:type_index => self.name}) unless self.superclass == Object super(query_string,options) end # Full Text Search Defaults fulltext_search_in :title, :meta_description, :meta_keywords ############################ def method_missing(*args) # Catch Un-initiated Attributes begin super(*args) rescue NoMethodError => e if !self.route_custom_template_class.blank? raise e unless self.route_custom_template_class.all_fields.include?(e.name.to_s) else Rails.logger.error("ERROR (NO METHOD): #{e}") end end end end