lib/eav_hashes/util.rb in eav_hashes-1.0.1 vs lib/eav_hashes/util.rb in eav_hashes-1.0.2

- old
+ new

@@ -1,122 +1,122 @@ -module ActiveRecord - module EavHashes - module Util - # Sanity checks! - # @param [Hash] options the options hash to check for emptyness and Hashiness - def self.sanity_check(options) - raise "options cannot be empty (and you shouldn't be calling this since you left options blank)" if - (!options.is_a? Hash) or options.empty? - end - - # Fills in any options not explicitly passed to eav_hash_for and creates an EavEntry type for the table - # @param [Hash] options the options hash to be filled with defaults on unset keys. - def self.fill_options_hash(options) - sanity_check options - - # Generate a unique class name based on the eav_hash's name and owner - options[:entry_class_name] ||= "#{options[:parent_class_name]}_#{options[:hash_name]}_entry".camelize.to_sym - - # Strip "_entries" from the table name - if /Entry$/.match options[:entry_class_name] - options[:table_name] ||= options[:entry_class_name].to_s.tableize.slice(0..-9).to_sym - else - options[:table_name] ||= options[:entry_class_name].to_s.tableize.to_sym - end - - # Create the symbol name for the "belongs_to" association in the entry model - options[:parent_assoc_name] ||= "#{options[:parent_class_name].to_s.underscore}".to_sym - - # Create the symbol name for the "has_many" association in the parent model - options[:entry_assoc_name] = options[:entry_class_name].to_s.tableize.to_sym - - # Change slashes to underscores in options to match what's output by the generator - # TODO: Refactor table naming into one location - options[:table_name] = options[:table_name].to_s.gsub(/\//,'_').to_sym - options[:parent_assoc_name] = options[:parent_assoc_name].to_s.gsub(/\//,'_').to_sym - options[:entry_assoc_name] = options[:entry_assoc_name].to_s.gsub(/\//,'_').to_sym - - # Create our custom type if it doesn't exist already - options[:entry_class] = create_eav_table_class options - - return options - end - - # Creates a new type subclassed from ActiveRecord::EavHashes::EavEntry which represents an eav_hash key-value pair - def self.create_eav_table_class (options) - sanity_check options - - # Don't overwrite an existing type - return class_from_string(options[:entry_class_name].to_s) if class_from_string_exists?(options[:entry_class_name]) - - # Create our type - klass = set_constant_from_string options[:entry_class_name].to_s, Class.new(ActiveRecord::EavHashes::EavEntry) - - # Fill in the associations and specify the table it belongs to - klass.class_eval <<-END_EVAL - self.table_name = "#{options[:table_name]}" - belongs_to :#{options[:parent_assoc_name]} - END_EVAL - - return klass - end - - # Searches an EavEntry's table for the specified key/value pair and returns an - # array containing the IDs of the models whose eav_hash key/value pair. - # You should not run this directly. - # @param [String, Symbol] key the key to search by - # @param [Object] value the value to search by. if this is nil, it will return all models which contain `key` - # @param [Hash] options the options hash which eav_hash_for hash generated. - def self.run_find_expression (key, value, options) - sanity_check options - raise "Can't search for a nil key!" if key.nil? - if value.nil? - options[:entry_class].where( - "entry_key = ? and symbol_key = ?", - key.to_s, - key.is_a?(Symbol) - ).pluck("#{options[:parent_assoc_name]}_id".to_sym) - else - val_type = EavEntry.get_value_type value - if val_type == EavEntry::SUPPORTED_TYPES[:Object] - raise "Can't search by Objects/Hashes/Arrays!" - else - options[:entry_class].where( - "entry_key = ? and symbol_key = ? and value = ? and value_type = ?", - key.to_s, - key.is_a?(Symbol), - value.to_s, - val_type - ).pluck("#{options[:parent_assoc_name]}_id".to_sym) - end - end - end - - # Find a class even if it's contained in one or more modules. - # See http://stackoverflow.com/questions/3163641/get-a-class-by-name-in-ruby - def self.class_from_string(str) - str.split('::').inject(Object) do |mod, class_name| - mod.const_get(class_name) - end - end - - # Check whether a class exists, even if it's contained in one or more modules. - def self.class_from_string_exists?(str) - begin - class_from_string(str) - rescue - return false - end - true - end - - # Set a constant from a string, even if the string contains modules. Modules - # are created if necessary. - def self.set_constant_from_string(str, val) - parent = str.deconstantize.split('::').inject(Object) do |mod, class_name| - mod.const_defined?(class_name) ? mod.const_get(class_name) : mod.const_set(class_name, Module.new()) - end - parent.const_set(str.demodulize.to_sym, val) - end - end - end -end +module ActiveRecord + module EavHashes + module Util + # Sanity checks! + # @param [Hash] options the options hash to check for emptyness and Hashiness + def self.sanity_check(options) + raise "options cannot be empty (and you shouldn't be calling this since you left options blank)" if + (!options.is_a? Hash) or options.empty? + end + + # Fills in any options not explicitly passed to eav_hash_for and creates an EavEntry type for the table + # @param [Hash] options the options hash to be filled with defaults on unset keys. + def self.fill_options_hash(options) + sanity_check options + + # Generate a unique class name based on the eav_hash's name and owner + options[:entry_class_name] ||= "#{options[:parent_class_name]}_#{options[:hash_name]}_entry".camelize.to_sym + + # Strip "_entries" from the table name + if /Entry$/.match options[:entry_class_name] + options[:table_name] ||= options[:entry_class_name].to_s.tableize.slice(0..-9).to_sym + else + options[:table_name] ||= options[:entry_class_name].to_s.tableize.to_sym + end + + # Create the symbol name for the "belongs_to" association in the entry model + options[:parent_assoc_name] ||= "#{options[:parent_class_name].to_s.underscore}".to_sym + + # Create the symbol name for the "has_many" association in the parent model + options[:entry_assoc_name] = options[:entry_class_name].to_s.tableize.to_sym + + # Change slashes to underscores in options to match what's output by the generator + # TODO: Refactor table naming into one location + options[:table_name] = options[:table_name].to_s.gsub(/\//,'_').to_sym + options[:parent_assoc_name] = options[:parent_assoc_name].to_s.gsub(/\//,'_').to_sym + options[:entry_assoc_name] = options[:entry_assoc_name].to_s.gsub(/\//,'_').to_sym + + # Create our custom type if it doesn't exist already + options[:entry_class] = create_eav_table_class options + + return options + end + + # Creates a new type subclassed from ActiveRecord::EavHashes::EavEntry which represents an eav_hash key-value pair + def self.create_eav_table_class (options) + sanity_check options + + # Don't overwrite an existing type + return class_from_string(options[:entry_class_name].to_s) if class_from_string_exists?(options[:entry_class_name]) + + # Create our type + klass = set_constant_from_string options[:entry_class_name].to_s, Class.new(ActiveRecord::EavHashes::EavEntry) + + # Fill in the associations and specify the table it belongs to + klass.class_eval <<-END_EVAL + self.table_name = "#{options[:table_name]}" + belongs_to :#{options[:parent_assoc_name]} + END_EVAL + + return klass + end + + # Searches an EavEntry's table for the specified key/value pair and returns an + # array containing the IDs of the models whose eav_hash key/value pair. + # You should not run this directly. + # @param [String, Symbol] key the key to search by + # @param [Object] value the value to search by. if this is nil, it will return all models which contain `key` + # @param [Hash] options the options hash which eav_hash_for hash generated. + def self.run_find_expression (key, value, options) + sanity_check options + raise "Can't search for a nil key!" if key.nil? + if value.nil? + options[:entry_class].where( + "entry_key = ? and symbol_key = ?", + key.to_s, + key.is_a?(Symbol) + ).pluck("#{options[:parent_assoc_name]}_id".to_sym) + else + val_type = EavEntry.get_value_type value + if val_type == EavEntry::SUPPORTED_TYPES[:Object] + raise "Can't search by Objects/Hashes/Arrays!" + else + options[:entry_class].where( + "entry_key = ? and symbol_key = ? and value = ? and value_type = ?", + key.to_s, + key.is_a?(Symbol), + value.to_s, + val_type + ).pluck("#{options[:parent_assoc_name]}_id".to_sym) + end + end + end + + # Find a class even if it's contained in one or more modules. + # See http://stackoverflow.com/questions/3163641/get-a-class-by-name-in-ruby + def self.class_from_string(str) + str.split('::').inject(Object) do |mod, class_name| + mod.const_get(class_name) + end + end + + # Check whether a class exists, even if it's contained in one or more modules. + def self.class_from_string_exists?(str) + begin + class_from_string(str) + rescue + return false + end + true + end + + # Set a constant from a string, even if the string contains modules. Modules + # are created if necessary. + def self.set_constant_from_string(str, val) + parent = str.deconstantize.split('::').inject(Object) do |mod, class_name| + mod.const_defined?(class_name) ? mod.const_get(class_name) : mod.const_set(class_name, Module.new()) + end + parent.const_set(str.demodulize.to_sym, val) + end + end + end +end