meta_search 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
 - data/.gitignore +21 -0
 - data/LICENSE +20 -0
 - data/README.rdoc +101 -0
 - data/Rakefile +53 -0
 - data/VERSION +1 -0
 - data/lib/meta_search/builder.rb +247 -0
 - data/lib/meta_search/exceptions.rb +3 -0
 - data/lib/meta_search/helpers/action_view.rb +168 -0
 - data/lib/meta_search/model_compatibility.rb +8 -0
 - data/lib/meta_search/railtie.rb +21 -0
 - data/lib/meta_search/searches/active_record.rb +19 -0
 - data/lib/meta_search/searches/base.rb +46 -0
 - data/lib/meta_search/utility.rb +85 -0
 - data/lib/meta_search/where.rb +174 -0
 - data/lib/meta_search.rb +31 -0
 - data/meta_search.gemspec +83 -0
 - data/test/fixtures/companies.yml +17 -0
 - data/test/fixtures/company.rb +9 -0
 - data/test/fixtures/data_type.rb +4 -0
 - data/test/fixtures/data_types.yml +15 -0
 - data/test/fixtures/developer.rb +5 -0
 - data/test/fixtures/developers.yml +55 -0
 - data/test/fixtures/developers_projects.yml +25 -0
 - data/test/fixtures/note.rb +3 -0
 - data/test/fixtures/notes.yml +79 -0
 - data/test/fixtures/project.rb +4 -0
 - data/test/fixtures/projects.yml +24 -0
 - data/test/fixtures/schema.rb +47 -0
 - data/test/helper.rb +37 -0
 - data/test/test_search.rb +351 -0
 - data/test/test_view_helpers.rb +149 -0
 - metadata +116 -0
 
| 
         @@ -0,0 +1,46 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'meta_search/builder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MetaSearch
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Searches
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Base
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Prepares the search to run against your model. Returns an instance of
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # MetaSearch::Builder, which behaves pretty much like an ActiveRecord::Relation,
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # in that it doesn't actually query the database until you do something that
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # requires it to do so.
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def search(opts = {})
         
     | 
| 
      
 11 
     | 
    
         
            +
                    opts ||= {} # to catch nil params
         
     | 
| 
      
 12 
     | 
    
         
            +
                    search_options = opts.delete(:search_options) || {}
         
     | 
| 
      
 13 
     | 
    
         
            +
                    builder = MetaSearch::Builder.new(self, search_options)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    builder.build(opts)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  
         
     | 
| 
      
 17 
     | 
    
         
            +
                  private
         
     | 
| 
      
 18 
     | 
    
         
            +
                  
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # Excludes model attributes from searchability. This means that searches can't be created against
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # these columns, whether the search is based on this model, or the model's attributes are being
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # searched by association from another model. If a Comment <tt>belongs_to :article</tt> but declares
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # <tt>metasearch_exclude_attr :user_id</tt> then <tt>Comment.search</tt> won't accept parameters
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # like <tt>:user_id_equals</tt>, nor will an Article.search accept the parameter
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # <tt>:comments_user_id_equals</tt>.
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def metasearch_exclude_attr(*args)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    args.each do |attr|
         
     | 
| 
      
 27 
     | 
    
         
            +
                      attr = attr.to_s
         
     | 
| 
      
 28 
     | 
    
         
            +
                      raise(ArgumentError, "No persisted attribute (column) named #{attr} in #{self}") unless self.columns_hash.has_key?(attr)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      self._metasearch_exclude_attributes = (self._metasearch_exclude_attributes + [attr]).uniq
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # Excludes model associations from searchability. This mean that searches can't be created against
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # these associations. An article that <tt>has_many :comments</tt> but excludes comments from
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # searching by declaring <tt>metasearch_exclude_assoc :comments</tt> won't make any of the
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # <tt>comments_*</tt> methods available.
         
     | 
| 
      
 37 
     | 
    
         
            +
                  def metasearch_exclude_assoc(*args)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    args.each do |assoc|
         
     | 
| 
      
 39 
     | 
    
         
            +
                      assoc = assoc.to_s
         
     | 
| 
      
 40 
     | 
    
         
            +
                      raise(ArgumentError, "No such association #{assoc} in #{self}") unless self.reflect_on_all_associations.map {|a| a.name.to_s}.include?(assoc)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      self._metasearch_exclude_associations = (self._metasearch_exclude_associations + [assoc]).uniq
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'meta_search/exceptions'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MetaSearch
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Utility #:nodoc:
         
     | 
| 
      
 5 
     | 
    
         
            +
                private
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                def array_of_arrays?(vals)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  vals.is_a?(Array) && vals.first.is_a?(Array)
         
     | 
| 
      
 9 
     | 
    
         
            +
                end
         
     | 
| 
      
 10 
     | 
    
         
            +
                
         
     | 
| 
      
 11 
     | 
    
         
            +
                def array_of_dates?(vals)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  vals.is_a?(Array) && vals.first.respond_to?(:to_time)
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
                
         
     | 
| 
      
 15 
     | 
    
         
            +
                def cast_attributes(type, vals)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  if array_of_arrays?(vals)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    vals.map! {|v| cast_attributes(type, v)}
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # Need to make sure not to kill multiparam dates/times
         
     | 
| 
      
 19 
     | 
    
         
            +
                  elsif vals.is_a?(Array) && (array_of_dates?(vals) || !(DATES+TIMES).include?(type))
         
     | 
| 
      
 20 
     | 
    
         
            +
                    vals.map! {|v| cast_attribute(type, v)}
         
     | 
| 
      
 21 
     | 
    
         
            +
                  else
         
     | 
| 
      
 22 
     | 
    
         
            +
                    cast_attribute(type, vals)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def cast_attribute(type, val)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  case type
         
     | 
| 
      
 28 
     | 
    
         
            +
                  when *STRINGS
         
     | 
| 
      
 29 
     | 
    
         
            +
                    val.respond_to?(:to_s) ? val.to_s : String.new(val)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  when *DATES
         
     | 
| 
      
 31 
     | 
    
         
            +
                    if val.respond_to?(:to_date)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      val.to_date
         
     | 
| 
      
 33 
     | 
    
         
            +
                    else
         
     | 
| 
      
 34 
     | 
    
         
            +
                      y, m, d = *[val].flatten
         
     | 
| 
      
 35 
     | 
    
         
            +
                      m ||= 1
         
     | 
| 
      
 36 
     | 
    
         
            +
                      d ||= 1
         
     | 
