lib/aux_codes.rb in remi-aux_codes-1.0.1 vs lib/aux_codes.rb in remi-aux_codes-1.0.5
- old
+ new
@@ -1,9 +1,19 @@
$:.unshift File.dirname(__FILE__)
%w( rubygems activerecord aux_codes/migration ).each {|lib| require lib }
+# top-level class for AuxCodes
+#
+# used for namespacing and global configuration (once added)
+#
+class AuxCodes
+end
+
+#
+# the basic AuxCode ActiveRecord class
+#
class AuxCode < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name, :scope => :aux_code_id
@@ -26,100 +36,77 @@
def class_name
name.gsub(/[^[:alpha:]]/,'_').titleize.gsub(' ','').singularize
end
+ def to_s
+ name
+ end
+
def [] attribute_or_code_name
if attributes.include?attribute_or_code_name
attributes[attribute_or_code_name]
else
found = codes.select {|c| c.name.to_s =~ /#{attribute_or_code_name}/ }
- if found.empty? # try case insensitive (sans underscores)
- found = codes.select {|c| c.name.downcase.gsub('_',' ').to_s =~
- /#{attribute_or_code_name.to_s.downcase.gsub('_',' ')}/ }
- end
+ if found.empty? # try case insensitive (sans underscores)
+ found = codes.select {|c| c.name.downcase.gsub('_',' ').to_s =~
+ /#{attribute_or_code_name.to_s.downcase.gsub('_',' ')}/ }
+ end
found.first if found
end
end
+ def deserialized_meta_hash
+ require 'yaml'
+ self.meta ||= ""
+ YAML::load(self.meta) || { }
+ end
+
+ def get_meta_attribute meta_attribute
+ deserialized_meta_hash[meta_attribute.to_s]
+ end
+
+ def set_meta_attribute meta_attribute, value
+ require 'yaml'
+ meta_hash = deserialized_meta_hash
+ meta_hash[meta_attribute.to_s] = value
+ self.meta = meta_hash.to_yaml
+ end
+
+ # this allows us to say things like:
+ #
+ # foo = AuxCode.create :name => 'foo'
+ # foo.codes.create :name => 'bar'
+ #
+ # foo.bar # should return the bar aux code under the foo category
+ #
+ # if bar doesn't exist, we throw a normal NoMethodError
+ #
+ # this should check meta_attributes on the object too
+ #
def method_missing_with_indifferent_hash_style_values name, *args, &block
method_missing_without_indifferent_hash_style_values name, *args, &block
rescue NoMethodError => ex
begin
- self[name]
+ if name.to_s[/=$/]
+ self.set_meta_attribute(name.to_s.sub(/=$/,''), args.first) # we said `code.foo= X` so we should set the foo meta attribute to X
+ save
+ else
+ code = self[name]
+ code = self.get_meta_attribute(name) unless code
+ raise ex unless code
+ return code
+ end
rescue
raise ex
end
end
alias_method_chain :method_missing, :indifferent_hash_style_values
- def aux_code_class
- klass = Class.new(AuxCode) do
- class << self
- attr_accessor :aux_code_id, :aux_code
-
- def aux_code
- # @aux_code ||= AuxCode.find aux_code_id
- AuxCode.find aux_code_id
- end
-
- def count_with_aux_code_scope options = {}
- with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
- count_without_aux_code_scope options
- end
- end
- def method_missing_with_aux_code_scope name, *args, &block
- if name.to_s[/^find/]
- if name.to_s[/or_create_by_/]
- name = "#{name}_and_aux_code_id".to_sym
- args << self.aux_code_id
- # method_missing_without_aux_code_scope name, *args
- AuxCode.send name, *args, &block
- else
- with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
- method_missing_without_aux_code_scope name, *args, &block
- end
- end
- else
- method_missing_without_aux_code_scope name, *args, &block
- end
- rescue NoMethodError => ex
- begin
- aux_code.send name, *args, &block # try on the AuxCode instance for this class ...
- rescue
- raise ex
- end
- end
- def find_with_aux_code_scope first_or_all, options = {}
- with_scope(:find => { :conditions => ['aux_code_id = ?', self.aux_code_id] }) do
- find_without_aux_code_scope first_or_all, options
- end
- end
- def create_with_aux_code_scope options = {}
- create_without_aux_code_scope options.merge({ :aux_code_id => self.aux_code_id })
- end
- def create_with_aux_code_scope! options = {}
- create_without_aux_code_scope! options.merge({ :aux_code_id => self.aux_code_id })
- end
- def new_with_aux_code_scope options = {}
- new_without_aux_code_scope options.merge({ :aux_code_id => self.aux_code_id })
- end
-
- alias_method_chain :count, :aux_code_scope
- alias_method_chain :method_missing, :aux_code_scope
- alias_method_chain :find, :aux_code_scope
- alias_method_chain :create, :aux_code_scope
- alias_method_chain :create!, :aux_code_scope
- alias_method_chain :new, :aux_code_scope
- end
- end
-
- klass.aux_code_id = self.id # the class needs to know its own aux_code_id
- klass
- end
-
+ # class methods
class << self
+
def categories
AuxCode.find_all_by_aux_code_id(0)
end
def category_names
@@ -128,12 +115,19 @@
def category category_object_or_id_or_name
obj = category_object_or_id_or_name
return obj if obj.is_a?AuxCode
return AuxCode.find(obj) if obj.is_a?Fixnum
- return AuxCode.find_by_name_and_aux_code_id(obj, 0) if obj.is_a?String
- return AuxCode.find_by_name_and_aux_code_id(obj.to_s, 0) if obj.is_a?Symbol
+ if obj.is_a?(String) || obj.is_a?(Symbol)
+ obj = obj.to_s
+ found = AuxCode.find_by_name_and_aux_code_id(obj, 0)
+ if found.nil?
+ # try replacing underscores with spaces and doing a 'LIKE' search
+ found = AuxCode.find :first, :conditions => ["name LIKE ? AND aux_code_id = ?", obj.gsub('_', ' '), 0]
+ end
+ return found
+ end
raise "I don't know how to find an AuxCode of type #{ obj.class }"
end
alias [] category
def category_codes category_object_or_id_or_name
@@ -149,10 +143,16 @@
AuxCode.categories.each do |category|
Kernel::const_set category.class_name, category.aux_code_class
end
end
+ # this allows us to say things like:
+ #
+ # AuxCode.create :name => 'foo'
+ #
+ # AuxCode.foo # should return the foo category aux code
+ #
def method_missing_with_indifferent_hash_style_values name, *args, &block
unless self.respond_to?:aux_code_id # in which case, this is a *derived* class, not AuxCode
begin
method_missing_without_indifferent_hash_style_values name, *args, &block
rescue NoMethodError => ex
@@ -165,14 +165,81 @@
else
method_missing_without_indifferent_hash_style_values name, *args, &block
end
end
alias_method_chain :method_missing, :indifferent_hash_style_values
+
+ def load_yaml yaml_string
+ require 'yaml'
+ self.load YAML::load(yaml_string)
+ end
+
+ def load_file serialized_yaml_file_path
+ load_yaml File.read(serialized_yaml_file_path)
+ end
+
+ # initialize AuxCodes ... looks for config/aux_codes.yml
+ # and creates classes
+ def init # should eventually take configuration options (hash || block)
+ aux_codes_yml = File.join 'config', 'aux_codes.yml'
+ if File.file? aux_codes_yml
+ load_file aux_codes_yml
+ create_classes!
+ end
+ end
+
+ #
+ # loads AuxCodes (creates them) from a Hash, keyed on the name of the aux code categories to create
+ #
+ # hash: a Hash or an Array [ [key,value], [key,value] ] or anything with an enumerator
+ # that'll work with `hash.each {|key,value| ... }`
+ #
+ def load hash
+ return unless hash.is_a?Hash
+ hash.each do |category_name, codes|
+ category = AuxCode.find_or_create_by_name( category_name.to_s ).aux_code_class
+ codes.each do |name, values|
+
+ # only a name given
+ if values.nil? or values.empty?
+ if name.is_a? String or name.is_a? Symbol # we have a String || Symbol, it's likely the code's name, eg. :foo or 'bar'
+ category.create :name => name.to_s unless category.code_names.include?(name.to_s)
+
+ elsif name.is_a? Hash # we have a Hash, likely with the create options, eg. { :name => 'hi', :foo =>'bar' }
+ category.create name
+
+ else
+ raise "not sure how to create code in category #{ category.name } with: #{ name.inspect }"
+ end
+
+ # we have a name and values
+ else
+ if values.is_a? Hash and (name.is_a? String or name.is_a? Symbol) # we have a Hash, likely with the create options ... we'll merge the name in as :name and create
+ code = category[ name.to_s ]
+ if code
+ values.each do |attribute, new_value|
+ code.send "#{attribute}=", new_value # update values
+ end
+ else
+ code = category.create values.merge({ :name => name.to_s })
+ end
+
+ else
+ raise "not sure how to create code in category #{ category.name } with: #{ name.inspect }, #{ values.inspect }"
+
+ end
+ end
+ end
+ end
+ end
+
end
-protected
+ protected
def set_default_values
self.aux_code_id = 0 unless aux_code_id
end
end
+
+%w( aux_codes/aux_code_class ).each {|lib| require lib } # define the class returned by #aux_code_class