class Sequel::Model include Sequel::Serialization # our own custom serializer include Fossil::NumberHelper self.use_transactions=false plugin :validation_helpers Sequel.extension :blank Sequel.extension :inflector def self.find_children Module.constants.find_all do |c_klass| if c_klass!=c_klass.upcase self > (Object.const_get c_klass) rescue nil end end end # Using this order for keys because it makes it easier to query fos records. # If you had a key like "MERE.0.8.40443.1259" for a trip leg and you did this: # TripLeg['MERE',0,8,40443,1259], you would get a trip leg # You still have to massage this key alittle to make the query: # see Sequel.fos_id_to_lookup_key in core_patch.rb PK_KEYS = [:"kid - user", :"kid - mult", :"kid - comm", :"kid - date", :"kid - time"] def fos_id PK_KEYS.collect { |key| value=send(key); value.strip! if value.is_a?(String); value }.join('-') end # Passing in an array of attribute / method names, fills a hash with values from # the model. Can pass in attributes with __ like :passenger__name and the 'name' # value will be delegated to 'passenger' with the delegator being created on the fly. # Can also pass in a hash of attributes, with the key being the key you want # for the hash and the value being the value you want from the model. So you could # pass in {:pax_name=>:passenger__name} and the hash returned will be {:pax_name=>'Bob'} def fill_hash(attributes) if attributes.is_a? Array attributes.inject({}) do |hash, attribute| attribute_name = attribute.to_s.gsub('__', '_').to_sym generate_delegator(attribute) unless respond_to? attribute_name hash[attribute_name] = send(attribute_name) if respond_to? attribute_name hash end elsif attributes.is_a? Hash hash = {} attributes.each do |key, attribute| attribute_name = attribute.to_s.gsub('__', '_').to_sym generate_delegator(attribute) unless respond_to? attribute_name hash[key] = send(attribute_name) if respond_to? attribute_name end hash end end def method_missing(method, * args, & block) if method.to_s.split(/__/).size > 1 generate_delegator(method) send(method.to_s.gsub('__', '_').to_sym) else super(method, * args, & block) end end def generate_delegator(method) arr = method.to_s.split(/__/) # check to see if method_name ( delegator ) exists first to ensure its not created again unless respond_to? arr.join('_') # create delegator on the fly method_call = arr.shift arr.each do |next_call| unless respond_to?(method_call + "_" + next_call) self.class.delegate next_call.to_sym, :to => method_call.to_sym, :prefix => true, :allow_nil => true end method_call = method_call + "_" + next_call end end end class << self attr_accessor :datatypes, :aliases def clear_schema db_schema.clear @datatypes = {} @aliases = {} end def n_to_o(assoc_name, options) association(:many_to_one, assoc_name, options) end def o_to_n(assoc_name, options) association(:one_to_many, assoc_name, options) end def association(assoc_type, assoc_name, options) class_name = options[:class] prefix = options[:prefix] case assoc_type when :one_to_many key = options[:key] || primary_key primary_key = options[:primary_key] || key extra_dataset_conditions = options[:extra_conditions] || {} graph_conditions = {:"#{prefix} kid - date" => :'kid - date', :"#{prefix} kid - time" => :'kid - time', :"#{prefix} kid - user" => :'kid - user', :"#{prefix} kid - mult" => :'kid - mult', :"#{prefix} kid - comm" => :'kid - comm'}. merge(extra_dataset_conditions) dataset_filter = proc { class_name.to_s.constantize.filter( {:"#{prefix} kid - date"=>self[:'kid - date'], :"#{prefix} kid - time"=>self[:'kid - time'], :"#{prefix} kid - user"=>self[:'kid - user'], :"#{prefix} kid - mult"=>self[:'kid - mult'], :"#{prefix} kid - comm"=>self[:'kid - comm']}. merge(extra_dataset_conditions) ) } when :many_to_one key = nil graph_conditions = {:'kid - date'=>:"#{prefix} kid - date", :'kid - time'=>:"#{prefix} kid - time", :'kid - user'=>:"#{prefix} kid - user", :'kid - mult'=>:"#{prefix} kid - mult", :'kid - comm'=>:"#{prefix} kid - comm"} extra_dataset_conditions = options[:dataset_conditions] || {} dataset_filter = proc { class_name.to_s.constantize.filter({:"kid - date"=>self[:"#{prefix} kid - date"], :"kid - time"=>self[:"#{prefix} kid - time"], :"kid - user"=>self[:"#{prefix} kid - user"], :"kid - mult"=>self[:"#{prefix} kid - mult"], :"kid - comm"=>self[:"#{prefix} kid - comm"]}.merge(extra_dataset_conditions)) } end graph_conditions.merge!(options[:graph_only_conditions]) if options[:graph_only_conditions] send(assoc_type, assoc_name, :key=> key, :primary_key=>primary_key, :class => class_name, :dataset=> dataset_filter, :graph_only_conditions=>graph_conditions) end # for creating an association to the 'Codes' table def code_association(assoc_name, column_name_holding_code, code_group_name) # create the many to one association send(:many_to_one, assoc_name, :class=>:Code, :key=>nil, # dataset adds :dataset=>proc { value = self[column_name_holding_code] || send(column_name_holding_code) Code.filter(:'value'=> value, :name=>CodeGroup.send(code_group_name)) }, # graph_only_conditions add the join conditions when using eager_graph in query :graph_only_conditions => {:'value'=> column_name_holding_code, :name=>CodeGroup.send(code_group_name)} ) # create the :get_all method to find all codes of the code group name meta_def "get_all_#{assoc_name.to_s.pluralize}" do Code.get_codes(CodeGroup.send(code_group_name)) end # add a delegator that calls for the code, and one for description delegate :code, :to=> assoc_name, :prefix=>true, :allow_nil => true delegate :description, :to=> assoc_name, :prefix=>true, :allow_nil => true end # date_column,time_column should be a Fixnum because fos stores dates,times as numbers def column_def_datetime(datetime_column_name, date_column, time_column) inst_def datetime_column_name do days = send(:[], date_column) || 0 # nil defaults to 0 days minutes = send(:[], time_column) || 0 # nil defaults to 0 minutes DateTime.from_fos_date_time(days, minutes) # returns utc datetime end end # column_alias :'bad_name', :'bad name', :type=>:date # can set type here if you like def column_alias(new_column_name, old_column_name, options={}) @aliases ||= {} @aliases[new_column_name]= old_column_name # define a getter method on the model class for the aliased column name # will get value from old_column_name inst_def new_column_name do send(:[], old_column_name) end # define a setter method on the model class with the aliased column name # will actually set value to old_column_name inst_def "#{new_column_name}=" do |value| send(:[]=, old_column_name, value) end if options[:type] column_type(new_column_name, options[:type]) column_type(old_column_name, options[:type]) else column_type(new_column_name, :string) end end # set the column type to an existing column # column_type :'bad name', :datetime def column_type(name, type) @datatypes = model.db_schema # if things went wrong and the db_schema could not load up # have to set up the datatypes entry manually @datatypes[name] = {} unless @datatypes[name] @datatypes[name][:type] = type end # define a method to get the value of set the column as a certain type # Used like this in a Model class. # # column_view :'column_name', :date # column_view :'column_name', :time # column_view :'column_name', :time, :custom_method_name # column_view :'column_name', :currency # column_view :'column_name', :boolean # # defines methods for getting new value in the type you requested from # the column_name. The type should be :time, :date, :currency, or :boolean # # For example: # column_name :eta_local, :date, the methods created are: # # eta_local_date => returns a Date # eta_local_date_view => returns a Date formatted y-m-d # eta_local_date_view_mdy => returns a Date formatted m/d/y # eta_local_date_view_mdy_ipc => returns a Date formatted m/d/y with first 0 chopped # ie. instead of 08/07/2009 .. it will be 8/07/2009 # # column_name :eta_local, :time, the methods created are: # # eta_local_time => returns a Time # eta_local_time_view => returns a Time formatted H:M # # column_name :price, :currency, the method created is: # # price_currency => which returns a currecy formatted value 12.15 ( no dollar sign ) # # column_name :item_total, :delimited, the method created is: # # item_total_delimited => which returns a delimited formatted value 12.15 ( no dollar sign ) # # column_name :paid, :boolean, the method created is: # # paid_boolean => which returns a string ("False" / "True") for values ( 0 / 1 ) # def column_view(column_name, type, custom_method_name=nil) raise(StandardError, "Expect the type to be either :date, :datetime, :time, :currency, :precision or :boolean") unless [:date, :time, :datetime, :currency, :precision, :boolean].include? type # will get value from old_column_name method_name = custom_method_name || "#{column_name}_#{type.to_s}" # the method that returns the column value as a Date or Time value inst_def method_name do column_value = send(column_name) case type when :date Date.from_fos_days(column_value) if column_value when :time Time.from_fos_time(column_value) if column_value when :currency number_to_currency((column_value || 0).to_f/100, :unit=>'', :precision=>2) when :precision # precision wont do delimiter .. so send to currency number_to_currency((column_value || 0).to_f/100, :unit=>'', :precision=>2) when :boolean return "True" if column_value and (column_value == 1 or column_value == true) "False" end end method_view_name = method_name.to_s + "_view" # now the view method for all except currency, delimited or boolean unless type == (:currency or :delimited or :boolean) inst_def method_view_name do value = send(method_name) value.to_s if value end end # extra view method for date type if type == :date method_view_name = method_name.to_s + "_view_mdy" # method that returns the column value as a Date in mdy format inst_def method_view_name do column_value = send(column_name) return nil unless column_value Date.from_fos_days(column_value).strftime('%m/%d/%Y') end method_view_name = method_name.to_s + "_view_mdy_ipc" # method that returns the column value as a Date in mdy format inst_def method_view_name do column_value = send(column_name) return nil unless column_value date_str = Date.from_fos_days(column_value).strftime('%m/%d/%Y') date_str.slice!(3) if date_str[3] == 48 # if the fourth char is 0 ( ascii value 48 ) remove it date_str.slice!(0) if date_str[0] == 48 # if the first char is 0 ( ascii value 48 ) remove it date_str end end end def select_fields(table, * fields) dataset.select_fields(table, * fields) end end end