ssickles-tire 0.4.2 → 0.4.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. data/lib/tire.rb +3 -18
  2. data/lib/tire/alias.rb +35 -11
  3. data/lib/tire/index.rb +76 -34
  4. data/lib/tire/results/collection.rb +13 -38
  5. data/lib/tire/results/item.rb +0 -19
  6. data/lib/tire/rubyext/to_json.rb +21 -0
  7. data/lib/tire/search.rb +8 -7
  8. data/lib/tire/search/scan.rb +8 -8
  9. data/lib/tire/search/sort.rb +1 -1
  10. data/lib/tire/version.rb +38 -7
  11. data/test/integration/reindex_test.rb +2 -2
  12. data/test/integration/scan_test.rb +1 -1
  13. data/test/test_helper.rb +3 -27
  14. data/test/unit/index_alias_test.rb +17 -3
  15. data/test/unit/index_test.rb +18 -30
  16. data/test/unit/results_collection_test.rb +0 -60
  17. data/test/unit/results_item_test.rb +0 -37
  18. data/test/unit/rubyext_test.rb +3 -3
  19. data/test/unit/search_test.rb +6 -1
  20. data/test/unit/tire_test.rb +0 -15
  21. data/tire.gemspec +13 -30
  22. metadata +41 -153
  23. data/lib/tire/model/callbacks.rb +0 -40
  24. data/lib/tire/model/import.rb +0 -26
  25. data/lib/tire/model/indexing.rb +0 -128
  26. data/lib/tire/model/naming.rb +0 -100
  27. data/lib/tire/model/percolate.rb +0 -99
  28. data/lib/tire/model/persistence.rb +0 -72
  29. data/lib/tire/model/persistence/attributes.rb +0 -143
  30. data/lib/tire/model/persistence/finders.rb +0 -66
  31. data/lib/tire/model/persistence/storage.rb +0 -71
  32. data/lib/tire/model/search.rb +0 -305
  33. data/lib/tire/rubyext/hash.rb +0 -8
  34. data/lib/tire/rubyext/ruby_1_8.rb +0 -54
  35. data/lib/tire/rubyext/symbol.rb +0 -11
  36. data/lib/tire/utils.rb +0 -17
  37. data/test/integration/active_model_indexing_test.rb +0 -51
  38. data/test/integration/active_model_searchable_test.rb +0 -114
  39. data/test/integration/active_record_searchable_test.rb +0 -446
  40. data/test/integration/mongoid_searchable_test.rb +0 -309
  41. data/test/integration/persistent_model_test.rb +0 -117
  42. data/test/models/active_model_article.rb +0 -31
  43. data/test/models/active_model_article_with_callbacks.rb +0 -49
  44. data/test/models/active_model_article_with_custom_document_type.rb +0 -7
  45. data/test/models/active_model_article_with_custom_index_name.rb +0 -7
  46. data/test/models/active_record_models.rb +0 -122
  47. data/test/models/mongoid_models.rb +0 -97
  48. data/test/models/persistent_article.rb +0 -11
  49. data/test/models/persistent_article_in_namespace.rb +0 -12
  50. data/test/models/persistent_article_with_casting.rb +0 -28
  51. data/test/models/persistent_article_with_defaults.rb +0 -11
  52. data/test/models/persistent_articles_with_custom_index_name.rb +0 -10
  53. data/test/models/supermodel_article.rb +0 -17
  54. data/test/models/validated_model.rb +0 -11
  55. data/test/unit/active_model_lint_test.rb +0 -17
  56. data/test/unit/model_callbacks_test.rb +0 -116
  57. data/test/unit/model_import_test.rb +0 -71
  58. data/test/unit/model_persistence_test.rb +0 -516
  59. data/test/unit/model_search_test.rb +0 -899
