ripple_searchable 0.0.1 → 0.0.2

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/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