waistband 0.4.2 → 0.7.0

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.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: waistband
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.7.0
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: 2013-10-16 00:00:00.000000000 Z
12
+ date: 2013-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -109,9 +109,7 @@ files:
109
109
  - lib/waistband.rb
110
110
  - lib/waistband/configuration.rb
111
111
  - lib/waistband/connection.rb
112
- - lib/waistband/free_query.rb
113
112
  - lib/waistband/index.rb
114
- - lib/waistband/model.rb
115
113
  - lib/waistband/query.rb
116
114
  - lib/waistband/query_helpers.rb
117
115
  - lib/waistband/query_result.rb
@@ -125,9 +123,7 @@ files:
125
123
  - spec/config/waistband/waistband_search.yml
126
124
  - spec/lib/configuration_spec.rb
127
125
  - spec/lib/connection_spec.rb
128
- - spec/lib/free_query_spec.rb
129
126
  - spec/lib/index_spec.rb
130
- - spec/lib/model_spec.rb
131
127
  - spec/lib/query_result_spec.rb
132
128
  - spec/lib/query_spec.rb
133
129
  - spec/lib/stringified_array_spec.rb
@@ -167,9 +163,7 @@ test_files:
167
163
  - spec/config/waistband/waistband_search.yml
168
164
  - spec/lib/configuration_spec.rb
169
165
  - spec/lib/connection_spec.rb
170
- - spec/lib/free_query_spec.rb
171
166
  - spec/lib/index_spec.rb
172
- - spec/lib/model_spec.rb
173
167
  - spec/lib/query_result_spec.rb
174
168
  - spec/lib/query_spec.rb
175
169
  - spec/lib/stringified_array_spec.rb