| 
      
 37 
     | 
    
         
            +
                      Date.new(y,m,d) rescue nil
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  when *TIMES
         
     | 
| 
      
 40 
     | 
    
         
            +
                    if val.respond_to?(:to_time)
         
     | 
| 
      
 41 
     | 
    
         
            +
                      val.to_time
         
     | 
| 
      
 42 
     | 
    
         
            +
                    else
         
     | 
| 
      
 43 
     | 
    
         
            +
                      y, m, d, hh, mm, ss = *[val].flatten
         
     | 
| 
      
 44 
     | 
    
         
            +
                      Time.zone.local(y, m, d, hh, mm, ss) rescue nil
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  when *BOOLEANS
         
     | 
| 
      
 47 
     | 
    
         
            +
                    ActiveRecord::ConnectionAdapters::Column.value_to_boolean(val)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  when :integer
         
     | 
| 
      
 49 
     | 
    
         
            +
                    val.blank? ? nil : val.to_i
         
     | 
| 
      
 50 
     | 
    
         
            +
                  when :float
         
     | 
| 
      
 51 
     | 
    
         
            +
                    val.blank? ? nil : val.to_f
         
     | 
| 
      
 52 
     | 
    
         
            +
                  when :decimal
         
     | 
| 
      
 53 
     | 
    
         
            +
                    val.blank? ? nil : ActiveRecord::ConnectionAdapters::Column.value_to_decimal(val)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  else
         
     | 
| 
      
 55 
     | 
    
         
            +
                    raise TypeCastError, "Unable to cast columns of type #{type}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
                
         
     | 
| 
      
 59 
     | 
    
         
            +
                def collapse_multiparameter_options(opts)
         
     | 
| 
      
 60 
     | 
    
         
            +
                  opts.each_key do |k|
         
     | 
| 
      
 61 
     | 
    
         
            +
                    if k.include?("(")
         
     | 
| 
      
 62 
     | 
    
         
            +
                      real_attribute, position = k.split(/\(|\)/)
         
     | 
| 
      
 63 
     | 
    
         
            +
                      cast = %w(a s i).include?(position.last) ? position.last : nil
         
     | 
| 
      
 64 
     | 
    
         
            +
                      position = position.to_i - 1
         
     | 
| 
      
 65 
     | 
    
         
            +
                      value = opts.delete(k)
         
     | 
| 
      
 66 
     | 
    
         
            +
                      opts[real_attribute] ||= []
         
     | 
| 
      
 67 
     | 
    
         
            +
                      opts[real_attribute][position] = if cast
         
     | 
| 
      
 68 
     | 
    
         
            +
                        (value.blank? && cast == 'i') ? nil : value.send("to_#{cast}")
         
     | 
| 
      
 69 
     | 
    
         
            +
                      else
         
     | 
| 
      
 70 
     | 
    
         
            +
                        value
         
     | 
| 
      
 71 
     | 
    
         
            +
                      end
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                  opts
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
                
         
     | 
| 
      
 77 
     | 
    
         
            +
                def quote_table_name(name)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  ActiveRecord::Base.connection.quote_table_name(name)
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
                
         
     | 
| 
      
 81 
     | 
    
         
            +
                def quote_column_name(name)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  ActiveRecord::Base.connection.quote_column_name(name)
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,174 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'meta_search/exceptions'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MetaSearch
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Wheres are how MetaSearch does its magic. Wheres have a name (and possible aliases) which are
         
     | 
| 
      
 5 
     | 
    
         
            +
              # appended to your model and association attributes. When you instantiate a MetaSearch::Builder
         
     | 
| 
      
 6 
     | 
    
         
            +
              # against a model (manually or by calling your model's +search+ method) the builder responds to
         
     | 
| 
      
 7 
     | 
    
         
            +
              # methods named for your model's attributes and associations, suffixed by the name of the Where.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # These are the default Wheres, broken down by the types of ActiveRecord columns they can search
         
     | 
| 
      
 10 
     | 
    
         
            +
              # against:
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              # === All data types
         
     | 
| 
      
 13 
     | 
    
         
            +
              #
         
     | 
| 
      
 14 
     | 
    
         
            +
              # * _equals_ (alias: _eq_) - Just as it sounds.
         
     | 
| 
      
 15 
     | 
    
         
            +
              # * _does_not_equal_ (alias: _ne_) - The opposite of equals, oddly enough.
         
     | 
| 
      
 16 
     | 
    
         
            +
              # * _in_ - Takes an array, matches on equality with any of the items in the array.
         
     | 
| 
      
 17 
     | 
    
         
            +
              # * _not_in_ (alias: _ni_) - Like above, but negated.
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              # === Strings
         
     | 
| 
      
 20 
     | 
    
         
            +
              #
         
     | 
| 
      
 21 
     | 
    
         
            +
              # * _contains_ (alias: _like_) - Substring match.
         
     | 
| 
      
 22 
     | 
    
         
            +
              # * _does_not_contain_ (alias: _nlike_) - Negative substring match.
         
     | 
| 
      
 23 
     | 
    
         
            +
              # * _starts_with_ (alias: _sw_) - Match strings beginning with the entered term.
         
     | 
| 
      
 24 
     | 
    
         
            +
              # * _does_not_start_with_ (alias: _dnsw_) - The opposite of above.
         
     | 
| 
      
 25 
     | 
    
         
            +
              # * _ends_with_ (alias: _ew_) - Match strings ending with the entered term.
         
     | 
| 
      
 26 
     | 
    
         
            +
              # * _does_not_end_with_ (alias: _dnew_) - Negative of above.
         
     | 
| 
      
 27 
     | 
    
         
            +
              #
         
     | 
| 
      
 28 
     | 
    
         
            +
              # === Numbers, dates, and times
         
     | 
| 
      
 29 
     | 
    
         
            +
              #
         
     | 
| 
      
 30 
     | 
    
         
            +
              # * _greater_than_ (alias: _gt_) - Greater than.
         
     | 
| 
      
 31 
     | 
    
         
            +
              # * _greater_than_or_equal_to_ (alias: _gte_) - Greater than or equal to.
         
     | 
| 
      
 32 
     | 
    
         
            +
              # * _less_than_ (alias: _lt_) - Less than.
         
     | 
| 
      
 33 
     | 
    
         
            +
              # * _less_than_or_equal_to_ (alias: _lte_) - Less than or equal to.
         
     | 
