drawbridge 0.2.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.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Michael Hoitomt
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,52 @@
1
+ # Drawbridge
2
+
3
+ [![Code Climate](https://codeclimate.com/repos/51d44e1c13d637371b0595e6/badges/f566eeb7ce85f64716ec/gpa.png)](https://codeclimate.com/repos/51d44e1c13d637371b0595e6/feed)
4
+
5
+ It draws the bridge between Endeca JSON bridge and IDG sites.
6
+
7
+ Sometimes it's up, sometimes it's down.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'drawbridge'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install drawbridge
22
+
23
+ ## Setup
24
+
25
+ To use:
26
+
27
+ ```ruby
28
+ require 'drawbridge'
29
+
30
+ Drawbridge.setup do |config|
31
+ config.bridge_url = "http://example.com:8080"
32
+ config.bridge_path = "BridgeAPIService_apt64/servlet/bridgeapi.json"
33
+ # e.g. ENDECA_DEBUG=true rackup
34
+ config.endeca_debug = ENV.fetch('ENDECA_DEBUG') { false }
35
+ # optional, default is 5
36
+ config.timeout = 5
37
+ # optional, default is to change ' into ' before JSON is parsed
38
+ config.skip_single_quote_encoding = true
39
+ end
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ [Endeca documentation here](https://github.com/primedia/idg/wiki/Endeca)
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ task :default => :test
5
+ task :spec => :test
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'spec'
9
+ t.test_files = FileList['spec/*_spec.rb']
10
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'drawbridge/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "drawbridge"
8
+ gem.version = Drawbridge::VERSION
9
+ gem.authors = ["Michael Hoitomt"]
10
+ gem.email = ["mhoitomt@primedia.com"]
11
+ gem.description = %q{Papi's Wife}
12
+ gem.summary = %q{Papi's Replacement}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency('turn', '~> 0.8.3')
21
+ gem.add_development_dependency('mocha', '~> 0.13.0')
22
+ gem.add_development_dependency('rake')
23
+
24
+ gem.add_dependency('curb', '~> 0.8.4')
25
+ gem.add_dependency('oj', '~> 2.0.14')
26
+ gem.add_dependency('activesupport', '>= 3.2.9')
27
+ end
data/lib/drawbridge.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'drawbridge/version'
3
+ require 'drawbridge/config'
4
+ require 'drawbridge/debug'
5
+ require 'drawbridge/transformer'
6
+ require 'drawbridge/adapter'
7
+ require 'drawbridge/mapper'
8
+ require 'drawbridge/request'
9
+ require 'drawbridge/result'
10
+ require 'drawbridge/refinement_scrubber'
11
+
12
+ module Drawbridge
13
+ end
@@ -0,0 +1,35 @@
1
+ module Drawbridge
2
+ class Adapter
3
+ include Drawbridge::Transformer
4
+
5
+ attr_accessor :mapper
6
+
7
+ def initialize(mapper)
8
+ @mapper = mapper
9
+ end
10
+
11
+ def search_path
12
+ "#{Drawbridge.config.bridge_url}/#{Drawbridge.config.bridge_path}"
13
+ end
14
+
15
+ def build_all_query
16
+ params = {
17
+ select: mapper.fields,
18
+ where: mapper.record_search,
19
+ record_filters: mapper.record_filters,
20
+ root_navigtion: mapper.navigation,
21
+ expand_refinements: mapper.dimensions,
22
+ geo_filter: mapper.geo_filters,
23
+ range_filter: mapper.range_filter,
24
+ rollup: mapper.rollup_key,
25
+ match_mode: mapper.mode,
26
+ aggregate_offset: mapper.aggregate_offset,
27
+ offset: mapper.offset,
28
+ limit: mapper.limit_param,
29
+ order: mapper.sort_key
30
+ }
31
+ transform(params)
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ module Drawbridge
2
+ class Config
3
+ attr_accessor :data
4
+ def initialize(data={})
5
+ @data = {}
6
+ update!(data)
7
+ end
8
+
9
+ def update!(data)
10
+ data.each do |key, value|
11
+ self[key.to_sym] = value
12
+ end
13
+ end
14
+
15
+ def [](key)
16
+ @data[key.to_sym]
17
+ end
18
+
19
+ def []=(key, value)
20
+ @data[key.to_sym] = value
21
+ end
22
+
23
+ def keys
24
+ @data.keys
25
+ end
26
+
27
+ def to_hash
28
+ @data
29
+ end
30
+
31
+ def method_missing(sym, *args)
32
+ if sym.to_s =~ /(.+)=$/
33
+ self[$1] = args.first
34
+ else
35
+ self[sym]
36
+ end
37
+ end
38
+ end
39
+
40
+ class << self
41
+ attr_accessor :config, :data
42
+
43
+ def setup
44
+ self.config ||= Config.new
45
+ yield config
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Drawbridge
3
+ class Debug
4
+
5
+ def self.draw_line
6
+ @draw_line ||= "*"*80
7
+ end
8
+
9
+ # @params prepend_text: Endeca_Params
10
+ # @params debug_value: any object thaat can be inspected
11
+ def self.log(prepend_text, debug_value)
12
+ if Drawbridge.config.endeca_debug
13
+ puts "#{draw_line}\n#{prepend_text}: #{debug_value.inspect}\n#{draw_line}"
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,214 @@
1
+ module Drawbridge
2
+ class Mapper
3
+
4
+
5
+ # Class Level accessor - Makes pretty
6
+ class << self
7
+ def where(params)
8
+ self.new.where(params)
9
+ end
10
+ end
11
+
12
+ def initialize
13
+ end
14
+
15
+ def where(params)
16
+ params.each do |k, v|
17
+ # regex should handle md5hash, and anything that doesn't have word characters
18
+ unless k[/\W|^\h+$/]
19
+ Mapper.send(:attr_accessor, k)
20
+ instance_variable_set("@#{k}", v)
21
+ end
22
+ end
23
+ self
24
+ end
25
+
26
+ def order_by(sort_string)
27
+ @sort_string = sort_string
28
+ self
29
+ end
30
+
31
+ def sort_args
32
+ return [] unless @sort_string
33
+ sort_string = @sort_string.split(',').join('^+^')
34
+ sort_array = parse_sort(sort_string.gsub('-', '^'))
35
+ sort_array
36
+ end
37
+
38
+ def all
39
+ adapter = Drawbridge::Adapter.new(self)
40
+ query = adapter.build_all_query
41
+ result_params = Request.perform adapter.search_path, query
42
+ Result.new(result_params)
43
+ end
44
+
45
+ def first
46
+ results = all
47
+ results.records.empty? ? nil : results.records.first
48
+ end
49
+
50
+ def default_refinements
51
+ []
52
+ end
53
+
54
+ # N
55
+ def navigation
56
+ (process_refinements(endeca_refinements) + default_refinements).flatten
57
+ end
58
+
59
+ # Ne - placeholder, might make more sense to hard-code in the adapter
60
+ def exposed_refinements
61
+ dimensions
62
+ end
63
+
64
+ # Ntt and Ntk
65
+ def record_search
66
+ record_key_values
67
+ end
68
+
69
+ # Ns
70
+ def sort_key
71
+ combined_sort
72
+ end
73
+
74
+ # Nr
75
+ def record_filters
76
+ []
77
+ end
78
+
79
+ # Nf
80
+ def range_filter
81
+ []
82
+ end
83
+
84
+ # Nf
85
+ def geo_filters
86
+ {}
87
+ end
88
+
89
+ # Nu
90
+ def rollup_key
91
+ ""
92
+ end
93
+
94
+ # No
95
+ def offset
96
+ page_param.to_i * limit_param.to_i
97
+ end
98
+
99
+ # Nao
100
+ def aggregate_offset
101
+ page_param.to_i * limit_param.to_i
102
+ end
103
+
104
+ def page_param
105
+ @page ? @page.to_i - 1 : 0
106
+ end
107
+
108
+ def limit_param
109
+ @limit ||= 20
110
+ @limit.to_i
111
+ end
112
+
113
+ def radius_param
114
+ @radius ||= @rad
115
+ end
116
+
117
+ def combined_sort
118
+ (base_sort + sort_args).flatten.uniq
119
+ end
120
+
121
+ def base_sort
122
+ []
123
+ end
124
+
125
+ def record_key_values
126
+ {}
127
+ end
128
+
129
+ def dimensions
130
+ []
131
+ end
132
+
133
+ def dimensions=(dimension_list)
134
+ dimension_list
135
+ end
136
+
137
+ def fields
138
+ []
139
+ end
140
+
141
+ def fields=(field_list)
142
+ field_list
143
+ end
144
+
145
+ def method_missing(meth)
146
+ nil
147
+ end
148
+
149
+ def name
150
+ ""
151
+ end
152
+
153
+ def mode
154
+ []
155
+ end
156
+
157
+ # converts refinements to int ids if they were base(36) encoded before
158
+ # @params - Array
159
+ # ["4j7"] => ['5875']
160
+ def process_refinements(refinements)
161
+ return [] if refinements.blank?
162
+
163
+ refinements.collect! do |ref|
164
+ ref.to_i(36).to_s
165
+ end
166
+ end
167
+
168
+ def parse_refinements(refinements)
169
+ refinements.collect! do |ref|
170
+ ref.to_s[/^\d+$/] ? ref.to_s : ref.to_i(36).to_s
171
+ end
172
+ end
173
+
174
+ protected
175
+
176
+ def refinements
177
+ @refinements ||= ""
178
+ end
179
+
180
+ def endeca_refinements
181
+ return [] if @refinements.nil?
182
+ split_out_dimension_codes(@refinements.dup) || []
183
+ end
184
+
185
+ def split_out_dimension_codes(refs)
186
+ nodes = refs.split('-').last
187
+ nodes = nodes.split(/\+|\s/) if nodes
188
+ nodes == ['miles'] ? nil : nodes
189
+ end
190
+
191
+ def parse_sort(sort)
192
+ return [] if sort.nil? || sort.empty?
193
+ [].tap do |a|
194
+ aray_sort = sort.split('^+^')
195
+ aray_sort.each do |s|
196
+ key, val = s.split('^')
197
+ a << {key => endeca_sort_value(val)}
198
+ end
199
+ end
200
+ end
201
+
202
+ def endeca_sort_value(value)
203
+ case value
204
+ when "asc"
205
+ return "0"
206
+ when "desc"
207
+ return "1"
208
+ else
209
+ return value
210
+ end
211
+ end
212
+
213
+ end
214
+ end