@@ -1,143 +0,0 @@
1
- module Tire
2
- module Model
3
-
4
- module Persistence
5
-
6
- # Provides infrastructure for declaring the model properties and accessing them.
7
- #
8
- module Attributes
9
-
10
- module ClassMethods
11
-
12
- # Define property of the model:
13
- #
14
- # class Article
15
- # include Tire::Model::Persistence
16
- #
17
- # property :title, :analyzer => 'snowball'
18
- # property :published, :type => 'date'
19
- # property :tags, :analyzer => 'keywords', :default => []
20
- # end
21
- #
22
- # You can pass mapping definition for ElasticSearch in the options Hash.
23
- #
24
- # You can define default property values.
25
- #
26
- def property(name, options = {})
27
-
28
- # Define attribute reader:
29
- define_method("#{name}") do
30
- instance_variable_get(:"@#{name}")
31
- end
32
-
33
- # Define attribute writer:
34
- define_method("#{name}=") do |value|
35
- instance_variable_set(:"@#{name}", value)
36
- end
37
-
38
- # Save the property in properties array:
39
- properties << name.to_s unless properties.include?(name.to_s)
40
-
41
- # Define convenience <NAME>? method:
42
- define_query_method name.to_sym
43
-
44
- # ActiveModel compatibility. NEEDED?
45
- define_attribute_methods [name.to_sym]
46
-
47
- # Save property default value (when relevant):
48
- unless (default_value = options.delete(:default)).nil?
49
- property_defaults[name.to_sym] = default_value
50
- end
51
-
52
- # Save property casting (when relevant):
53
- property_types[name.to_sym] = options[:class] if options[:class]
54
-
55
- # Store mapping for the property:
56
- mapping[name] = options
57
- self
58
- end
59
-
60
- def properties
61
- @properties ||= []
62
- end
63
-
64
- def property_defaults
65
- @property_defaults ||= {}
66
- end
67
-
68
- def property_types
69
- @property_types ||= {}
70
- end
71
-
72
- private
73
-
74
- def define_query_method name
75
- define_method("#{name}?") { !! send(name) }
76
- end
77
-
78
- end
79
-
80
- module InstanceMethods
81
-
82
- attr_accessor :id
83
-
84
- def initialize(attributes={})
85
- # Make a copy of objects in the property defaults hash, so default values such as `[]` or `{ foo: [] }` are left intact
86
- property_defaults = self.class.property_defaults.inject({}) do |hash, item|
87
- key, value = item
88
- hash[key.to_s] = value.class.respond_to?(:new) ? value.clone : value
89
- hash
90
- end
91
-
92
- __update_attributes(property_defaults.merge(attributes))
93
- end
94
-
95
- def attributes
96
- self.class.properties.
97
- inject( self.id ? {'id' => self.id} : {} ) {|attributes, key| attributes[key] = send(key); attributes}
98
- end
99
-
100
- def attribute_names
101
- self.class.properties.sort
102
- end
103
-
104
- def has_attribute?(name)
105
- properties.include?(name.to_s)
106
- end
107
- alias :has_property? :has_attribute?
108
-
109
- def __update_attributes(attributes)
110
- attributes.each { |name, value| send "#{name}=", __cast_value(name, value) }
111
- end
112
-
113
- # Casts the values according to the <tt>:class</tt> option set when
114
- # defining the property, cast Hashes as Hashr[http://rubygems.org/gems/hashr]
115
- # instances and automatically convert UTC formatted strings to Time.
116
- #
117
- def __cast_value(name, value)
118
- case
119
-
120
- when klass = self.class.property_types[name.to_sym]
121
- if klass.is_a?(Array) && value.is_a?(Array)
122
- value.map { |v| klass.first.new(v) }
123
- else
124
- klass.new(value)
125
- end
126
-
127
- when value.is_a?(Hash)
128
- Hashr.new(value)
129
-
130
- else
131
- # Strings formatted as <http://en.wikipedia.org/wiki/ISO8601> are automatically converted to Time
132
- value = Time.parse(value) if value.is_a?(String) && value =~ /^\d{4}[\/\-]\d{2}[\/\-]\d{2}T\d{2}\:\d{2}\:\d{2}Z$/
133
- value
134
- end
135
- end
136
-
137
- end
138
-
139
- end
140
-
141
- end
142
- end
143
- end
@@ -1,66 +0,0 @@
1
- module Tire
2
- module Model
3
-
4
- module Persistence
5
-
6
- # Provides infrastructure for an _ActiveRecord_-like interface for finding records.
7
- #
8
- module Finders
9
-
10
- module ClassMethods
11
-
12
- def find *args
13
- # TODO: Options like `sort`
14
- old_wrapper = Tire::Configuration.wrapper
15
- Tire::Configuration.wrapper self
16
- options = args.pop if args.last.is_a?(Hash)
17
- args.flatten!
18
- if args.size > 1
19
- Tire::Search::Search.new(index.name) do |search|
20
- search.query do |query|
21
- query.ids(args, document_type)
22
- end
23
- search.size args.size
24
- end.results
25
- else
26
- case args = args.pop
27
- when Fixnum, String
28
- index.retrieve document_type, args
29
- when :all, :first
30
- send(args)
31
- else
32
- raise ArgumentError, "Please pass either ID as Fixnum or String, or :all, :first as an argument"
33
- end
34
- end
35
- ensure
36
- Tire::Configuration.wrapper old_wrapper
37
- end
38
-
39
- def all
40
- # TODO: Options like `sort`; Possibly `filters`
41
- old_wrapper = Tire::Configuration.wrapper
42
- Tire::Configuration.wrapper self
43
- s = Tire::Search::Search.new(index.name).query { all }
44
- s.results
45
- ensure
46
- Tire::Configuration.wrapper old_wrapper
47
- end
48
-
49
- def first
50
- # TODO: Options like `sort`; Possibly `filters`
51
- old_wrapper = Tire::Configuration.wrapper
52
- Tire::Configuration.wrapper self
53
- s = Tire::Search::Search.new(index.name).query { all }.size(1)
54
- s.results.first
55
- ensure
56
- Tire::Configuration.wrapper old_wrapper
57
- end
58
-
59
- end
60
-
61
- end
62
-
63
- end
64
-
65
- end
66
- end
@@ -1,71 +0,0 @@
1
- module Tire
2
- module Model
3
-
4
- module Persistence
5
-
6
- # Provides infrastructure for storing records in _ElasticSearch_.
7
- #
8
- module Storage
9
-
10
- def self.included(base)
11
-
12
- base.class_eval do
13
- extend ClassMethods
14
- include InstanceMethods
15
- end
16
-
17
- end
18
-
19
- module ClassMethods
20
-
21
- def create(args={})
22
- document = new(args)
23
- return false unless document.valid?
24
- document.save
25
- document
26
- end
27
-
28
- end
29
-
30
- module InstanceMethods
31
-
32
- def update_attribute(name, value)
33
- __update_attributes name => value
34
- save
35
- end
36
-
37
- def update_attributes(attributes={})
38
- __update_attributes attributes
39
- save
40
- end
41
-
42
- def save
43
- return false unless valid?
44
- run_callbacks :save do
45
- # Document#id is set in the +update_elasticsearch_index+ method,
46
- # where we have access to the JSON response
47
- end
48
- self
49
- end
50
-
51
- def destroy
52
- run_callbacks :destroy do
53
- @destroyed = true
54
- end
55
- self.freeze
56
- end
57
-
58
- # TODO: Implement `new_record?` and clean up
59
-
60
- def destroyed?; !!@destroyed; end
61
-
62
- def persisted?; !!id; end
63
-
64
- end
65
-
66
- end
67
-
68
- end
69
-
70
- end
71
- end
@@ -1,305 +0,0 @@
1
- module Tire
2
- module Model
3
-
4
- # Main module containing the search infrastructure for ActiveModel classes.
5
- #
6
- # By including this module, you'll provide the model with facilities to
7
- # perform searches against index, define index settings and mappings,
8
- # access the index object, etc.
9
- #
10
- # All the _Tire_ methods are accessible via the "proxy" class and instance
11
- # methods of the model, named `tire`, eg. `Article.tire.search 'foo'`.
12
- #
13
- # When there's no clash with a method in the class (your own, defined by another gem, etc)
14
- # _Tire_ will bring these methods to the top-level namespace of the class,
15
- # eg. `Article.search 'foo'`.
16
- #
17
- # You'll find the relevant methods in the ClassMethods and InstanceMethods module.
18
- #
19
- #
20
- module Search
21
-
22
- # Alias for Tire::Model::Naming::ClassMethods.index_prefix
23
- #
24
- def self.index_prefix(*args)
25
- Naming::ClassMethods.index_prefix(*args)
26
- end
27
-
28
- module ClassMethods
29
-
30
- # Returns search results for a given query.
31
- #
32
- # Query can be passed simply as a String:
33
- #
34
- # Article.search 'love'
35
- #
36
- # Any options, such as pagination or sorting, can be passed as a second argument:
37
- #
38
- # Article.search 'love', :per_page => 25, :page => 2
39
- # Article.search 'love', :sort => 'title'
40
- #
41
- # For more powerful query definition, use the query DSL passed as a block:
42
- #
43
- # Article.search do
44
- # query { terms :tags, ['ruby', 'python'] }
45
- # facet 'tags' { terms :tags }
46
- # end
47
- #
48
- # You can pass options as the first argument, in this case:
49
- #
50
- # Article.search :per_page => 25, :page => 2 do
51
- # query { string 'love' }
52
- # end
53
- #
54
- # This methods returns a Tire::Results::Collection instance, containing instances
55
- # of Tire::Results::Item, populated by the data available in _ElasticSearch, by default.
56
- #
57
- # If you'd like to load the "real" models from the database, you may use the `:load` option:
58
- #
59
- # Article.search 'love', :load => true
60
- #
61
- # You can pass options as a Hash to the model's `find` method:
62
- #
63
- # Article.search :load => { :include => 'comments' } do ... end
64
- #
65
- def search(*args, &block)
66
- default_options = {:type => document_type, :index => index.name}
67
-
68
- if block_given?
69
- options = args.shift || {}
70
- else
71
- query, options = args
72
- options ||= {}
73
- end
74
-
75
- sort = Array( options[:order] || options[:sort] )
76
- options = default_options.update(options)
77
-
78
- s = Tire::Search::Search.new(options.delete(:index), options)
79
- s.size( options[:per_page].to_i ) if options[:per_page]
80
- s.from( options[:page].to_i <= 1 ? 0 : (options[:per_page].to_i * (options[:page].to_i-1)) ) if options[:page] && options[:per_page]
81
- s.sort do
82
- sort.each do |t|
83
- field_name, direction = t.split(' ')
84
- by field_name, direction
85
- end
86
- end unless sort.empty?
87
-
88
- if block_given?
89
- block.arity < 1 ? s.instance_eval(&block) : block.call(s)
90
- else
91
- s.query { string query }
92
- # TODO: Actualy, allow passing all the valid options from
93
- # <http://www.elasticsearch.org/guide/reference/api/search/uri-request.html>
94
- s.fields Array(options[:fields]) if options[:fields]
95
- end
96
-
97
- s.results
98
- end
99
-
100
- # Returns a Tire::Index instance for this model.
101
- #
102
- # Example usage: `Article.index.refresh`.
103
- #
104
- def index
105
- name = index_name.respond_to?(:to_proc) ? klass.instance_eval(&index_name) : index_name
106
- @index = Index.new(name)
107
- end
108
-
109
- end
110
-
111
- module InstanceMethods
112
-
113
- # Returns a Tire::Index instance for this instance of the model.
114
- #
115
- # Example usage: `@article.index.refresh`.
116
- #
117
- def index
118
- instance.class.tire.index
119
- end
120
-
121
- # Updates the index in _ElasticSearch_.
122
- #
123
- # On model instance create or update, it will store its serialized representation in the index.
124
- #
125
- # On model destroy, it will remove the corresponding document from the index.
126
- #
127
- # It will also execute any `<after|before>_update_elasticsearch_index` callback hooks.
128
- #
129
- def update_index
130
- #instance.send :_run_update_elasticsearch_index_callbacks do
131
- if instance.destroyed?
132
- index.remove instance
133
- else
134
- response = index.store( instance, {:percolate => percolator} )
135
- instance.id ||= response['_id'] if instance.respond_to?(:id=)
136
- instance._index = response['_index'] if instance.respond_to?(:_index=)
137
- instance._type = response['_type'] if instance.respond_to?(:_type=)
138
- instance._version = response['_version'] if instance.respond_to?(:_version=)
139
- instance.matches = response['matches'] if instance.respond_to?(:matches=)
140
- self
141
- end
142
- #end
143
- end
144
- alias :update_elasticsearch_index :update_index
145
- alias :update_elastic_search_index :update_index
146
-
147
- # The default JSON serialization of the model, based on its `#to_hash` representation.
148
- #
149
- # If you don't define any mapping, the model is serialized as-is.
150
- #
151
- # If you do define the mapping for _ElasticSearch_, only attributes
152
- # declared in the mapping are serialized.
153
- #
154
- # For properties declared with the `:as` option, the passed String or Proc
155
- # is evaluated in the instance context.
156
- #
157
- def to_indexed_json
158
- if instance.class.tire.mapping.empty?
159
- # Reject the id and type keys
160
- instance.to_hash.reject {|key,_| key.to_s == 'id' || key.to_s == 'type' }.to_json
161
- else
162
- mapping = instance.class.tire.mapping
163
- # Reject keys not declared in mapping
164
- hash = instance.to_hash.reject { |key, value| ! mapping.keys.map(&:to_s).include?(key.to_s) }
165
-
166
- # Evalute the `:as` options
167
- mapping.each do |key, options|
168
- case options[:as]
169
- when String
170
- hash[key] = instance.instance_eval(options[:as])
171
- when Proc
172
- hash[key] = instance.instance_eval(&options[:as])
173
- end
174
- end
175
-
176
- hash.to_json
177
- end
178
- end
179
-
180
- def matches
181
- @attributes['matches']
182
- end
183
-
184
- def matches=(value)
185
- @attributes ||= {}; @attributes['matches'] = value
186
- end
187
-
188
- end
189
-
190
- module Loader
191
-
192
- # Load the "real" model from the database via the corresponding model's `find` method.
193
- #
194
- # Notice that there's an option to eagerly load models with the `:load` option
195
- # for the search method.
196
- #
197
- def load(options=nil)
198
- options ? self.class.find(self.id, options) : self.class.find(self.id)
199
- end
200
-
201
- end
202
-
203
- # An object containing _Tire's_ model class methods, accessed as `Article.tire`.
204
- #
205
- class ClassMethodsProxy
206
- include Tire::Model::Naming::ClassMethods
207
- include Tire::Model::Import::ClassMethods
208
- include Tire::Model::Indexing::ClassMethods
209
- include Tire::Model::Percolate::ClassMethods
210
- include ClassMethods
211
-
212
- INTERFACE = public_instance_methods.map(&:to_sym) - Object.public_instance_methods.map(&:to_sym)
213
-
214
- attr_reader :klass
215
- def initialize(klass)
216
- @klass = klass
217
- end
218
-
219
- end
220
-
221
- # An object containing _Tire's_ model instance methods, accessed as `@article.tire`.
222
- #
223
- class InstanceMethodsProxy
224
- include Tire::Model::Naming::InstanceMethods
225
- include Tire::Model::Percolate::InstanceMethods
226
- include InstanceMethods
227
-
228
- INTERFACE = public_instance_methods.map(&:to_sym) - Object.public_instance_methods.map(&:to_sym)
229
-
230
- attr_reader :instance
231
- def initialize(instance)
232
- @instance = instance
233
- end
234
- end
235
-
236
- # A hook triggered by the `include Tire::Model::Search` statement in the model.
237
- #
238
- def self.included(base)
239
- base.class_eval do
240
-
241
- # Returns proxy to the _Tire's_ class methods.
242
- #
243
- def self.tire &block
244
- @__tire__ ||= ClassMethodsProxy.new(self)
245
-
246
- @__tire__.instance_eval(&block) if block_given?
247
- @__tire__
248
- end
249
-
250
- # Returns proxy to the _Tire's_ instance methods.
251
- #
252
- def tire &block
253
- @__tire__ ||= InstanceMethodsProxy.new(self)
254
-
255
- @__tire__.instance_eval(&block) if block_given?
256
- @__tire__
257
- end
258
-
259
- # Define _Tire's_ callbacks (<after|before>_update_elasticsearch_index).
260
- #
261
- #define_model_callbacks(:update_elasticsearch_index, :only => [:after, :before]) if \
262
- # respond_to?(:define_model_callbacks)
263
-
264
- # Serialize the model as a Hash.
265
- #
266
- # Uses `serializable_hash` representation of the model,
267
- # unless implemented in the model already.
268
- #
269
- def to_hash
270
- self.serializable_hash
271
- end unless instance_methods.map(&:to_sym).include?(:to_hash)
272
-
273
- end
274
-
275
- # Alias _Tire's_ class methods in the top-level namespace of the model,
276
- # unless there's a conflict with existing method.
277
- #
278
- ClassMethodsProxy::INTERFACE.each do |method|
279
- base.class_eval <<-"end;", __FILE__, __LINE__ unless base.public_methods.map(&:to_sym).include?(method.to_sym)
280
- def self.#{method}(*args, &block) # def search(*args, &block)
281
- tire.__send__(#{method.inspect}, *args, &block) # tire.__send__(:search, *args, &block)
282
- end # end
283
- end;
284
- end
285
-
286
- # Alias _Tire's_ instance methods in the top-level namespace of the model,
287
- # unless there's a conflict with existing method
288
- InstanceMethodsProxy::INTERFACE.each do |method|
289
- base.class_eval <<-"end;", __FILE__, __LINE__ unless base.instance_methods.map(&:to_sym).include?(method.to_sym)
290
- def #{method}(*args, &block) # def to_indexed_json(*args, &block)
291
- tire.__send__(#{method.inspect}, *args, &block) # tire.__send__(:to_indexed_json, *args, &block)
292
- end # end
293
- end;
294
- end
295
-
296
- # Include the `load` functionality in Results::Item
297
- #
298
- Results::Item.send :include, Loader
299
- end
300
-
301
-
302
- end
303
-
304
- end
305
- end