| 
      
 34 
     | 
    
         
            +
              #
         
     | 
| 
      
 35 
     | 
    
         
            +
              # So, given a model like this...
         
     | 
| 
      
 36 
     | 
    
         
            +
              #
         
     | 
| 
      
 37 
     | 
    
         
            +
              #   class Article < ActiveRecord::Base
         
     | 
| 
      
 38 
     | 
    
         
            +
              #     belongs_to :author
         
     | 
| 
      
 39 
     | 
    
         
            +
              #     has_many :comments
         
     | 
| 
      
 40 
     | 
    
         
            +
              #     has_many :moderations, :through => :comments
         
     | 
| 
      
 41 
     | 
    
         
            +
              #   end
         
     | 
| 
      
 42 
     | 
    
         
            +
              #
         
     | 
| 
      
 43 
     | 
    
         
            +
              # ...you might end up with attributes like <tt>title_contains</tt>,
         
     | 
| 
      
 44 
     | 
    
         
            +
              # <tt>comments_title_starts_with</tt>, <tt>moderations_value_less_than</tt>,
         
     | 
| 
      
 45 
     | 
    
         
            +
              # <tt>author_name_equals</tt>, and so on.
         
     | 
| 
      
 46 
     | 
    
         
            +
              class Where
         
     | 
| 
      
 47 
     | 
    
         
            +
                attr_reader :name, :aliases, :types, :condition, :substitutions, :formatter
         
     | 
| 
      
 48 
     | 
    
         
            +
                def initialize(where)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  if [String,Symbol].include?(where.class)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    where = Where.get(where) or raise ArgumentError("A where could not be instantiated for the argument #{where}")
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  @name = where[:name]
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @aliases = where[:aliases]
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @types = where[:types]
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @condition = where[:condition]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @substitutions = where[:substitutions]
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @formatter = where[:formatter]
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @keep_arrays = where[:keep_arrays]
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
                
         
     | 
| 
      
 61 
     | 
    
         
            +
                def keep_arrays?
         
     | 
| 
      
 62 
     | 
    
         
            +
                  @keep_arrays
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
                
         
     | 
| 
      
 65 
     | 
    
         
            +
                # Checks that the given +value+ is valid to use for the substitutions of this where.
         
     | 
| 
      
 66 
     | 
    
         
            +
                # Requires that there are the same number of parameters as substitutions, and none of'
         
     | 
| 
      
 67 
     | 
    
         
            +
                # them is blank.
         
     | 
| 
      
 68 
     | 
    
         
            +
                def valid_substitutions?(*values)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  values.flatten! unless values.size > 1 || self.keep_arrays?
         
     | 
| 
      
 70 
     | 
    
         
            +
                  self.substitutions.count('?') == values.select {|v| !v.blank?}.size
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
                
         
     | 
| 
      
 73 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # At application initialization, you can add additional custom Wheres to the mix.
         
     | 
| 
      
 75 
     | 
    
         
            +
                  # in your application's <tt>config/initializers/meta_search.rb</tt>, place lines
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # like this:
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  #   MetaSearch::Where.add :between, :btw, {
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #     :types =>  [:integer, :float, :decimal, :date, :datetime, :timestamp, :time],
         
     | 
| 
      
 80 
     | 
    
         
            +
                  #     :condition => 'BETWEEN',
         
     | 
| 
      
 81 
     | 
    
         
            +
                  #     :substitutions => '? AND ?',
         
     | 
| 
      
 82 
     | 
    
         
            +
                  #     :formatter => Proc.new {|param| param}
         
     | 
| 
      
 83 
     | 
    
         
            +
                  #   }
         
     | 
| 
      
 84 
     | 
    
         
            +
                  #
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # The first options are all names for the where. Well, the first is a name, the rest
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # are aliases, really. They will determine the suffix you will use to access your Where.
         
     | 
| 
      
 87 
     | 
    
         
            +
                  #
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # <tt>types</tt> is an array of types the comparison is valid for. The where will not
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # be available against columns that are not one of these types. Default is +ALL_TYPES+,
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # Which is one of several MetaSearch constants available for type assignment (the others
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # being +DATES+, +TIIMES+, +STRINGS+, and +NUMBERS+).
         
     | 
| 
      
 92 
     | 
    
         
            +
                  #
         
     | 
| 
      
 93 
     | 
    
         
            +
                  # <tt>condition</tt> is the condition placed between the column and the substitutions, such as
         
     | 
| 
      
 94 
     | 
    
         
            +
                  # BETWEEN, IN, or =. Default is =.
         
     | 
| 
      
 95 
     | 
    
         
            +
                  #
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # <tt>substitutions</tt> is the text that comes next. It's normally going to have some
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # question marks in it (for variable substitution) if it's going to be of much use. The
         
     | 
| 
      
 98 
     | 
    
         
            +
                  # default is ?. Keep in mind if you use more than one ? MetaSearch will require an array
         
     | 
| 
      
 99 
     | 
    
         
            +
                  # be passed to the attribute for substitution.
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # <tt>keep_arrays</tt> tells MetaSearch that if any arrays are received as parameters, they
         
     | 
| 
      
 102 
     | 
    
         
            +
                  # should be used as-is in the substitution, rather than flattened and passed as a list.
         
     | 
| 
      
 103 
     | 
    
         
            +
                  # For example, this is the definition of the "in" Where:
         
     | 
| 
      
 104 
     | 
    
         
            +
                  #
         
     | 
| 
      
 105 
     | 
    
         
            +
                  #   ['in', {:types => ALL_TYPES, :condition => 'IN', :substitutions => '(?)',
         
     | 
| 
      
 106 
     | 
    
         
            +
                  #    :keep_arrays => true}]
         
     | 
| 
      
 107 
     | 
    
         
            +
                  #
         
     | 
| 
      
 108 
     | 
    
         
            +
                  # <tt>formatter</tt> is the Proc that will do any formatting to the variables to be substituted.
         
     | 
| 
      
 109 
     | 
    
         
            +
                  # The default proc is <tt>{|param| param}</tt>, which doesn't really do anything. If you pass a
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # string, it will be +eval+ed in the context of this Proc.
         
     | 
| 
      
 111 
     | 
    
         
            +
                  #
         
     | 
| 
      
 112 
     | 
    
         
            +
                  # For example, this is the definition of the "contains" Where:
         
     | 
| 
      
 113 
     | 
    
         
            +
                  #
         
     | 
