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
|