@@ -1,34 +0,0 @@
1
- require 'active_support/core_ext/hash/indifferent_access'
2
-
3
- module Waistband
4
- class FreeQuery
5
-
6
- include ::Waistband::QueryHelpers
7
-
8
- attr_accessor :page, :page_size
9
-
10
- def initialize(index, options = {})
11
- @index = index
12
- @page = (options[:page] || 1).to_i
13
- @page_size = (options[:page_size] || 20).to_i
14
- end
15
-
16
- def prepare(hash)
17
- @hash = hash.with_indifferent_access
18
- end
19
-
20
- private
21
-
22
- def to_hash
23
- raise "No query has been prepared yet!" unless @hash
24
-
25
- @hash[:from] = from unless @hash[:from]
26
- @hash[:size] = @page_size unless @hash[:size]
27
-
28
- @hash
29
- end
30
-
31
- # /private
32
-
33
- end
34
- end
@@ -1,215 +0,0 @@
1
- require 'digest/sha1'
2
- require 'json'
3
- require 'active_support/core_ext/class/attribute'
4
- require 'active_support/values/time_zone'
5
- require 'active_support/time_with_zone'
6
-
7
- module Waistband
8
- class Model
9
-
10
- class_attribute :index_name, :column_names, :validate_columns,
11
- :default_values, :stringify_columns
12
-
13
- class << self
14
-
15
- def index
16
- @index ||= Waistband::Index.new(index_name)
17
- end
18
-
19
- def find(id)
20
- attrs = index.read(id)
21
- raise ActiveRecord::RecordNotFound.new("#{self} not found!") unless attrs
22
- new attrs
23
- end
24
-
25
- def create(attributes)
26
- instance = new(attributes)
27
- instance.save
28
- instance
29
- end
30
-
31
- def query
32
- q = index.query('')
33
- q.add_term('model_type', name.underscore)
34
- q
35
- end
36
-
37
- def query_desc
38
- q = query
39
- q.add_sort 'created_at', 'desc'
40
- q
41
- end
42
-
43
- def query_asc
44
- q = query
45
- q.add_sort 'created_at', 'asc'
46
- q
47
- end
48
-
49
- def first
50
- attrs = query_asc.results.first.try(:source)
51
- return new(attrs) if attrs
52
- nil
53
- end
54
-
55
- def last
56
- attrs = query_desc.results.first.try(:source)
57
- return new(attrs) if attrs
58
- nil
59
- end
60
-
61
- def with_index(name)
62
- self.index_name = name.to_s
63
- end
64
-
65
- def create_index!
66
- Waistband::Index.new(index_name).create!
67
- end
68
-
69
- def destroy_index!
70
- Waistband::Index.new(index_name).destroy!
71
- end
72
-
73
- def validates(*cols)
74
- self.validate_columns ||= []
75
- self.validate_columns |= cols.map(&:to_sym)
76
- end
77
-
78
- def defaults(values = {})
79
- self.default_values ||= {}
80
-
81
- values.each do |k, v|
82
- self.default_values = self.default_values.merge({k.to_sym => v})
83
- end
84
- end
85
-
86
- def stringify(*cols)
87
- self.stringify_columns ||= []
88
- self.stringify_columns |= cols.map(&:to_sym)
89
- end
90
-
91
- def columns(*cols)
92
- cols |= [:id, :model_type, :created_at, :updated_at]
93
- self.column_names ||= []
94
- self.column_names |= cols.map(&:to_sym)
95
-
96
- cols.each do |col|
97
- # Normal attributes setters and getters
98
- class_eval <<-EV, __FILE__, __LINE__ + 1
99
- def #{col}
100
- read_attribute(:#{col})
101
- end
102
-
103
- def #{col}=(val)
104
- write_attribute(:#{col}, val)
105
- end
106
- EV
107
-
108
- # Relationship type columns: `user_id`, `app_id` to `user`, `app`
109
- if col =~ /(.*)_id$/
110
- class_eval <<-EV, __FILE__, __LINE__ + 1
111
- def #{$1}
112
- klass = "#{$1}".classify.constantize
113
- klass.find(#{col})
114
- end
115
-
116
- def #{$1}=(val)
117
- write_attribute(:#{$1}_id, val.id)
118
- end
119
- EV
120
- end
121
-
122
- end
123
- end
124
-
125
- end # /class << self
126
-
127
- attr_reader :errors
128
-
129
- def initialize(attributes = {})
130
- @attributes = (attributes || {}).symbolize_keys
131
-
132
- @attributes.each do |key, val|
133
- if self.class.stringify_columns.include?(key)
134
- self.send("#{key}=", val)
135
- elsif !self.class.column_names.include?(key)
136
- raise ArgumentError.new("#{key} is not a valid column name!")
137
- end
138
- end
139
-
140
- self.class.default_values.each do |k, v|
141
- self.send("#{k}=", v) if self.send(k).nil?
142
- end
143
-
144
- @errors = ::Waistband::QuickError.new
145
- end
146
-
147
- def save
148
- return false unless valid?
149
-
150
- before_save
151
-
152
- prev_id = self.id
153
- prev_created_at = self.created_at
154
- prev_updated_at = self.updated_at
155
-
156
- self.id ||= generated_id
157
- self.created_at ||= (Time.zone || Time).now.to_i
158
- self.updated_at = (Time.zone || Time).now.to_i
159
- self.model_type = self.class.name.downcase
160
-
161
- stored_json = JSON.parse store!
162
- stored = !!stored_json.try(:[], 'ok')
163
-
164
- if stored
165
- after_save
166
- else stored
167
- self.id = prev_id
168
- self.created_at = prev_created_at
169
- self.updated_at = prev_updated_at
170
- end
171
-
172
- stored
173
- end
174
-
175
- def before_save
176
- end
177
-
178
- def after_save
179
- end
180
-
181
- def valid?
182
- self.class.validate_columns.each do |col|
183
- @errors << "#{col} cannot be nil" if self.send(col).nil?
184
- end
185
-
186
- @errors.empty?
187
- end
188
-
189
- def attributes
190
- Hash[self.class.column_names.map{|col| [col, self.send(col)] }]
191
- end
192
-
193
- def read_attribute(attribute)
194
- @attributes[attribute]
195
- end
196
-
197
- def write_attribute(attribute, val)
198
- val = val.to_s if self.class.stringify_columns.include?(attribute.to_sym)
199
- @attributes[attribute] = val
200
- end
201
-
202
- private
203
-
204
- def store!
205
- self.class.index.store!(id, attributes)
206
- end
207
-
208
- def generated_id
209
- @generated_id ||= Digest::SHA1.hexdigest "#{attributes}:#{rand(999999)}"
210
- end
211
-
212
- # /private
213
-
214
- end
215
- end
@@ -1,161 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Waistband::FreeQuery do
4
-
5
- let(:index) { Waistband::Index.new('search') }
6
- let(:geo_index) { Waistband::Index.new('geo') }
7
-
8
- let(:query) { Waistband::FreeQuery.new('search') }
9
- let(:geo_query) { Waistband::FreeQuery.new('geo') }
10
-
11
- let(:attrs) do
12
- {
13
- 'query' => {
14
- 'bool' => {
15
- 'must' => [
16
- {
17
- 'multi_match' => {
18
- 'query' => "shopping ikea",
19
- 'fields' => ['name']
20
- },
21
- }
22
- ]
23
- }
24
- }
25
- }
26
- end
27
-
28
- it "correclty forms a query hash" do
29
- add_result!
30
- query.prepare(attrs)
31
-
32
- expect(query.instance_variable_get('@hash')).to eql(attrs)
33
- end
34
-
35
- it "finds results for a free query" do
36
- add_result!
37
- query.prepare(attrs)
38
- json = query.send(:execute!)
39
-
40
- json['hits'].should be_a Hash
41
- json['hits']['total'].should > 0
42
- json['hits']['hits'].size.should eql 2
43
-
44
- hit = json['hits']['hits'].first
45
-
46
- hit['_id'].should match(/^task_.*/)
47
- hit['_source'].should be_a Hash
48
- hit['_source']['id'].should eql 123123
49
- hit['_source']['name'].should eql 'some shopping in ikea'
50
- hit['_source']['user_id'].should eql 999
51
- hit['_source']['description'].should eql 'i need you to pick up some stuff in ikea'
52
- end
53
-
54
- it "permits storing and fetching geo results" do
55
- add_geo_results!
56
- geo_query.prepare({
57
- "query" => {
58
- "filtered" => {
59
- "query" => { "match_all" => {} },
60
- "filter" => {
61
- "geo_shape" => {
62
- "work_area" => {
63
- "relation" => "intersects",
64
- "shape" => {
65
- "type" => "Point",
66
- "coordinates" => [-122.39455,37.7841]
67
- }
68
- }
69
- }
70
- }
71
- }
72
- }
73
- })
74
-
75
- json = geo_query.send(:execute!)
76
-
77
- json['hits'].should be_a Hash
78
- json['hits']['total'].should eql 3
79
- json['hits']['hits'].size.should eql 3
80
-
81
- geo_query.prepare({
82
- "query" => {
83
- "filtered" => {
84
- "query" => { "match_all" => {} },
85
- "filter" => {
86
- "geo_shape" => {
87
- "work_area" => {
88
- "relation" => "intersects",
89
- "shape" => {
90
- "type" => "Point",
91
- "coordinates" => [-122.3859222222222,37.78292222222222]
92
- }
93
- }
94
- }
95
- }
96
- }
97
- }
98
- })
99
-
100
- json = geo_query.send(:execute!)
101
-
102
- json['hits'].should be_a Hash
103
- json['hits']['total'].should eql 1
104
- json['hits']['hits'].size.should eql 1
105
- end
106
-
107
- def add_result!
108
- index.store!("task_123123", {id: 123123, name: 'some shopping in ikea', user_id: 999, description: 'i need you to pick up some stuff in ikea', internal: false})
109
- index.store!("task_234234", {id: 234234, name: "some shopping in ikea and trader joe's", user_id: 987, description: 'pick me up some eggs', internal: true})
110
- index.refresh
111
- end
112
-
113
- def add_geo_results!
114
- geo_index.store!("rabbit_1", {
115
- id: "rabbit_1",
116
- work_area: {
117
- type: 'polygon',
118
- coordinates: [[
119
- [-122.4119,37.78211],
120
- [-122.39285,37.79649],
121
- [-122.37997,37.78415],
122
- [-122.39817,37.77248],
123
- [-122.40932,37.77302],
124
- [-122.4119,37.78211]
125
- ]]
126
- }
127
- })
128
-
129
- geo_index.store!("rabbit_2", {
130
- id: "rabbit_2",
131
- work_area: {
132
- type: 'polygon',
133
- coordinates: [[
134
- [-122.41087,37.78822],
135
- [-122.43174,37.78578],
136
- [-122.42816,37.75938],
137
- [-122.38787,37.76882],
138
- [-122.39199,37.78584],
139
- [-122.41087,37.78822]
140
- ]]
141
- }
142
- })
143
-
144
- geo_index.store!("rabbit_3", {
145
- id: "rabbit_3",
146
- work_area: {
147
- type: 'polygon',
148
- coordinates: [[
149
- [-122.41997,37.79744],
150
- [-122.39576,37.79975],
151
- [-122.38461,37.78469],
152
- [-122.40294,37.77738],
153
- [-122.41997,37.79744]
154
- ]]
155
- }
156
- })
157
-
158
- geo_index.refresh
159
- end
160
-
161
- end