| 
      
 114 
     | 
    
         
            +
                  #   ['contains', 'like', {:types => STRINGS, :condition => 'LIKE', :formatter => '"%#{param}%"'}]
         
     | 
| 
      
 115 
     | 
    
         
            +
                  #
         
     | 
| 
      
 116 
     | 
    
         
            +
                  # Be sure to single-quote the string, so that variables aren't interpolated until later. If in doubt,
         
     | 
| 
      
 117 
     | 
    
         
            +
                  # just use a Proc.
         
     | 
| 
      
 118 
     | 
    
         
            +
                  def add(*args)
         
     | 
| 
      
 119 
     | 
    
         
            +
                    opts = args.last.is_a?(Hash) ? args.pop : {}
         
     | 
| 
      
 120 
     | 
    
         
            +
                    args = args.compact.flatten.map {|a| a.to_s }
         
     | 
| 
      
 121 
     | 
    
         
            +
                    raise ArgumentError, "Name parameter required" if args.blank?
         
     | 
| 
      
 122 
     | 
    
         
            +
                    opts[:name] ||= args.first
         
     | 
| 
      
 123 
     | 
    
         
            +
                    opts[:types] ||= ALL_TYPES
         
     | 
| 
      
 124 
     | 
    
         
            +
                    opts[:types] = [opts[:types]].flatten
         
     | 
| 
      
 125 
     | 
    
         
            +
                    opts[:condition] ||= '='
         
     | 
| 
      
 126 
     | 
    
         
            +
                    opts[:substitutions] ||= '?'
         
     | 
| 
      
 127 
     | 
    
         
            +
                    opts[:keep_arrays] ||= false
         
     | 
| 
      
 128 
     | 
    
         
            +
                    opts[:formatter] ||= Proc.new {|param| param}
         
     | 
| 
      
 129 
     | 
    
         
            +
                    if opts[:formatter].is_a?(String)
         
     | 
| 
      
 130 
     | 
    
         
            +
                      formatter = opts[:formatter]
         
     | 
| 
      
 131 
     | 
    
         
            +
                      opts[:formatter] = Proc.new {|param| eval formatter}
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
                    opts[:aliases] ||= [args - [opts[:name]]].flatten
         
     | 
| 
      
 134 
     | 
    
         
            +
                    @@wheres ||= {}
         
     | 
| 
      
 135 
     | 
    
         
            +
                    if @@wheres.has_key?(opts[:name])
         
     | 
| 
      
 136 
     | 
    
         
            +
                      raise ArgumentError, "\"#{opts[:name]}\" is not available for use as a where name."
         
     | 
| 
      
 137 
     | 
    
         
            +
                    end
         
     | 
| 
      
 138 
     | 
    
         
            +
                    @@wheres[opts[:name]] = opts
         
     | 
| 
      
 139 
     | 
    
         
            +
                    opts[:aliases].each do |a|
         
     | 
| 
      
 140 
     | 
    
         
            +
                      if @@wheres.has_key?(a)
         
     | 
| 
      
 141 
     | 
    
         
            +
                        opts[:aliases].delete(a)
         
     | 
| 
      
 142 
     | 
    
         
            +
                      else
         
     | 
| 
      
 143 
     | 
    
         
            +
                        @@wheres[a] = opts[:name]
         
     | 
| 
      
 144 
     | 
    
         
            +
                      end
         
     | 
| 
      
 145 
     | 
    
         
            +
                    end
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
      
 147 
     | 
    
         
            +
                  
         
     | 
| 
      
 148 
     | 
    
         
            +
                  # Returns the complete array of Wheres
         
     | 
| 
      
 149 
     | 
    
         
            +
                  def all
         
     | 
| 
      
 150 
     | 
    
         
            +
                    @@wheres
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                  
         
     | 
| 
      
 153 
     | 
    
         
            +
                  # Get the where matching a method or condition.
         
     | 
| 
      
 154 
     | 
    
         
            +
                  def get(method_id_or_condition)
         
     | 
| 
      
 155 
     | 
    
         
            +
                    return nil unless where_key = @@wheres.keys.
         
     | 
| 
      
 156 
     | 
    
         
            +
                      sort {|a,b| b.length <=> a.length}.
         
     | 
