ripple_searchable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # RippleSearchable
2
2
 
3
3
  Mongoid / Active Record style query criteria DSL and Scoping for Ripple
4
- using RIAK's solr search interface.
4
+ using RIAK's solr search interface. (riak_search must be enabled)
5
5
 
6
- RippleSearchable adds chainable Criteria methods such as :where, :lt, :lte, :gt, :gte, :between
6
+ RippleSearchable adds named scopes, and chainable Criteria methods such as :where, :lt, :lte, :gt, :gte, :between
7
7
  along with :sort, :skip, :limit options to your Ripple::Document models.
8
8
 
9
9
  ## Installation
@@ -31,15 +31,19 @@ Any of the following criteria can be chained:
31
31
  === Example:
32
32
 
33
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)
34
+ Product.where(tags: "nerd", name: "joe", something: 2).
35
+ or({can_sell:1}, {can_sell: 3}).
36
+ between(availibility: 1..3, price: [3, 12]).
37
+ gte(quantity: 0, ratings: 5).
38
+ sort(:created_at, :desc).limit(5)
37
39
  ```
38
40
 
39
41
  ### Scoping
40
42
 
41
43
  Mongoid / Active Record style named scopes:
42
44
 
45
+ Use 'scope' class method to create a named scope that can be accessed from the class level or chained to criteria by the provided name.
46
+
43
47
  === Example:
44
48
 
45
49
  ```ruby
@@ -52,9 +56,12 @@ Mongoid / Active Record style named scopes:
52
56
  end
