meta_search_hub 1.1.3

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.
@@ -0,0 +1,264 @@
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_ (aliases: _ne_, _noteq_) - 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_ (aliases: _ni_, _notin_) - Like above, but negated.
18
+ # * _is_null_ - The column has an SQL NULL value.
19
+ # * _is_not_null_ - The column contains anything but NULL.
20
+ #
21
+ # === Strings
22
+ #
23
+ # * _contains_ (aliases: _like_, _matches_) - Substring match.
24
+ # * _does_not_contain_ (aliases: _nlike_, _nmatches_) - Negative substring match.
25
+ # * _starts_with_ (alias: _sw_) - Match strings beginning with the entered term.
26
+ # * _does_not_start_with_ (alias: _dnsw_) - The opposite of above.
27
+ # * _ends_with_ (alias: _ew_) - Match strings ending with the entered term.
28
+ # * _does_not_end_with_ (alias: _dnew_) - Negative of above.
29
+ #
30
+ # === Numbers, dates, and times
31
+ #
32
+ # * _greater_than_ (alias: _gt_) - Greater than.
33
+ # * _greater_than_or_equal_to_ (aliases: _gte_, _gteq_) - Greater than or equal to.
34
+ # * _less_than_ (alias: _lt_) - Less than.
35
+ # * _less_than_or_equal_to_ (aliases: _lte_, _lteq_) - Less than or equal to.
36
+ #
37
+ # === Booleans
38
+ #
39
+ # * _is_true_ - Is true. Useful for a checkbox like "only show admin users".
40
+ # * _is_false_ - The complement of _is_true_.
41
+ #
42
+ # === Non-boolean data types
43
+ #
44
+ # * _is_present_ - As with _is_true_, useful with a checkbox. Not NULL or the empty string.
45
+ # * _is_blank_ - Returns records with a value of NULL or the empty string in the column.
46
+ #
47
+ # So, given a model like this...
48
+ #
49
+ # class Article < ActiveRecord::Base
50
+ # belongs_to :author
51
+ # has_many :comments
52
+ # has_many :moderations, :through => :comments
53
+ # end
54
+ #
55
+ # ...you might end up with attributes like <tt>title_contains</tt>,
56
+ # <tt>comments_title_starts_with</tt>, <tt>moderations_value_less_than</tt>,
57
+ # <tt>author_name_equals</tt>, and so on.
58
+ #
59
+ # Additionally, all of the above predicate types also have an _any and _all version, which
60
+ # expects an array of the corresponding parameter type, and requires any or all of the
61
+ # parameters to be a match, respectively. So:
62
+ #
63
+ # Article.search :author_name_starts_with_any => ['Jim', 'Bob', 'Fred']
64
+ #
65
+ # will match articles authored by Jimmy, Bobby, or Freddy, but not Winifred.
66
+ class Where
67
+ attr_reader :name, :aliases, :types, :cast, :predicate, :formatter, :validator
68
+ def initialize(where)
69
+ if [String,Symbol].include?(where.class)
70
+ where = Where.get(where) or raise ArgumentError("A where could not be instantiated for the argument #{where}")
71
+ end
72
+ @name = where[:name]
73
+ @aliases = where[:aliases]
74
+ @types = where[:types]
75
+ @cast = where[:cast]
76
+ @predicate = where[:predicate]
77
+ @validator = where[:validator]
78
+ @formatter = where[:formatter]
79
+ @splat_param = where[:splat_param]
80
+ @skip_compounds = where[:skip_compounds]
81
+ end
82
+
83
+ def splat_param?
84
+ !!@splat_param
85
+ end
86
+
87
+ def skip_compounds?
88
+ !!@skip_compounds
89
+ end
90
+
91
+ # Format a parameter for searching using the Where's defined formatter.
92
+ def format_param(param)
93
+ formatter.call(param)
94
+ end
95
+
96
+ # Validate the parameter for use in a search using the Where's defined validator.
97
+ def validate(param)
98
+ validator.call(param)
99
+ end
100
+
101
+ # Evaluate the Where for the given relation, attribute, and parameter(s)
102
+ def evaluate(relation, attributes, param)
103
+ if splat_param?
104
+ conditions = attributes.map {|a| a.send(predicate, *format_param(param))}
105
+ else
106
+ conditions = attributes.map {|a| a.send(predicate, format_param(param))}
107
+ end
108
+
109
+ relation.where(conditions.inject(nil) {|memo, c| memo ? memo.or(c) : c})
110
+ end
111
+
112
+ class << self
113
+ # At application initialization, you can add additional custom Wheres to the mix.
114
+ # in your application's <tt>config/initializers/meta_search.rb</tt>, place lines
115
+ # like this:
116
+ #
117
+ # MetaSearch::Where.add :between, :btw,
118
+ # :predicate => :in,
119
+ # :types => [:integer, :float, :decimal, :date, :datetime, :timestamp, :time],
120
+ # :formatter => Proc.new {|param| Range.new(param.first, param.last)},
121
+ # :validator => Proc.new {|param|
122
+ # param.is_a?(Array) && !(param[0].blank? || param[1].blank?)
123
+ # }
124
+ #
125
+ # The first options are all names for the where. Well, the first is a name, the rest
126
+ # are aliases, really. They will determine the suffix you will use to access your Where.
127
+ #
128
+ # <tt>types</tt> is an array of types the comparison is valid for. The where will not
129
+ # be available against columns that are not one of these types. Default is +ALL_TYPES+,
130
+ # Which is one of several MetaSearch constants available for type assignment (the others
131
+ # being +DATES+, +TIIMES+, +STRINGS+, and +NUMBERS+).
132
+ #
133
+ # <tt>predicate</tt> is the Arel::Attribute predication (read: conditional operator) used
134
+ # for the comparison. Default is :eq, or equality.
135
+ #
136
+ # <tt>formatter</tt> is the Proc that will do any formatting to the variables to be substituted.
137
+ # The default proc is <tt>{|param| param}</tt>, which doesn't really do anything. If you pass a
138
+ # string, it will be +eval+ed in the context of this Proc.
139
+ #
140
+ # For example, this is the definition of the "contains" Where:
141
+ #
142
+ # ['contains', 'like', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}%"'}]
143
+ #
144
+ # Be sure to single-quote the string, so that variables aren't interpolated until later. If in doubt,
145
+ # just use a Proc.
146
+ #
147
+ # <tt>validator</tt> is the Proc that will be used to check whether a parameter supplied to the
148
+ # Where is valid. If it is not valid, it won't be used in the query. The default is
149
+ # <tt>{|param| !param.blank?}</tt>, so that empty parameters aren't added to the search, but you
150
+ # can get more complex if you desire, like the one in the between example, above.
151
+ #
152
+ # <tt>splat_param</tt>, if true, will cause the parameters sent to the predicate in question
153
+ # to be splatted (converted to an argument list). This is not normally useful and defaults to
154
+ # false, but is used when automatically creating compound Wheres (*_any, *_all) so that the
155
+ # Arel attribute method gets the correct parameter list.
156
+ #
157
+ # <tt>skip_compounds</tt> will prevent creation of compound condition methods (ending in
158
+ # _any_ or _all_) for situations where they wouldn't make sense, such as the built-in
159
+ # conditions <tt>is_true</tt> and <tt>is_false</tt>.
160
+ #
161
+ # <tt>cast</tt> will override the normal cast of the parameter when using this Where
162
+ # condition. Normally, the value supplied to a condition is cast to the type of the column
163
+ # it's being compared against. In cases where this isn't desirable, because the value you
164
+ # intend to accept isn't the same kind of data you'll be comparing against, you can override
165
+ # that cast here, using one of the standard DB type symbols such as :integer, :string, :boolean
166
+ # and so on.
167
+ def add(*args)
168
+ where = create_where_from_args(*args)
169
+ create_where_compounds_for(where) unless where.skip_compounds?
170
+ end
171
+
172
+ # Returns the complete array of Wheres
173
+ def all
174
+ @@wheres
175
+ end
176
+
177
+ # Get the where matching a method or predicate.
178
+ def get(method_id_or_predicate)
179
+ return nil unless where_key = @@wheres.keys.
180
+ sort {|a,b| b.length <=> a.length}.
181
+ detect {|n| method_id_or_predicate.to_s.match(/#{n}=?$/)}
182
+ where = @@wheres[where_key]
183
+ where = @@wheres[where] if where.is_a?(String)
184
+ where
185
+ end
186
+
187
+ # Set the wheres to their default values, removing any customized settings.
188
+ def initialize_wheres
189
+ @@wheres = {}
190
+ DEFAULT_WHERES.each do |where|
191
+ add(*where)
192
+ end
193
+ end
194
+
195
+ private
196
+
197
+ # "Creates" the Where by adding it (and its aliases) to the current hash of wheres. It then
198
+ # instantiates a Where and returns it for use.
199
+ def create_where_from_args(*args)
200
+ opts = args.last.is_a?(Hash) ? args.pop : {}
201
+ args = args.compact.flatten.map {|a| a.to_s }
202
+ raise ArgumentError, "Name parameter required" if args.blank?
203
+ opts[:name] ||= args.first
204
+ opts[:types] ||= ALL_TYPES
205
+ opts[:types] = [opts[:types]].flatten
206
+ opts[:cast] = opts[:cast]
207
+ opts[:predicate] ||= :eq
208
+ opts[:splat_param] ||= false
209
+ opts[:skip_compounds] ||= false
210
+ opts[:formatter] ||= Proc.new {|param| param}
211
+ if opts[:formatter].is_a?(String)
212
+ formatter = opts[:formatter]
213
+ opts[:formatter] = Proc.new {|param| eval formatter}
214
+ end
215
+ unless opts[:formatter].respond_to?(:call)
216
+ raise ArgumentError, "Invalid formatter for #{opts[:name]}, should be a Proc or String."
217
+ end
218
+ opts[:validator] ||= Proc.new {|param| !param.blank?}
219
+ unless opts[:validator].respond_to?(:call)
220
+ raise ArgumentError, "Invalid validator for #{opts[:name]}, should be a Proc."
221
+ end
222
+ opts[:aliases] ||= [args - [opts[:name]]].flatten
223
+ @@wheres ||= {}
224
+ if @@wheres.has_key?(opts[:name])
225
+ raise ArgumentError, "\"#{opts[:name]}\" is not available for use as a where name."
226
+ end
227
+ @@wheres[opts[:name]] = opts
228
+ opts[:aliases].each do |a|
229
+ if @@wheres.has_key?(a)
230
+ opts[:aliases].delete(a)
231
+ else
232
+ @@wheres[a] = opts[:name]
233
+ end
234
+ end
235
+ new(opts[:name])
236
+ end
237
+
238
+ # Takes the provided +where+ param and derives two additional Wheres from it, with the
239
+ # name appended by _any/_all. These will use Arel's grouped predicate methods (matching
240
+ # the same naming convention) to be invoked instead, with a list of possible/required
241
+ # matches.
242
+ def create_where_compounds_for(where)
243
+ ['any', 'all'].each do |compound|
244
+ args = [where.name, *where.aliases].map {|n| "#{n}_#{compound}"}
245
+ create_where_from_args(*args + [{
246
+ :types => where.types,
247
+ :predicate => "#{where.predicate}_#{compound}".to_sym,
248
+ # Only use valid elements in the array
249
+ :formatter => Proc.new {|param|
250
+ param.select {|p| where.validator.call(p)}.map {|p| where.formatter.call(p)}
251
+ },
252
+ # Compound where is valid if it has at least one element which is valid
253
+ :validator => Proc.new {|param|
254
+ param.is_a?(Array) &&
255
+ !param.select {|p| where.validator.call(p)}.blank?}
256
+ }]
257
+ )
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ Where.initialize_wheres
264
+ end
@@ -0,0 +1,58 @@
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
+ # Change this only if you know what you're doing. It's here for your protection.
10
+ MAX_JOIN_DEPTH = 5
11
+
12
+ DEFAULT_WHERES = [
13
+ ['equals', 'eq', {:validator => Proc.new {|param| !param.blank? || (param == false)}}],
14
+ ['does_not_equal', 'ne', 'not_eq', {:types => ALL_TYPES, :predicate => :not_eq}],
15
+ ['contains', 'like', 'matches', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}%"'}],
16
+ ['does_not_contain', 'nlike', 'not_matches', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}%"'}],
17
+ ['starts_with', 'sw', {:types => STRINGS, :predicate => :matches, :formatter => '"#{param}%"'}],
18
+ ['does_not_start_with', 'dnsw', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"#{param}%"'}],
19
+ ['ends_with', 'ew', {:types => STRINGS, :predicate => :matches, :formatter => '"%#{param}"'}],
20
+ ['does_not_end_with', 'dnew', {:types => STRINGS, :predicate => :does_not_match, :formatter => '"%#{param}"'}],
21
+ ['greater_than', 'gt', {:types => (NUMBERS + DATES + TIMES), :predicate => :gt}],
22
+ ['less_than', 'lt', {:types => (NUMBERS + DATES + TIMES), :predicate => :lt}],
23
+ ['greater_than_or_equal_to', 'gte', 'gteq', {:types => (NUMBERS + DATES + TIMES), :predicate => :gteq}],
24
+ ['less_than_or_equal_to', 'lte', 'lteq', {:types => (NUMBERS + DATES + TIMES), :predicate => :lteq}],
25
+ ['in', {:types => ALL_TYPES, :predicate => :in}],
26
+ ['not_in', 'ni', 'not_in', {:types => ALL_TYPES, :predicate => :not_in}],
27
+ ['is_true', {:types => BOOLEANS, :skip_compounds => true}],
28
+ ['is_false', {:types => BOOLEANS, :skip_compounds => true, :formatter => Proc.new {|param| !param}}],
29
+ ['is_present', {:types => (ALL_TYPES - BOOLEANS), :predicate => :not_eq_all, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| [nil, '']}}],
30
+ ['is_blank', {:types => (ALL_TYPES - BOOLEANS), :predicate => :eq_any, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| [nil, '']}}],
31
+ ['is_null', {:types => ALL_TYPES, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| nil}}],
32
+ ['is_not_null', {:types => ALL_TYPES, :predicate => :not_eq, :skip_compounds => true, :cast => :boolean, :formatter => Proc.new {|param| nil}}]
33
+ ]
34
+
35
+ RELATION_METHODS = [
36
+ # Query construction
37
+ :joins, :includes, :select, :order, :where, :having, :group,
38
+ # Results, debug, array methods
39
+ :to_a, :all, :length, :size, :to_sql, :debug_sql, :paginate, :page,
40
+ :find_each, :first, :last, :each, :arel, :in_groups_of, :group_by,
41
+ # Calculations
42
+ :count, :average, :minimum, :maximum, :sum
43
+ ]
44
+ end
45
+
46
+ require 'active_record'
47
+ require 'active_support'
48
+ require 'action_view'
49
+ require 'action_controller'
50
+ require 'meta_search/searches/active_record'
51
+ require 'meta_search/helpers'
52
+
53
+ I18n.load_path += Dir[File.join(File.dirname(__FILE__), 'meta_search', 'locale', '*.yml')]
54
+
55
+ ActiveRecord::Base.send(:include, MetaSearch::Searches::ActiveRecord)
56
+ ActionView::Helpers::FormBuilder.send(:include, MetaSearch::Helpers::FormBuilder)
57
+ ActionController::Base.helper(MetaSearch::Helpers::UrlHelper)
58
+ ActionController::Base.helper(MetaSearch::Helpers::FormHelper)
@@ -0,0 +1,90 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'meta_search_hub'
8
+ s.version = "1.1.3"
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 = "2012-02-02"
13
+ s.description = "\n Allows simple search forms to be created against an AR3 model\n and its associations, has useful view helpers for sort links\n and multiparameter fields as well.\n "
14
+ s.email = "ernie@metautonomo.us"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitmodules",
22
+ "CHANGELOG",
23
+ "Gemfile",
24
+ "LICENSE",
25
+ "README.rdoc",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/meta_search.rb",
29
+ "lib/meta_search/builder.rb",
30
+ "lib/meta_search/exceptions.rb",
31
+ "lib/meta_search/helpers.rb",
32
+ "lib/meta_search/helpers/form_builder.rb",
33
+ "lib/meta_search/helpers/form_helper.rb",
34
+ "lib/meta_search/helpers/url_helper.rb",
35
+ "lib/meta_search/locale/en.yml",
36
+ "lib/meta_search/method.rb",
37
+ "lib/meta_search/model_compatibility.rb",
38
+ "lib/meta_search/searches/active_record.rb",
39
+ "lib/meta_search/utility.rb",
40
+ "lib/meta_search/where.rb",
41
+ "meta_search.gemspec",
42
+ "test/fixtures/companies.yml",
43
+ "test/fixtures/company.rb",
44
+ "test/fixtures/data_type.rb",
45
+ "test/fixtures/data_types.yml",
46
+ "test/fixtures/developer.rb",
47
+ "test/fixtures/developers.yml",
48
+ "test/fixtures/developers_projects.yml",
49
+ "test/fixtures/note.rb",
50
+ "test/fixtures/notes.yml",
51
+ "test/fixtures/project.rb",
52
+ "test/fixtures/projects.yml",
53
+ "test/fixtures/schema.rb",
54
+ "test/helper.rb",
55
+ "test/locales/es.yml",
56
+ "test/locales/flanders.yml",
57
+ "test/test_search.rb",
58
+ "test/test_view_helpers.rb"
59
+ ]
60
+ s.homepage = "http://metautonomo.us/projects/metasearch/"
61
+ s.post_install_message = "\n*** Thanks for installing MetaSearch! ***\nBe sure to check out http://metautonomo.us/projects/metasearch/ for a\nwalkthrough of MetaSearch's features, and click the donate button if\nyou're feeling especially appreciative. It'd help me justify this\n\"open source\" stuff to my lovely wife. :)\n\n"
62
+ s.require_paths = ["lib"]
63
+ s.rubygems_version = "1.8.15"
64
+ s.summary = "Object-based searching (and more) for simply creating search forms."
65
+
66
+ if s.respond_to? :specification_version then
67
+ s.specification_version = 3
68
+
69
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
70
+ s.add_runtime_dependency(%q<activerecord>, ["~> 3.1"])
71
+ s.add_runtime_dependency(%q<activesupport>, ["~> 3.1"])
72
+ s.add_runtime_dependency(%q<polyamorous>, ["~> 0.5.0"])
73
+ s.add_runtime_dependency(%q<actionpack>, ["~> 3.1"])
74
+ s.add_development_dependency(%q<shoulda>, ["~> 2.11"])
75
+ else
76
+ s.add_dependency(%q<activerecord>, ["~> 3.1"])
77
+ s.add_dependency(%q<activesupport>, ["~> 3.1"])
78
+ s.add_dependency(%q<polyamorous>, ["~> 0.5.0"])
79
+ s.add_dependency(%q<actionpack>, ["~> 3.1"])
80
+ s.add_dependency(%q<shoulda>, ["~> 2.11"])
81
+ end
82
+ else
83
+ s.add_dependency(%q<activerecord>, ["~> 3.1"])
84
+ s.add_dependency(%q<activesupport>, ["~> 3.1"])
85
+ s.add_dependency(%q<polyamorous>, ["~> 0.5.0"])
86
+ s.add_dependency(%q<actionpack>, ["~> 3.1"])
87
+ s.add_dependency(%q<shoulda>, ["~> 2.11"])
88
+ end
89
+ end
90
+
@@ -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,22 @@
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
+
8
+ scope :backwards_name, lambda {|name| where(:name => name.reverse)}
9
+ scope :with_slackers_by_name_and_salary_range,
10
+ lambda {|name, low, high|
11
+ joins(:slackers).where(:developers => {:name => name, :salary => low..high})
12
+ }
13
+ search_methods :backwards_name, :backwards_name_as_string, :if => proc {|s| s.options[:user] != 'blocked'}
14
+ search_methods :with_slackers_by_name_and_salary_range,
15
+ :splat_param => true, :type => [:string, :integer, :integer]
16
+ attr_unsearchable :updated_at, :if => proc {|s| s.options[:user] == 'blocked' || !s.options[:user]}
17
+ assoc_unsearchable :notes, :if => proc {|s| s.options[:user] == 'blocked' || !s.options[:user]}
18
+
19
+ def self.backwards_name_as_string(name)
20
+ name.reverse
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ class DataType < ActiveRecord::Base
2
+ belongs_to :company
3
+ attr_unsearchable :str
4
+ attr_protected :str
5
+ 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.local(2009, 12, 24) + 86400 * n).in_time_zone.to_s(:db) %>
10
+ tms : <%= (Time.local(2009, 12, 24) + 86400 * n).in_time_zone.to_s(:db) %>
11
+ tim : <%= Time.local(2000, 01, 01, n+8, n).in_time_zone.to_s(:db) %>
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,11 @@
1
+ class Developer < ActiveRecord::Base
2
+ belongs_to :company
3
+ has_and_belongs_to_many :projects
4
+ has_many :notes, :as => :notable
5
+
6
+ attr_searchable :name, :salary, :if => proc {|s| !s.options[:user] || s.options[:user] == 'privileged'}
7
+ assoc_searchable :notes, :projects, :company, :if => proc {|s| !s.options[:user] || s.options[:user] == 'privileged'}
8
+
9
+ scope :sort_by_salary_and_name_asc, order('salary ASC, name ASC')
10
+ scope :sort_by_salary_and_name_desc, order('salary DESC, name DESC')
11
+ end
@@ -0,0 +1,62 @@
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
56
+
57
+ forgetful:
58
+ id : 9
59
+ company_id: 3
60
+ name : Forgetful Notetaker
61
+ salary : 40000
62
+ 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,3 @@
1
+ class Note < ActiveRecord::Base
2
+ belongs_to :notable, :polymorphic => true
3
+ 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,4 @@
1
+ class Project < ActiveRecord::Base
2
+ has_and_belongs_to_many :developers
3
+ has_many :notes, :as => :notable
4
+ end