| 
      
 157 
     | 
    
         
            +
                      detect {|n| method_id_or_condition.to_s.match(/#{n}=?$/)}
         
     | 
| 
      
 158 
     | 
    
         
            +
                    where = @@wheres[where_key]
         
     | 
| 
      
 159 
     | 
    
         
            +
                    where = @@wheres[where] if where.is_a?(String)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    where
         
     | 
| 
      
 161 
     | 
    
         
            +
                  end
         
     | 
| 
      
 162 
     | 
    
         
            +
                  
         
     | 
| 
      
 163 
     | 
    
         
            +
                  # Set the wheres to their default values, removing any customized settings.
         
     | 
| 
      
 164 
     | 
    
         
            +
                  def initialize_wheres
         
     | 
| 
      
 165 
     | 
    
         
            +
                    @@wheres = {}
         
     | 
| 
      
 166 
     | 
    
         
            +
                    DEFAULT_WHERES.each do |where|
         
     | 
| 
      
 167 
     | 
    
         
            +
                      add(*where)
         
     | 
| 
      
 168 
     | 
    
         
            +
                    end
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
              end
         
     | 
| 
      
 172 
     | 
    
         
            +
              
         
     | 
| 
      
 173 
     | 
    
         
            +
              Where.initialize_wheres
         
     | 
| 
      
 174 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/meta_search.rb
    ADDED
    
    | 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module MetaSearch
         
     | 
| 
      
 2 
     | 
    
         
            +
              NUMBERS = [:integer, :float, :decimal]
         
     | 
| 
      
 3 
     | 
    
         
            +
              STRINGS = [:string, :text, :binary]
         
     | 
| 
      
 4 
     | 
    
         
            +
              DATES = [:date]
         
     | 
| 
      
 5 
     | 
    
         
            +
              TIMES = [:datetime, :timestamp, :time]
         
     | 
| 
      
 6 
     | 
    
         
            +
              BOOLEANS = [:boolean]
         
     | 
| 
      
 7 
     | 
    
         
            +
              ALL_TYPES = NUMBERS + STRINGS + DATES + TIMES + BOOLEANS
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              DEFAULT_WHERES = [
         
     | 
| 
      
 10 
     | 
    
         
            +
                ['equals', 'eq'],
         
     | 
| 
      
 11 
     | 
    
         
            +
                ['does_not_equal', 'ne', {:types => ALL_TYPES, :condition => '!='}],
         
     | 
| 
      
 12 
     | 
    
         
            +
                ['contains', 'like', {:types => STRINGS, :condition => 'LIKE', :formatter => '"%#{param}%"'}],
         
     | 
| 
      
 13 
     | 
    
         
            +
                ['does_not_contain', 'nlike', {:types => STRINGS, :condition => 'NOT LIKE', :formatter => '"%#{param}%"'}],
         
     | 
| 
      
 14 
     | 
    
         
            +
                ['starts_with', 'sw', {:types => STRINGS, :condition => 'LIKE', :formatter => '"#{param}%"'}],
         
     | 
| 
      
 15 
     | 
    
         
            +
                ['does_not_start_with', 'dnsw', {:types => STRINGS, :condition => 'NOT LIKE', :formatter => '"%#{param}%"'}],
         
     | 
| 
      
 16 
     | 
    
         
            +
                ['ends_with', 'ew', {:types => STRINGS, :condition => 'LIKE', :formatter => '"%#{param}"'}],
         
     | 
| 
      
 17 
     | 
    
         
            +
                ['does_not_end_with', 'dnew', {:types => STRINGS, :condition => 'NOT LIKE', :formatter => '"%#{param}"'}],
         
     | 
| 
      
 18 
     | 
    
         
            +
                ['greater_than', 'gt', {:types => (NUMBERS + DATES + TIMES), :condition => '>'}],
         
     | 
| 
      
 19 
     | 
    
         
            +
                ['less_than', 'lt', {:types => (NUMBERS + DATES + TIMES), :condition => '<'}],
         
     | 
| 
      
 20 
     | 
    
         
            +
                ['greater_than_or_equal_to', 'gte', {:types => (NUMBERS + DATES + TIMES), :condition => '>='}],
         
     | 
| 
      
 21 
     | 
    
         
            +
                ['less_than_or_equal_to', 'lte', {:types => (NUMBERS + DATES + TIMES), :condition => '<='}],
         
     | 
| 
      
 22 
     | 
    
         
            +
                ['in', {:types => ALL_TYPES, :condition => 'IN', :substitutions => '(?)', :keep_arrays => true}],
         
     | 
| 
      
 23 
     | 
    
         
            +
                ['not_in', 'ni', {:types => ALL_TYPES, :condition => 'NOT IN', :substitutions => '(?)', :keep_arrays => true}]
         
     | 
| 
      
 24 
     | 
    
         
            +
              ]
         
     | 
| 
      
 25 
     | 
    
         
            +
              
         
     | 
| 
      
 26 
     | 
    
         
            +
              RELATION_METHODS = [:joins, :includes, :all, :count, :to_sql, :paginate, :find_each, :first, :last, :each]
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            if defined?(::Rails::Railtie)
         
     | 
| 
      
 30 
     | 
    
         
            +
              require 'meta_search/railtie'
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
    
        data/meta_search.gemspec
    ADDED
    
    | 
         @@ -0,0 +1,83 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # Generated by jeweler
         
     | 
| 
      
 2 
     | 
    
         
            +
            # DO NOT EDIT THIS FILE DIRECTLY
         
     | 
| 
      
 3 
     | 
    
         
            +
            # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
         
     | 
| 
      
 4 
     | 
    
         
            +
            # -*- encoding: utf-8 -*-
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Gem::Specification.new do |s|
         
     | 
| 
      
 7 
     | 
    
         
            +
              s.name = %q{meta_search}
         
     | 
| 
      
 8 
     | 
    
         
            +
              s.version = "0.3.0"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
         
     | 
| 
      
 11 
     | 
    
         
            +
              s.authors = ["Ernie Miller"]
         
     | 
| 
      
 12 
     | 
    
         
            +
              s.date = %q{2010-03-16}
         
     | 
| 
      
 13 
     | 
    
         
            +
              s.description = %q{Adds a search method to your ActiveRecord models which returns an object to be used in form_for while constructing a search. Works with Rails 3 only.}
         
     | 
| 
      
 14 
     | 
    
         
            +
              s.email = %q{ernie@metautonomo.us}
         
     | 
| 
      
 15 
     | 
    
         
            +
              s.extra_rdoc_files = [
         
     | 
| 
      
 16 
     | 
    
         
            +
                "LICENSE",
         
     | 
| 
      
 17 
     | 
    
         
            +
                 "README.rdoc"
         
     | 
| 
      
 18 
     | 
    
         
            +
              ]
         
     | 
| 
      
 19 
     | 
    
         
            +
              s.files = [
         
     | 
| 
      
 20 
     | 
    
         
            +
                ".document",
         
     | 
| 
      
 21 
     | 
    
         
            +
                 ".gitignore",
         
     | 
| 
      
 22 
     | 
    
         
            +
                 "LICENSE",
         
     | 
| 
      
 23 
     | 
    
         
            +
                 "README.rdoc",
         
     | 
| 
      
 24 
     | 
    
         
            +
                 "Rakefile",
         
     | 
| 
      
 25 
     | 
    
         
            +
                 "VERSION",
         
     | 
| 
      
 26 
     | 
    
         
            +
                 "lib/meta_search.rb",
         
     | 
| 
      
 27 
     | 
    
         
            +
                 "lib/meta_search/builder.rb",
         
     | 
| 
      
 28 
     | 
    
         
            +
                 "lib/meta_search/exceptions.rb",
         
     | 
| 
      
 29 
     | 
    
         
            +
                 "lib/meta_search/helpers/action_view.rb",
         
     | 
| 
      
 30 
     | 
    
         
            +
                 "lib/meta_search/model_compatibility.rb",
         
     | 
| 
      
 31 
     | 
    
         
            +
                 "lib/meta_search/railtie.rb",
         
     | 
| 
      
 32 
     | 
    
         
            +
                 "lib/meta_search/searches/active_record.rb",
         
     | 
| 
      
 33 
     | 
    
         
            +
                 "lib/meta_search/searches/base.rb",
         
     | 
| 
      
 34 
     | 
    
         
            +
                 "lib/meta_search/utility.rb",
         
     | 
| 
      
 35 
     | 
    
         
            +
                 "lib/meta_search/where.rb",
         
     | 
| 
      
 36 
     | 
    
         
            +
                 "meta_search.gemspec",
         
     | 
| 
      
 37 
     | 
    
         
            +
                 "test/fixtures/companies.yml",
         
     | 
| 
      
 38 
     | 
    
         
            +
                 "test/fixtures/company.rb",
         
     | 
| 
      
 39 
     | 
    
         
            +
                 "test/fixtures/data_type.rb",
         
     | 
| 
      
 40 
     | 
    
         
            +
                 "test/fixtures/data_types.yml",
         
     | 
| 
      
 41 
     | 
    
         
            +
                 "test/fixtures/developer.rb",
         
     | 
| 
      
 42 
     | 
    
         
            +
                 "test/fixtures/developers.yml",
         
     | 
| 
      
 43 
     | 
    
         
            +
                 "test/fixtures/developers_projects.yml",
         
     | 
| 
      
 44 
     | 
    
         
            +
                 "test/fixtures/note.rb",
         
     | 
| 
      
 45 
     | 
    
         
            +
                 "test/fixtures/notes.yml",
         
     | 
| 
      
 46 
     | 
    
         
            +
                 "test/fixtures/project.rb",
         
     | 
| 
      
 47 
     | 
    
         
            +
                 "test/fixtures/projects.yml",
         
     | 
| 
      
 48 
     | 
    
         
            +
                 "test/fixtures/schema.rb",
         
     | 
| 
      
 49 
     | 
    
         
            +
                 "test/helper.rb",
         
     | 
| 
      
 50 
     | 
    
         
            +
                 "test/test_search.rb",
         
     | 
| 
      
 51 
     | 
    
         
            +
                 "test/test_view_helpers.rb"
         
     | 
| 
      
 52 
     | 
    
         
            +
              ]
         
     | 
| 
      
 53 
     | 
    
         
            +
              s.homepage = %q{http://metautonomo.us}
         
     | 
| 
      
 54 
     | 
    
         
            +
              s.rdoc_options = ["--charset=UTF-8"]
         
     | 
| 
      
 55 
     | 
    
         
            +
              s.require_paths = ["lib"]
         
     | 
| 
      
 56 
     | 
    
         
            +
              s.rubygems_version = %q{1.3.6}
         
     | 
| 
      
 57 
     | 
    
         
            +
              s.summary = %q{ActiveRecord 3 object-based searching.}
         
     | 
| 
      
 58 
     | 
    
         
            +
              s.test_files = [
         
     | 
| 
      
 59 
     | 
    
         
            +
                "test/fixtures/company.rb",
         
     | 
| 
      
 60 
     | 
    
         
            +
                 "test/fixtures/data_type.rb",
         
     | 
| 
      
 61 
     | 
    
         
            +
                 "test/fixtures/developer.rb",
         
     | 
| 
      
 62 
     | 
    
         
            +
                 "test/fixtures/note.rb",
         
     | 
| 
      
 63 
     | 
    
         
            +
                 "test/fixtures/project.rb",
         
     | 
| 
      
 64 
     | 
    
         
            +
                 "test/fixtures/schema.rb",
         
     | 
| 
      
 65 
     | 
    
         
            +
                 "test/helper.rb",
         
     | 
| 
      
 66 
     | 
    
         
            +
                 "test/test_search.rb",
         
     | 
| 
      
 67 
     | 
    
         
            +
                 "test/test_view_helpers.rb"
         
     | 
| 
      
 68 
     | 
    
         
            +
              ]
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              if s.respond_to? :specification_version then
         
     | 
| 
      
 71 
     | 
    
         
            +
                current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
         
     | 
| 
      
 72 
     | 
    
         
            +
                s.specification_version = 3
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
         
     | 
| 
      
 75 
     | 
    
         
            +
                  s.add_development_dependency(%q<activerecord>, [">= 3.0.0.beta"])
         
     | 
| 
      
 76 
     | 
    
         
            +
                else
         
     | 
| 
      
 77 
     | 
    
         
            +
                  s.add_dependency(%q<activerecord>, [">= 3.0.0.beta"])
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
              else
         
     | 
| 
      
 80 
     | 
    
         
            +
                s.add_dependency(%q<activerecord>, [">= 3.0.0.beta"])
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
            end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            initech:
         
     | 
| 
      
 2 
     | 
    
         
            +
              name      : Initech
         
     | 
| 
      
 3 
     | 
    
         
            +
              id        : 1
         
     | 
| 
      
 4 
     | 
    
         
            +
              created_at: 1999-02-19 08:00
         
     | 
| 
      
 5 
     | 
    
         
            +
              updated_at: 1999-02-19 08:00
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            aos:
         
     | 
| 
      
 8 
     | 
    
         
            +
              name: Advanced Optical Solutions
         
     | 
| 
      
 9 
     | 
    
         
            +
              id  : 2
         
     | 
| 
      
 10 
     | 
    
         
            +
              created_at: 2004-02-01 08:00
         
     | 
| 
      
 11 
     | 
    
         
            +
              updated_at: 2004-02-01 08:00
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            mission_data:
         
     | 
| 
      
 14 
     | 
    
         
            +
              name: Mission Data
         
     | 
| 
      
 15 
     | 
    
         
            +
              id  : 3
         
     | 
| 
      
 16 
     | 
    
         
            +
              created_at: 1996-09-21 08:00
         
     | 
| 
      
 17 
     | 
    
         
            +
              updated_at: 1996-09-21 08:00
         
     | 
| 
         @@ -0,0 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class Company < ActiveRecord::Base
         
     | 
| 
      
 2 
     | 
    
         
            +
              has_many :developers
         
     | 
| 
      
 3 
     | 
    
         
            +
              has_many :developer_notes, :through => :developers, :source => :notes
         
     | 
| 
      
 4 
     | 
    
         
            +
              has_many :slackers, :class_name => "Developer", :conditions => {:slacker => true}
         
     | 
| 
      
 5 
     | 
    
         
            +
              has_many :notes, :as => :notable
         
     | 
| 
      
 6 
     | 
    
         
            +
              has_many :data_types
         
     | 
| 
      
 7 
     | 
    
         
            +
              metasearch_exclude_attr :updated_at
         
     | 
| 
      
 8 
     | 
    
         
            +
              metasearch_exclude_assoc :notes
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <% 1.upto(9) do |n| %>
         
     | 
| 
      
 2 
     | 
    
         
            +
            dt_<%= n %>:
         
     | 
| 
      
 3 
     | 
    
         
            +
              company_id: <%= n % 3 + 1 %>
         
     | 
| 
      
 4 
     | 
    
         
            +
              str       : This string has <%= n %> exclamation points<%= '!' * n %>
         
     | 
| 
      
 5 
     | 
    
         
            +
              txt       : <%= 'This is some text that may or may not repeat based on the value of n.' * n %>
         
     | 
| 
      
 6 
     | 
    
         
            +
              int       : <%= n ** 3 %>
         
     | 
| 
      
 7 
     | 
    
         
            +
              flt       : <%= n.to_f / 2.0 %>
         
     | 
| 
      
 8 
     | 
    
         
            +
              dec       : <%= n.to_f ** (n + 0.1) %>
         
     | 
| 
      
 9 
     | 
    
         
            +
              dtm       : <%= Time.utc(2009, 12, 24) + 86400 * n %>
         
     | 
| 
      
 10 
     | 
    
         
            +
              tms       : <%= Time.utc(2009, 12, 24) + 86400 * n %>
         
     | 
| 
      
 11 
     | 
    
         
            +
              tim       : <%= Time.utc(2000, 01, 01, n+8, n) %>
         
     | 
| 
      
 12 
     | 
    
         
            +
              dat       : <%= (Date.new(2009, 12, 24) + n).strftime("%Y-%m-%d") %>
         
     | 
| 
      
 13 
     | 
    
         
            +
              bin       : <%= "BLOB#{n}" * n %>
         
     | 
| 
      
 14 
     | 
    
         
            +
              bln       : <%= n % 2 > 0 ? true : false %>
         
     | 
| 
      
 15 
     | 
    
         
            +
            <% end %>
         
     | 
| 
         @@ -0,0 +1,55 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            peter:
         
     | 
| 
      
 2 
     | 
    
         
            +
              id        : 1
         
     | 
| 
      
 3 
     | 
    
         
            +
              company_id: 1
         
     | 
| 
      
 4 
     | 
    
         
            +
              name      : Peter Gibbons
         
     | 
| 
      
 5 
     | 
    
         
            +
              salary    : 100000
         
     | 
| 
      
 6 
     | 
    
         
            +
              slacker   : true
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            michael:
         
     | 
| 
      
 9 
     | 
    
         
            +
              id        : 2
         
     | 
| 
      
 10 
     | 
    
         
            +
              company_id: 1
         
     | 
| 
      
 11 
     | 
    
         
            +
              name      : Michael Bolton
         
     | 
| 
      
 12 
     | 
    
         
            +
              salary    : 70000
         
     | 
| 
      
 13 
     | 
    
         
            +
              slacker   : false
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            samir:
         
     | 
| 
      
 16 
     | 
    
         
            +
              id        : 3
         
     | 
| 
      
 17 
     | 
    
         
            +
              company_id: 1
         
     | 
| 
      
 18 
     | 
    
         
            +
              name      : Samir Nagheenanajar
         
     | 
| 
      
 19 
     | 
    
         
            +
              salary    : 65000
         
     | 
| 
      
 20 
     | 
    
         
            +
              slacker   : false
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            herb:
         
     | 
| 
      
 23 
     | 
    
         
            +
              id        : 4
         
     | 
| 
      
 24 
     | 
    
         
            +
              company_id: 2
         
     | 
| 
      
 25 
     | 
    
         
            +
              name      : Herb Myers
         
     | 
| 
      
 26 
     | 
    
         
            +
              salary    : 50000
         
     | 
| 
      
 27 
     | 
    
         
            +
              slacker   : false
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            dude:
         
     | 
| 
      
 30 
     | 
    
         
            +
              id        : 5
         
     | 
| 
      
 31 
     | 
    
         
            +
              company_id: 2
         
     | 
| 
      
 32 
     | 
    
         
            +
              name      : Some Dude
         
     | 
| 
      
 33 
     | 
    
         
            +
              salary    : 84000
         
     | 
| 
      
 34 
     | 
    
         
            +
              slacker   : true
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            ernie:
         
     | 
| 
      
 37 
     | 
    
         
            +
              id        : 6
         
     | 
| 
      
 38 
     | 
    
         
            +
              company_id: 3
         
     | 
| 
      
 39 
     | 
    
         
            +
              name      : Ernie Miller
         
     | 
| 
      
 40 
     | 
    
         
            +
              salary    : 45000
         
     | 
| 
      
 41 
     | 
    
         
            +
              slacker   : true
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            someone:
         
     | 
| 
      
 44 
     | 
    
         
            +
              id        : 7
         
     | 
| 
      
 45 
     | 
    
         
            +
              company_id: 3
         
     | 
| 
      
 46 
     | 
    
         
            +
              name      : Someone Else
         
     | 
| 
      
 47 
     | 
    
         
            +
              salary    : 70000
         
     | 
| 
      
 48 
     | 
    
         
            +
              slacker   : true
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            another:
         
     | 
| 
      
 51 
     | 
    
         
            +
              id        : 8
         
     | 
| 
      
 52 
     | 
    
         
            +
              company_id: 3
         
     | 
| 
      
 53 
     | 
    
         
            +
              name      : Another Guy
         
     | 
| 
      
 54 
     | 
    
         
            +
              salary    : 80000
         
     | 
| 
      
 55 
     | 
    
         
            +
              slacker   : false
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <% 1.upto(3) do |d| %>
         
     | 
| 
      
 2 
     | 
    
         
            +
            y2k_<%= d %>:
         
     | 
| 
      
 3 
     | 
    
         
            +
              developer_id: <%= d %>
         
     | 
| 
      
 4 
     | 
    
         
            +
              project_id  : 1
         
     | 
| 
      
 5 
     | 
    
         
            +
            <% end %>
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            virus:
         
     | 
| 
      
 8 
     | 
    
         
            +
              developer_id: 2
         
     | 
| 
      
 9 
     | 
    
         
            +
              project_id  : 2
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
            <% 1.upto(8) do |d| %>
         
     | 
| 
      
 12 
     | 
    
         
            +
            awesome_<%= d %>:
         
     | 
| 
      
 13 
     | 
    
         
            +
              developer_id: <%= d %>
         
     | 
| 
      
 14 
     | 
    
         
            +
              project_id  : 3
         
     | 
| 
      
 15 
     | 
    
         
            +
            <% end %>
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            metasearch:
         
     | 
| 
      
 18 
     | 
    
         
            +
              developer_id: 6
         
     | 
| 
      
 19 
     | 
    
         
            +
              project_id  : 4
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            <% 4.upto(8) do |d| %>
         
     | 
| 
      
 22 
     | 
    
         
            +
            another_<%= d %>:
         
     | 
| 
      
 23 
     | 
    
         
            +
              developer_id: <%= d %>
         
     | 
| 
      
 24 
     | 
    
         
            +
              project_id  : 5
         
     | 
| 
      
 25 
     | 
    
         
            +
            <% end %>
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            peter:
         
     | 
| 
      
 2 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 3 
     | 
    
         
            +
              notable_id  : 1
         
     | 
| 
      
 4 
     | 
    
         
            +
              note        : A straight shooter with upper management written all over him.
         
     | 
| 
      
 5 
     | 
    
         
            +
              
         
     | 
| 
      
 6 
     | 
    
         
            +
            michael:
         
     | 
| 
      
 7 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 8 
     | 
    
         
            +
              notable_id  : 2
         
     | 
| 
      
 9 
     | 
    
         
            +
              note        : Doesn't like the singer of the same name. The nerve!
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            samir:
         
     | 
| 
      
 12 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 13 
     | 
    
         
            +
              notable_id  : 3
         
     | 
| 
      
 14 
     | 
    
         
            +
              note        : Naga.... Naga..... Not gonna work here anymore anyway.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            herb:
         
     | 
| 
      
 17 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 18 
     | 
    
         
            +
              notable_id  : 4
         
     | 
| 
      
 19 
     | 
    
         
            +
              note        : Will show you what he's doing.
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            dude:
         
     | 
| 
      
 22 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 23 
     | 
    
         
            +
              notable_id  : 5
         
     | 
| 
      
 24 
     | 
    
         
            +
              note        : Nothing of note.
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ernie:
         
     | 
| 
      
 27 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 28 
     | 
    
         
            +
              notable_id  : 6
         
     | 
| 
      
 29 
     | 
    
         
            +
              note        : Complete slacker. Should probably be fired.
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
            someone:
         
     | 
| 
      
 32 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 33 
     | 
    
         
            +
              notable_id  : 7
         
     | 
| 
      
 34 
     | 
    
         
            +
              note        : Just another developer.
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            another:
         
     | 
| 
      
 37 
     | 
    
         
            +
              notable_type: Developer
         
     | 
| 
      
 38 
     | 
    
         
            +
              notable_id  : 8
         
     | 
| 
      
 39 
     | 
    
         
            +
              note        : Placing a note in this guy's file for insubordination.
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            initech:
         
     | 
| 
      
 42 
     | 
    
         
            +
              notable_type: Company
         
     | 
| 
      
 43 
     | 
    
         
            +
              notable_id  : 1
         
     | 
| 
      
 44 
     | 
    
         
            +
              note        : Innovation + Technology!
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            aos:
         
     | 
| 
      
 47 
     | 
    
         
            +
              notable_type: Company
         
     | 
| 
      
 48 
     | 
    
         
            +
              notable_id  : 2
         
     | 
| 
      
 49 
     | 
    
         
            +
              note        : Advanced solutions of an optical nature.
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            mission_data:
         
     | 
| 
      
 52 
     | 
    
         
            +
              notable_type: Company
         
     | 
| 
      
 53 
     | 
    
         
            +
              notable_id  : 3
         
     | 
| 
      
 54 
     | 
    
         
            +
              note        : Best design + development shop in the 'ville.
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            y2k:
         
     | 
| 
      
 57 
     | 
    
         
            +
              notable_type: Project
         
     | 
| 
      
 58 
     | 
    
         
            +
              notable_id  : 1
         
     | 
| 
      
 59 
     | 
    
         
            +
              note        : It may have already passed but that's no excuse to be unprepared!
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            virus:
         
     | 
| 
      
 62 
     | 
    
         
            +
              notable_type: Project
         
     | 
| 
      
 63 
     | 
    
         
            +
              notable_id  : 2
         
     | 
| 
      
 64 
     | 
    
         
            +
              note        : It could bring the company to its knees.
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            awesome:
         
     | 
| 
      
 67 
     | 
    
         
            +
              notable_type: Project
         
     | 
| 
      
 68 
     | 
    
         
            +
              notable_id  : 3
         
     | 
| 
      
 69 
     | 
    
         
            +
              note        : This note is AWESOME!!!
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            metasearch:
         
     | 
| 
      
 72 
     | 
    
         
            +
              notable_type: Project
         
     | 
| 
      
 73 
     | 
    
         
            +
              notable_id  : 4
         
     | 
| 
      
 74 
     | 
    
         
            +
              note        : A complete waste of the developer's time.
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
            another:
         
     | 
| 
      
 77 
     | 
    
         
            +
              notable_type: Project
         
     | 
| 
      
 78 
     | 
    
         
            +
              notable_id  : 5
         
     | 
| 
      
 79 
     | 
    
         
            +
              note        : This is another project note.
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            y2k:
         
     | 
| 
      
 2 
     | 
    
         
            +
              estimated_hours: 1000
         
     | 
| 
      
 3 
     | 
    
         
            +
              name           : Y2K Software Updates
         
     | 
| 
      
 4 
     | 
    
         
            +
              id             : 1
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            virus:
         
     | 
| 
      
 7 
     | 
    
         
            +
              estimated_hours: 80
         
     | 
| 
      
 8 
     | 
    
         
            +
              name           : Virus
         
     | 
| 
      
 9 
     | 
    
         
            +
              id             : 2
         
     | 
| 
      
 10 
     | 
    
         
            +
              
         
     | 
| 
      
 11 
     | 
    
         
            +
            awesome:
         
     | 
| 
      
 12 
     | 
    
         
            +
              estimated_hours: 100
         
     | 
| 
      
 13 
     | 
    
         
            +
              name           : Do something awesome
         
     | 
| 
      
 14 
     | 
    
         
            +
              id             : 3
         
     | 
| 
      
 15 
     | 
    
         
            +
              
         
     | 
| 
      
 16 
     | 
    
         
            +
            metasearch:
         
     | 
| 
      
 17 
     | 
    
         
            +
              estimated_hours: 100
         
     | 
| 
      
 18 
     | 
    
         
            +
              name           : MetaSearch Development
         
     | 
| 
      
 19 
     | 
    
         
            +
              id             : 4
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
            another:
         
     | 
| 
      
 22 
     | 
    
         
            +
              estimated_hours: 120
         
     | 
| 
      
 23 
     | 
    
         
            +
              name           : Another Project
         
     | 
| 
      
 24 
     | 
    
         
            +
              id             : 5
         
     |