53
57
  ```
54
58
 
55
- See docs for method details.
59
+ See [docs](http://rubydoc.info/github/computadude/ripple_searchable/master/frames) for method details.
60
+
61
+ TODO:
56
62
 
57
- TODO: Write better docs.
63
+ Ability to bypass default scopes when using named scopes
64
+ Write better docs.
58
65
 
59
66
  ## Contributing
60
67
 
@@ -27,13 +27,14 @@ module Ripple
27
27
  # will append this selector:
28
28
  # "(tags:nerd AND name:Joe AND something:2)"
29
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)
30
+ clone.tap do |crit|
31
+ case selector
32
+ when String
33
+ crit.add_restriction selector
34
+ when Hash
35
+ crit.add_restriction to_lucene_pair(selector)
36
+ end
35
37
  end
36
- self
37
38
  end
38
39
 
39
40
  # Add an OR selector
@@ -45,12 +46,13 @@ module Ripple
45
46
  # will append this selector:
46
47
  # "((name:Pants) OR (name:Shirt))"
47
48
  def or(*criterion)
48
- add_restriction do
49
- criterion.each do |crit|
50
- add_restriction(to_lucene_pair(crit, operator: "OR"), operator: "OR" )
49
+ clone.tap do |crit|
50
+ crit.add_restriction do
51
+ criterion.each do |c|
52
+ crit.add_restriction(to_lucene_pair(c, operator: "OR"), operator: "OR" )
53
+ end
51
54
  end
52
55
  end
53
- self
54
56
  end
55
57
 
56
58
  alias :any_of :or
@@ -66,12 +68,13 @@ module Ripple
66
68
  # will append this selector:
67
69
  # "((availibility:[1 TO 3] AND price:[12 TO 20]))"
68
70
  def between(*criterion)
69
- add_restriction do
70
- criterion.each do |crit|
71
- add_restriction(to_lucene_pair(crit, operator: "BETWEEN"))
71
+ clone.tap do |crit|
72
+ crit.add_restriction do
73
+ criterion.each do |c|
74
+ crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN"))
75
+ end
72
76
  end
73
77
  end
74
- self
75
78
  end
76
79
 
77
80
  # Add a 'less or equal than' selector
@@ -83,13 +86,14 @@ module Ripple
83
86
  # will append this selector:
84
87
  # "((quantity:[* TO 10] AND ratings:[* TO 5]))"
85
88
  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"))
89
+ clone.tap do |crit|
90
+ crit.add_restriction do
91
+ crit.criterion.each do |c|
92
+ c.each {|k,v| c[k]=Array.wrap(v).unshift(10**20)}
93
+ crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN"))
94
+ end
90
95
  end
91
96
  end
92
- self
93
97
  end
94
98
 
95
99
  # Add a 'greater or equal than' selector
@@ -101,13 +105,14 @@ module Ripple
101
105
  # will append this selector:
102
106
  # "((quantity:[0 TO *] AND ratings:[5 TO *]))"
103
107
  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
+ clone.tap do |crit|
109
+ crit.add_restriction do
110
+ crit.criterion.each do |c|
111
+ c.each {|k,v| c[k]=Array.wrap(v).push(10**20)}
112
+ crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN"))
113
+ end
108
114
  end
109
115
  end
110
- self
111
116
  end
112
117
 
113
118
  # Add a 'less than' selector
@@ -119,13 +124,14 @@ module Ripple
119
124
  # will append this selector:
120
125
  # "((quantity:{* TO 10} AND ratings:{* TO 5}))"
121
126
  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))
127
+ clone.tap do |crit|
128
+ crit.add_restriction do
129
+ criterion.each do |c|
130
+ c.each {|k,v| c[k]=Array.wrap(v).unshift("*")}
131
+ crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN", exclusive: true))
132
+ end
126
133
  end
127
134
  end
128
- self
129
135
  end
130
136
 
131
137
  # Add a 'greater than' selector
@@ -137,13 +143,14 @@ module Ripple
137
143
  # will append this selector:
138
144
  # "((quantity:{0 TO *} AND ratings:{5 TO *}))"
139
145
  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))
146
+ clone.tap do |crit|
147
+ crit.add_restriction do
148
+ criterion.each do |c|
149
+ c.each {|k,v| c[k]=Array.wrap(v).push("*")}
150
+ crit.add_restriction(to_lucene_pair(c, operator: "BETWEEN", exclusive: true))
151
+ end
144
152
  end
145
153
  end
146
- self
147
154
  end
148
155
 
149
156
  # Add sort options to criteria
@@ -155,13 +162,14 @@ module Ripple
155
162
  # will append this sort option:
156
163
  # "availibility asc, created_at desc"
157
164
  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}"}
165
+ clone.tap do |crit|
166
+ case sort_options
167
+ when String
168
+ crit.add_sort_option sort_options
169
+ when Hash
170
+ sort_options.each {|k,v| crit.add_sort_option "#{k} #{v.downcase}"}
171
+ end
163
172
  end
164
- self
165
173
  end
166
174
 
167
175
  alias :order_by :sort
@@ -175,9 +183,10 @@ module Ripple
175
183
  #
176
184
  # will limit the number of returned documetns to 10
177
185
  def limit(limit)
178
- clear_cache
179
- self.options[:rows] = limit
180
- self
186
+ clone.tap do |crit|
187
+ crit.clear_cache
188
+ crit.options[:rows] = limit
189
+ end
181
190
  end
182
191
 
183
192
  alias :rows :limit
@@ -189,9 +198,10 @@ module Ripple
189
198
  # Product.between(availibility:[1,3]).skip(10)
190
199
  #
191
200
  def skip(skip)
192
- clear_cache
193
- self.options[:start] = skip
194
- self
201
+ clone.tap do |crit|
202
+ crit.clear_cache
203
+ crit.options[:start] = skip
204
+ end
195
205
  end
196
206
 
197
207
  alias :start :skip
@@ -231,20 +241,48 @@ module Ripple
231
241
  end
232
242
 
233
243
  def merge(criteria)
244
+ crit = clone
245
+ crit.merge!(criteria)
246
+ crit
247
+ end
248
+
249
+ def merge!(criteria)
234
250
  add_restriction criteria.selector
235
251
  self.options.merge!(criteria.options)
236
252
  self
237
253
  end
238
254
 
255
+ def to_proc
256
+ ->{ self }
257
+ end
258
+
259
+ def ==(other)
260
+ self.klass == other.klass && self.selector == other.selector && self.options == other.options
261
+ end
262
+
263
+ def initialize_copy(other)
264
+ @selector = other.selector.dup
265
+ @options = other.options.dup
266
+ if other.response.present?
267
+ @response = other.response.dup
268
+ @documents = other.documents.dup
269
+ @docs = other.docs.dup
270
+ @document_ids = other.document_ids.dup
271
+ end
272
+ super
273
+ end
274
+
239
275
  def method_missing(name, *args, &block)
240
276
  if klass.respond_to?(name)
241
277
  klass.send(:with_scope, self) do
242
278
  klass.send(name, *args, &block)
243
279
  end
280
+ else
281
+ super
244
282
  end
245
283
  end
246
284
 
247
- private
285
+ protected
248
286
 
249
287
  def clear_cache
250
288
  @documents, @cached, @response, @total, @docs, @document_ids = [], false
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module Ripple
3
+ module Loggable
4
+
5
+ def logger
6
+ return @logger if defined?(@logger)
7
+ @logger = rails_logger || default_logger
8
+ end
9
+
10
+ def logger=(logger)
11
+ @logger = logger
12
+ end
13
+
14
+ private
15
+
16
+ def default_logger
17
+ logger = Logger.new($stdout)
18
+ logger.level = Logger::INFO
19
+ logger
20
+ end
21
+
22
+ def rails_logger
23
+ defined?(::Rails) && ::Rails.respond_to?(:logger) && ::Rails.logger
24
+ end
25
+ end
26
+ end
@@ -12,9 +12,26 @@ module Ripple
12
12
 
13
13
  module ClassMethods
14
14
 
15
+
16
+ # Create a scope that can be accessed from the class level or chained to
17
+ # criteria by the provided name.
18
+ #
19
+ # === Example
20
+ #
21
+ # class Product
22
+ # include Ripple::Document
23
+ #
24
+ # scope :active, where(active: true)
25
+ # scope :avail, ->(count){ where(quantity: count)}
26
+ # end
27
+ #
28
+ # Product.active.where(name: "peter")
29
+ #
30
+ # sets the selector to:
31
+ # "((active:true)) AND (name:peter)"
15
32
  def scope(name, value, &block)
16
33
  name = name.to_sym
17
- #valid_scope_name?(name)
34
+ valid_scope_name?(name)
18
35
  scopes[name] = {
19
36
  scope: strip_default_scope(value),
20
37
  extension: Module.new(&block)
@@ -22,12 +39,29 @@ module Ripple
22
39
  define_scope_method(name)
23
40
  end
24
41
 
42
+ def default_scope(value)
43
+ self.default_scoping = if default_scoping
44
+ ->{ default_scoping.call.merge(value.to_proc.call) } unless default_scoping.call == value
45
+ else
46
+ value.to_proc
47
+ end
48
+ end
49
+
25
50
  def scope_stack
26
51
  Thread.current[:"#{self.bucket_name}_scope_stack"] ||= []
27
52
  end
28
53
 
29
54
  def with_default_scope
30
- default_scoping || scope_stack.last || Criteria.new(self)
55
+ default_scoping.try(:call) || without_default_scope
56
+ end
57
+
58
+ def without_default_scope
59
+ Thread.current[:"#{self.bucket_name}_without_default_scope"] = true
60
+ scope_stack.last || Criteria.new(self)
61
+ end
62
+
63
+ def without_default_scope?
64
+ Thread.current[:"#{self.bucket_name}_without_default_scope"]
31
65
  end
32
66
 
33
67
  def with_scope(criteria)
@@ -42,8 +76,8 @@ module Ripple
42
76
  protected
43
77
 
44
78
  def valid_scope_name?(name)
45
- if logger && respond_to?(name, true)
46
- logger.warn "Creating scope :#{name}. " \
79
+ if Ripple.logger && respond_to?(name, true)
80
+ Ripple.logger.warn "Creating scope :#{name}. " \
47
81
  "Overwriting existing method #{self.name}.#{name}."
48
82
  end
49
83
  end
@@ -62,7 +96,7 @@ module Ripple
62
96
 
63
97
  def strip_default_scope(value)
64
98
  if value.is_a?(Criteria)
65
- ->{ value}
99
+ value.to_proc
66
100
  else
67
101
  value
68
102
  end
@@ -27,7 +27,7 @@ module Ripple
27
27
  end
28
28
 
29
29
  def criteria
30
- @criteria = Criteria.new(self)
30
+ @criteria = default_scoping.try(:call) || Criteria.new(self)
31
31
  end
32
32
  end
33
33
 
@@ -1,3 +1,3 @@
1
1
  module RippleSearchable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -4,8 +4,13 @@ require 'active_support/concern'
4
4
  require 'ripple_searchable/version'
5
5
  require 'ripple_searchable/searchable'
6
6
  require 'ripple_searchable/criteria'
7
+ require 'ripple_searchable/loggable'
7
8
  require 'ripple_searchable/scoping'
8
9
 
10
+ Ripple.class_eval do
11
+ extend Ripple::Loggable
12
+ end
13
+
9
14
  Ripple::Document.class_eval do
10
15
  include Ripple::Searchable
11
16
  include Ripple::Scoping
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ripple_searchable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-17 00:00:00.000000000 Z
12
+ date: 2012-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -101,6 +101,7 @@ files:
101
101
  - Rakefile
102
102
  - lib/ripple_searchable.rb
103
103
  - lib/ripple_searchable/criteria.rb
104
+ - lib/ripple_searchable/loggable.rb
104
105
  - lib/ripple_searchable/scoping.rb
105
106
  - lib/ripple_searchable/searchable.rb
106
107
  - lib/ripple_searchable/version.rb