ripple_searchable 0.0.1
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/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +67 -0
- data/Rakefile +2 -0
- data/lib/ripple_searchable/criteria.rb +305 -0
- data/lib/ripple_searchable/scoping.rb +73 -0
- data/lib/ripple_searchable/searchable.rb +35 -0
- data/lib/ripple_searchable/version.rb +3 -0
- data/lib/ripple_searchable.rb +12 -0
- data/ripple_searchable.gemspec +23 -0
- metadata +133 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Mark Ronai
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# RippleSearchable
|
2
|
+
|
3
|
+
Mongoid / Active Record style query criteria DSL and Scoping for Ripple
|
4
|
+
using RIAK's solr search interface.
|
5
|
+
|
6
|
+
RippleSearchable adds chainable Criteria methods such as :where, :lt, :lte, :gt, :gte, :between
|
7
|
+
along with :sort, :skip, :limit options to your Ripple::Document models.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'ripple_searchable'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install ripple_searchable
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Criteria:
|
26
|
+
|
27
|
+
Any of the following criteria can be chained:
|
28
|
+
|
29
|
+
:where, :lt, :lte, :gt, :gte, :between, with sort, :skip, :limit
|
30
|
+
|
31
|
+
=== Example:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
Product.where(tags: "nerd", name: "joe", something: 2).or({can_sell:
|
35
|
+
1}, {can_sell: 3}).between(availibility: 1..3, price: [3,
|
36
|
+
12]).gte(quantity: 0, ratings: 5).sort(created_at, :desc).limit(5)
|
37
|
+
```
|
38
|
+
|
39
|
+
### Scoping
|
40
|
+
|
41
|
+
Mongoid / Active Record style named scopes:
|
42
|
+
|
43
|
+
=== Example:
|
44
|
+
|
45
|
+
```ruby
|
46
|
+
class Product
|
47
|
+
include Ripple::Document
|
48
|
+
|
49
|
+
scope :active, where(active: true)
|
50
|
+
scope :avail, ->(count){ where(quantity: count)}
|
51
|
+
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
See docs for method details.
|
56
|
+
|
57
|
+
TODO: Write better docs.
|
58
|
+
|
59
|
+
## Contributing
|
60
|
+
|
61
|
+
This gem is still under heavy development. Feel free to contribute.
|
62
|
+
|
63
|
+
1. Fork it
|
64
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
65
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
66
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
67
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,305 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
|
5
|
+
class CriteriaError < StandardError; end
|
6
|
+
|
7
|
+
# chainable Criteria methods such as :where, :lt, :lte, :gt, :gte, :between,
|
8
|
+
# with sort, :skip, :limit options
|
9
|
+
class Criteria
|
10
|
+
|
11
|
+
include Translation
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
attr_accessor :selector, :klass, :options, :response, :cached, :total, :docs, :document_ids
|
15
|
+
|
16
|
+
def initialize(klass)
|
17
|
+
@selector, @klass, @options, @documents, @cached = "", klass, {}
|
18
|
+
clear_cache
|
19
|
+
end
|
20
|
+
|
21
|
+
# Main criteria selector to search records
|
22
|
+
#
|
23
|
+
# === Example
|
24
|
+
#
|
25
|
+
# Model.where(tags: "nerd", name: "Joe", something: 2)
|
26
|
+
#
|
27
|
+
# will append this selector:
|
28
|
+
# "(tags:nerd AND name:Joe AND something:2)"
|
29
|
+
def where(selector = nil)
|
30
|
+
case selector
|
31
|
+
when String
|
32
|
+
add_restriction selector
|
33
|
+
when Hash
|
34
|
+
add_restriction to_lucene_pair(selector)
|
35
|
+
end
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add an OR selector
|
40
|
+
#
|
41
|
+
# === Example
|
42
|
+
#
|
43
|
+
# Product.or({name: "Pants"}, {name: "Shirt"})
|
44
|
+
#
|
45
|
+
# will append this selector:
|
46
|
+
# "((name:Pants) OR (name:Shirt))"
|
47
|
+
def or(*criterion)
|
48
|
+
add_restriction do
|
49
|
+
criterion.each do |crit|
|
50
|
+
add_restriction(to_lucene_pair(crit, operator: "OR"), operator: "OR" )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
alias :any_of :or
|
57
|
+
|
58
|
+
# Add an Range selector. Values in the passed hash can be either a Range or an Array.
|
59
|
+
# of the passed hash has multiple elements, the condition will be AND.
|
60
|
+
# The range is inclusive.
|
61
|
+
#
|
62
|
+
# === Example
|
63
|
+
#
|
64
|
+
# Product.between(availibility: 1..3, price: [12, 20])
|
65
|
+
#
|
66
|
+
# will append this selector:
|
67
|
+
# "((availibility:[1 TO 3] AND price:[12 TO 20]))"
|
68
|
+
def between(*criterion)
|
69
|
+
add_restriction do
|
70
|
+
criterion.each do |crit|
|
71
|
+
add_restriction(to_lucene_pair(crit, operator: "BETWEEN"))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Add a 'less or equal than' selector
|
78
|
+
#
|
79
|
+
# === Example
|
80
|
+
#
|
81
|
+
# Product.lte(quantity: 10, ratings: 5)
|
82
|
+
#
|
83
|
+
# will append this selector:
|
84
|
+
# "((quantity:[* TO 10] AND ratings:[* TO 5]))"
|
85
|
+
def lte(*criterion)
|
86
|
+
add_restriction do
|
87
|
+
criterion.each do |crit|
|
88
|
+
crit.each {|k,v| crit[k]=Array.wrap(v).unshift(10**20)}
|
89
|
+
add_restriction(to_lucene_pair(crit, operator: "BETWEEN"))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# Add a 'greater or equal than' selector
|
96
|
+
#
|
97
|
+
# === Example
|
98
|
+
#
|
99
|
+
# Product.gte(quantity: 0, ratings: 5)
|
100
|
+
#
|
101
|
+
# will append this selector:
|
102
|
+
# "((quantity:[0 TO *] AND ratings:[5 TO *]))"
|
103
|
+
def gte(*criterion)
|
104
|
+
add_restriction do
|
105
|
+
criterion.each do |crit|
|
106
|
+
crit.each {|k,v| crit[k]=Array.wrap(v).push(10**20)}
|
107
|
+
add_restriction(to_lucene_pair(crit, operator: "BETWEEN"))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# Add a 'less than' selector
|
114
|
+
#
|
115
|
+
# === Example
|
116
|
+
#
|
117
|
+
# Product.lt(quantity: 10, ratings: 5)
|
118
|
+
#
|
119
|
+
# will append this selector:
|
120
|
+
# "((quantity:{* TO 10} AND ratings:{* TO 5}))"
|
121
|
+
def lt(*criterion)
|
122
|
+
add_restriction do
|
123
|
+
criterion.each do |crit|
|
124
|
+
crit.each {|k,v| crit[k]=Array.wrap(v).unshift("*")}
|
125
|
+
add_restriction(to_lucene_pair(crit, operator: "BETWEEN", exclusive: true))
|
126
|
+
end
|
127
|
+
end
|
128
|
+
self
|
129
|
+
end
|
130
|
+
|
131
|
+
# Add a 'greater than' selector
|
132
|
+
#
|
133
|
+
# === Example
|
134
|
+
#
|
135
|
+
# Product.gt(quantity: 0, ratings: 5)
|
136
|
+
#
|
137
|
+
# will append this selector:
|
138
|
+
# "((quantity:{0 TO *} AND ratings:{5 TO *}))"
|
139
|
+
def gt(*criterion)
|
140
|
+
add_restriction do
|
141
|
+
criterion.each do |crit|
|
142
|
+
crit.each {|k,v| crit[k]=Array.wrap(v).push("*")}
|
143
|
+
add_restriction(to_lucene_pair(crit, operator: "BETWEEN", exclusive: true))
|
144
|
+
end
|
145
|
+
end
|
146
|
+
self
|
147
|
+
end
|
148
|
+
|
149
|
+
# Add sort options to criteria
|
150
|
+
#
|
151
|
+
# === Example
|
152
|
+
#
|
153
|
+
# Product.between(availibility:[1,3]).sort(availibility: :asc, created_at: :desc)
|
154
|
+
#
|
155
|
+
# will append this sort option:
|
156
|
+
# "availibility asc, created_at desc"
|
157
|
+
def sort(sort_options)
|
158
|
+
case sort_options
|
159
|
+
when String
|
160
|
+
add_sort_option sort_options
|
161
|
+
when Hash
|
162
|
+
sort_options.each {|k,v| add_sort_option "#{k} #{v.downcase}"}
|
163
|
+
end
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
alias :order_by :sort
|
168
|
+
alias :order :sort
|
169
|
+
|
170
|
+
# Add limit option to criteria. Useful for pagination. Default is 10.
|
171
|
+
#
|
172
|
+
# === Example
|
173
|
+
#
|
174
|
+
# Product.between(availibility:[1,3]).limit(10)
|
175
|
+
#
|
176
|
+
# will limit the number of returned documetns to 10
|
177
|
+
def limit(limit)
|
178
|
+
clear_cache
|
179
|
+
self.options[:rows] = limit
|
180
|
+
self
|
181
|
+
end
|
182
|
+
|
183
|
+
alias :rows :limit
|
184
|
+
|
185
|
+
# Add skip option to criteria. Useful for pagination. Default is 0.
|
186
|
+
#
|
187
|
+
# === Example
|
188
|
+
#
|
189
|
+
# Product.between(availibility:[1,3]).skip(10)
|
190
|
+
#
|
191
|
+
def skip(skip)
|
192
|
+
clear_cache
|
193
|
+
self.options[:start] = skip
|
194
|
+
self
|
195
|
+
end
|
196
|
+
|
197
|
+
alias :start :skip
|
198
|
+
|
199
|
+
|
200
|
+
# Executes the search query
|
201
|
+
def execute
|
202
|
+
raise CriteriaError, t('empty_selector_error') if self.selector.blank?
|
203
|
+
@response = @klass.search self.selector, self.options
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns the matched documents
|
207
|
+
def documents
|
208
|
+
if @cached
|
209
|
+
@documents
|
210
|
+
else
|
211
|
+
parse_response
|
212
|
+
@cached = true
|
213
|
+
@documents = self.klass.find self.document_ids
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def each(&block)
|
218
|
+
documents.each(&block)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Total number of matching documents
|
222
|
+
def total
|
223
|
+
parse_response
|
224
|
+
@total
|
225
|
+
end
|
226
|
+
|
227
|
+
# Array of matching document id's
|
228
|
+
def document_ids
|
229
|
+
parse_response
|
230
|
+
@document_ids
|
231
|
+
end
|
232
|
+
|
233
|
+
def merge(criteria)
|
234
|
+
add_restriction criteria.selector
|
235
|
+
self.options.merge!(criteria.options)
|
236
|
+
self
|
237
|
+
end
|
238
|
+
|
239
|
+
def method_missing(name, *args, &block)
|
240
|
+
if klass.respond_to?(name)
|
241
|
+
klass.send(:with_scope, self) do
|
242
|
+
klass.send(name, *args, &block)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
def clear_cache
|
250
|
+
@documents, @cached, @response, @total, @docs, @document_ids = [], false
|
251
|
+
end
|
252
|
+
|
253
|
+
def parse_response
|
254
|
+
execute if @response.blank?
|
255
|
+
self.total = @response["response"]["numFound"]
|
256
|
+
self.docs = @response["response"]["docs"]
|
257
|
+
self.document_ids = self.docs.map {|e| e["id"]}
|
258
|
+
rescue
|
259
|
+
clear_cache
|
260
|
+
raise CriteriaError, t('failed_query')
|
261
|
+
end
|
262
|
+
|
263
|
+
def add_restriction(*args, &block)
|
264
|
+
clear_cache
|
265
|
+
options = args.extract_options!
|
266
|
+
operator = options[:operator] || "AND"
|
267
|
+
restriction = args.first
|
268
|
+
separator = @selector.present? ? " #{operator} " : ""
|
269
|
+
if block_given?
|
270
|
+
@selector << "#{separator}("
|
271
|
+
yield
|
272
|
+
@selector << ")"
|
273
|
+
else
|
274
|
+
@selector << "#{separator unless @selector[-1] == '('}(#{restriction})"
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def add_sort_option(*args)
|
279
|
+
clear_cache
|
280
|
+
args.each do |s|
|
281
|
+
if options[:sort].present?
|
282
|
+
options[:sort] << ", #{s}"
|
283
|
+
else
|
284
|
+
options[:sort] = s
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def to_lucene_pair(conditions, options = {})
|
290
|
+
operator = options[:operator] || "AND"
|
291
|
+
if operator == "BETWEEN"
|
292
|
+
conditions.map do |k,v|
|
293
|
+
case v
|
294
|
+
when Range, Array
|
295
|
+
"#{k}:#{options[:exclusive] ? '{' : '['}#{v.first} TO #{v.last}#{options[:exclusive] ? '}' : ']'}"
|
296
|
+
when String
|
297
|
+
"#{k}: #{v}"
|
298
|
+
end
|
299
|
+
end.join(" AND ")
|
300
|
+
else
|
301
|
+
conditions.map {|k,v| "#{k}:#{v}"}.join(" #{operator} ")
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Scoping
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :default_scoping
|
9
|
+
class_attribute :scopes
|
10
|
+
self.scopes = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
module ClassMethods
|
14
|
+
|
15
|
+
def scope(name, value, &block)
|
16
|
+
name = name.to_sym
|
17
|
+
#valid_scope_name?(name)
|
18
|
+
scopes[name] = {
|
19
|
+
scope: strip_default_scope(value),
|
20
|
+
extension: Module.new(&block)
|
21
|
+
}
|
22
|
+
define_scope_method(name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def scope_stack
|
26
|
+
Thread.current[:"#{self.bucket_name}_scope_stack"] ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def with_default_scope
|
30
|
+
default_scoping || scope_stack.last || Criteria.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def with_scope(criteria)
|
34
|
+
scope_stack.push(criteria)
|
35
|
+
begin
|
36
|
+
yield criteria
|
37
|
+
ensure
|
38
|
+
scope_stack.pop
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def valid_scope_name?(name)
|
45
|
+
if logger && respond_to?(name, true)
|
46
|
+
logger.warn "Creating scope :#{name}. " \
|
47
|
+
"Overwriting existing method #{self.name}.#{name}."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def define_scope_method(name)
|
52
|
+
(class << self; self; end).class_eval <<-SCOPE
|
53
|
+
def #{name}(*args)
|
54
|
+
scoping = scopes[:#{name}]
|
55
|
+
scope, extension = scoping[:scope].(*args), scoping[:extension]
|
56
|
+
criteria = with_default_scope.merge(scope)
|
57
|
+
criteria.extend(extension)
|
58
|
+
criteria
|
59
|
+
end
|
60
|
+
SCOPE
|
61
|
+
end
|
62
|
+
|
63
|
+
def strip_default_scope(value)
|
64
|
+
if value.is_a?(Criteria)
|
65
|
+
->{ value}
|
66
|
+
else
|
67
|
+
value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Ripple
|
4
|
+
module Searchable
|
5
|
+
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
unless method_defined? :id
|
13
|
+
define_method :id do
|
14
|
+
self.key
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
attr_accessor :criteria
|
21
|
+
|
22
|
+
delegate :where, :or, :any_of, :gte, :lte, :gt, :lt, :between, :sort, to: :criteria
|
23
|
+
|
24
|
+
# Performs a search via the Solr interface.
|
25
|
+
def search(*args)
|
26
|
+
Ripple.client.search(self.bucket_name, *args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def criteria
|
30
|
+
@criteria = Criteria.new(self)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'ripple'
|
2
|
+
require 'ripple/translation'
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'ripple_searchable/version'
|
5
|
+
require 'ripple_searchable/searchable'
|
6
|
+
require 'ripple_searchable/criteria'
|
7
|
+
require 'ripple_searchable/scoping'
|
8
|
+
|
9
|
+
Ripple::Document.class_eval do
|
10
|
+
include Ripple::Searchable
|
11
|
+
include Ripple::Scoping
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/ripple_searchable/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Mark Ronai"]
|
6
|
+
gem.email = ["computadude@me.com"]
|
7
|
+
gem.description = %q{Mongoid / Active Record style query criteria and scoping for Ripple}
|
8
|
+
gem.summary = %q{Mongoid / Active Record style query criteria and scoping for Ripple}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "ripple_searchable"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = RippleSearchable::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "activesupport", [">= 3.0.0", "< 3.3.0"]
|
19
|
+
gem.add_dependency "activemodel", [">= 3.0.0", "< 3.3.0"]
|
20
|
+
gem.add_dependency "ripple", ">=1.0.0.beta2"
|
21
|
+
|
22
|
+
gem.add_development_dependency "rails", '3.2.8'
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ripple_searchable
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mark Ronai
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
- - <
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 3.3.0
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
- - <
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 3.3.0
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activemodel
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 3.0.0
|
44
|
+
- - <
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 3.3.0
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.0.0
|
55
|
+
- - <
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 3.3.0
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: ripple
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: 1.0.0.beta2
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.0.0.beta2
|
74
|
+
- !ruby/object:Gem::Dependency
|
75
|
+
name: rails
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - '='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 3.2.8
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.2.8
|
90
|
+
description: Mongoid / Active Record style query criteria and scoping for Ripple
|
91
|
+
email:
|
92
|
+
- computadude@me.com
|
93
|
+
executables: []
|
94
|
+
extensions: []
|
95
|
+
extra_rdoc_files: []
|
96
|
+
files:
|
97
|
+
- .gitignore
|
98
|
+
- Gemfile
|
99
|
+
- LICENSE
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- lib/ripple_searchable.rb
|
103
|
+
- lib/ripple_searchable/criteria.rb
|
104
|
+
- lib/ripple_searchable/scoping.rb
|
105
|
+
- lib/ripple_searchable/searchable.rb
|
106
|
+
- lib/ripple_searchable/version.rb
|
107
|
+
- ripple_searchable.gemspec
|
108
|
+
homepage: ''
|
109
|
+
licenses: []
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 1.8.23
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: Mongoid / Active Record style query criteria and scoping for Ripple
|
132
|
+
test_files: []
|
133
|
+
has_rdoc:
|