endeca 1.3.7

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.
@@ -0,0 +1,93 @@
1
+ module Endeca
2
+ class ReaderError < ::StandardError; end
3
+
4
+ module Readers
5
+ module ClassMethods
6
+ def add_reader(name, &block)
7
+ meta = (class << self; self; end)
8
+ meta.instance_eval do
9
+ define_method(name) { |*attrs| reader(*attrs, &block) }
10
+ end
11
+ end
12
+
13
+ # Maps key/value pairs from the data structure used to initialize a
14
+ # Endeca object. Allows attribute renaming. Use a block to perform data
15
+ # injunction on the value as it is set.
16
+ #
17
+ # ==== Examples
18
+ #
19
+ # # Specify basic attributes
20
+ # reader :title
21
+ #
22
+ # # Attribute renaming
23
+ # reader :long_desc => :description
24
+ #
25
+ # # Data injunction
26
+ # reader(:title => :upcased_title) { |title| title.upcase }
27
+ def reader(*attrs,&block)
28
+ hash = {}
29
+ block ||= lambda {|x| x}
30
+
31
+ hash.update(attrs.pop) if Hash === attrs.last
32
+
33
+ attrs.each{ |attr| hash[attr] = attr }
34
+
35
+ hash.each do |variable, method|
36
+ reader_names << method if respond_to?(:reader_names)
37
+ define_method(method) do
38
+ begin
39
+ block.call(attributes[variable.to_s])
40
+ rescue StandardError => e
41
+ raise Endeca::ReaderError, e.message
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ # Typecasts attributes as integers.
48
+ #
49
+ # ==== Examples
50
+ # integer_reader :id, :rating
51
+ def integer_reader(*attrs)
52
+ reader(*attrs) { |value| value.blank? ? 0 : Integer(value) }
53
+ end
54
+
55
+ # Typecasts attributes as BigDecimal
56
+ #
57
+ # ==== Examples
58
+ # decimal_reader :price
59
+ def decimal_reader(*attrs)
60
+ require 'bigdecimal' unless defined?(BigDecimal)
61
+ reader(*attrs) { |value| BigDecimal(value.to_s) }
62
+ end
63
+
64
+ # Typecasts attributes as floats
65
+ #
66
+ # ==== Examples
67
+ # float_reader :latitude, :longitude
68
+ def float_reader(*attrs)
69
+ reader(*attrs) { |value| Float(value) if value }
70
+ end
71
+
72
+ # Typecasts attributes as a Perly boolean ("0" == false, "1" == true")
73
+ #
74
+ # ==== Examples
75
+ # boolean_reader :price
76
+ def boolean_reader(*attrs)
77
+ reader(*attrs) { |value| value == "1" ? true : false }
78
+ end
79
+
80
+ end
81
+
82
+ module InstanceMethods
83
+ def method_missing(method_id)
84
+ raise NoMethodError, "undefined method '#{method_id}' for #{self.inspect}. Do you need to add a reader for it?"
85
+ end
86
+ end
87
+
88
+ def self.included(receiver)
89
+ receiver.extend ClassMethods
90
+ receiver.send :include, InstanceMethods
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,42 @@
1
+ module Endeca
2
+ class Refinement
3
+ include Readers
4
+ extend ClassToProc
5
+
6
+ reader 'DimensionName' => :name,
7
+ 'ExpansionLink' => :expansion_link,
8
+ 'ContractionLink' => :contraction_link
9
+
10
+ integer_reader 'DimensionID' => :id
11
+
12
+ reader('DimensionValues' => :dimension_values) do |values|
13
+ values.map(&Dimension) if values
14
+ end
15
+
16
+ reader('Dimensions' => :dimensions) do |values|
17
+ values.map(&RefinementDimension) if values
18
+ end
19
+
20
+ attr_reader :raw
21
+ def initialize(raw={})
22
+ @raw = raw
23
+ end
24
+
25
+ def ==(other)
26
+ id == other.id
27
+ end
28
+
29
+ def inspect
30
+ "#<#{self.class}=0x#{self.object_id.to_s(16)} id=#{id} name=#{name.inspect}>"
31
+ end
32
+
33
+ def attributes
34
+ (@raw['Dimensions'] || []).first || {}
35
+ end
36
+
37
+ def to_endeca_params
38
+ expansion_link || contraction_link
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,32 @@
1
+ module Endeca
2
+ class RefinementDimension
3
+ include Comparable
4
+ include Readers
5
+ extend ClassToProc
6
+
7
+ reader \
8
+ "DimensionName" => :name,
9
+ "ExpansionLink" => :to_endeca_params
10
+
11
+ integer_reader \
12
+ "DimensionID" => :id
13
+
14
+ attr_reader :raw
15
+ def initialize(raw={})
16
+ @raw=raw
17
+ end
18
+ alias_method :attributes, :raw
19
+
20
+ def inspect
21
+ "#<#{self.class}=0x#{self.object_id.to_s(16)} id=#{id} name=#{name.inspect}>"
22
+ end
23
+
24
+ def ==(other)
25
+ id == other.id
26
+ end
27
+
28
+ def <=>(other)
29
+ name <=> other.name
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,88 @@
1
+ require 'uri'
2
+
3
+ module Endeca
4
+ class RequestError < ::StandardError; end
5
+
6
+ class Request
7
+ def self.perform(path, query=nil)
8
+ raise RequestError, "Must provide a path" unless path
9
+ new(path, query).perform
10
+ end
11
+
12
+ def initialize(path, query=nil)
13
+ @path = path.strip
14
+ @query = query
15
+ end
16
+
17
+ def perform
18
+ raise RequestError, endeca_error[:message] if endeca_error?
19
+ Endeca.increase_metric(:request_count, 1)
20
+ return response
21
+ end
22
+
23
+ def response
24
+ @response ||= handle_response(get_response)
25
+ end
26
+
27
+ def uri
28
+ return @uri if @uri
29
+
30
+ @uri = URI.parse(@path)
31
+ @uri.query = query_string unless !@query || @query.include?("/_/")
32
+ @uri = URI.parse("#{@path}#{@query}") if @query && @query.include?("/_/")
33
+ @uri
34
+ end
35
+
36
+ private
37
+
38
+ def endeca_error
39
+ method_response = response["methodResponse"]
40
+ fault = method_response && method_response["fault"]
41
+ values = fault && fault["value"]
42
+ return nil unless values
43
+ {
44
+ :code => values["faultCode"].to_i,
45
+ :message => values["faultString"]
46
+ }
47
+ end
48
+
49
+ def endeca_error?
50
+ !endeca_error.nil?
51
+ end
52
+
53
+ def get_response #:nodoc:
54
+ Endeca.log "ENDECA ADAPTER REQUEST"
55
+ Endeca.log " parameters => " + @query.inspect
56
+ Endeca.log " uri => " + uri.to_s
57
+ Endeca.bm(:request_time, "#{@path} #{@query.inspect}") do
58
+ Endeca.timer.timeout(Endeca.timeout) do
59
+ @response = Net::HTTP.get_response(uri)
60
+ end
61
+ end
62
+
63
+ return @response
64
+ end
65
+
66
+ # Raises exception Net::XXX (http error code) if an http error occured
67
+ def handle_response(response) #:nodoc:
68
+ case response
69
+ when Net::HTTPSuccess
70
+ Endeca.bm :parse_time do
71
+ @json = JSON.parse(response.body)
72
+ end
73
+ else
74
+ response.error! # raises exception corresponding to http error Net::XXX
75
+ end
76
+
77
+ rescue => e
78
+ raise RequestError, e.message
79
+ end
80
+
81
+ def query_string
82
+ query_string_parts = [@uri.query, @query.to_endeca_params]
83
+ query_string_parts.reject!{ |s| s.nil? || s.empty? }
84
+
85
+ query_string_parts.empty? ? nil : query_string_parts.join('&')
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,43 @@
1
+ module Endeca
2
+ module Transformer
3
+ # Requires existence of mappings accessor (Hash)
4
+ #
5
+ # ==== Examples
6
+ # # Standard map call that returns an Endeca::Map object
7
+ # map(:old_name => :new_name)
8
+ #
9
+ # # Allows to to create a map object to perform other functionality such
10
+ # # as transformations.
11
+ # map(:new_name)
12
+ def map(mapping = {})
13
+ mapping = {mapping => mapping} if Symbol === mapping
14
+ mapping = mapping.symbolize_keys
15
+
16
+ if mapping.length > 1
17
+ raise ArgumentError, "map only accepts one key=>value pair"
18
+ end
19
+
20
+ mapping.each do |key, transformed_key|
21
+ transformed_key = key unless transformed_key
22
+ return mappings[key] = Map.new(key, transformed_key)
23
+ end
24
+ end
25
+
26
+ # Use the mappings hash to replace domain level query query_options with
27
+ # their Endeca equivalent.
28
+ def transform_query_options(query_options)
29
+ # {"apartments" => true}
30
+ query = query_options.symbolize_keys
31
+ # {:apartments => true}
32
+ query.each do |key, _|
33
+ if mapping = mappings[key]
34
+ new_options = mapping.perform(query)
35
+ query.delete(key)
36
+ query.update(new_options)
37
+ end
38
+ end
39
+ query
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,134 @@
1
+ require File.join(File.dirname(__FILE__), %w[spec_helper])
2
+
3
+ describe Array do
4
+ describe "#to_endeca_params" do
5
+ it "should join the elements with ampersand" do
6
+ ["foo=1","bar=3"].to_endeca_params.should == "foo=1&bar=3"
7
+ end
8
+
9
+ it "should escape all elements" do
10
+ ['|=|','||=||'].to_endeca_params.should == '%7C=%7C&%7C%7C=%7C%7C'
11
+ end
12
+ end
13
+ end
14
+
15
+ describe Benchmark do
16
+ describe ".realtime" do
17
+ it "should check the time twice" do
18
+ Time.should_receive(:now).exactly(2).times
19
+ Benchmark.realtime(){}
20
+ end
21
+
22
+ it "should return the time difference as a float" do
23
+ Benchmark.realtime(){}.should be_a_kind_of(Float)
24
+ end
25
+ end
26
+
27
+ describe ".ms" do
28
+ it "should be 1000 times the realtime value" do
29
+ Benchmark.stub!(:realtime).and_return(1)
30
+ Benchmark.ms.should == 1000
31
+ end
32
+ end
33
+ end
34
+
35
+ describe Hash do
36
+ describe "#to_endeca_params" do
37
+ it "should join a key-value pair with equals" do
38
+ {:foo => :bar}.to_endeca_params.should == 'foo=bar'
39
+ end
40
+
41
+ it "should join two key-value pairs with ampersand" do
42
+ result = {:foo => :bar, :bizz => :bazz}.to_endeca_params
43
+ (result == 'foo=bar&bizz=bazz' || result == 'bizz=bazz&foo=bar').should be_true
44
+ end
45
+
46
+ it "should use brackets to indicate a nested hash" do
47
+ {:foo => {:foo => :bar}}.to_endeca_params.should == 'foo[foo]=bar'
48
+ end
49
+
50
+ it "should escape all elements" do
51
+ {'|' => '||'}.to_endeca_params.should == '%7C=%7C%7C'
52
+ end
53
+ end
54
+ end
55
+
56
+ describe NilClass do
57
+ describe "to_endeca_params" do
58
+ it "should return the empty string" do
59
+ nil.to_endeca_params.should == ''
60
+ end
61
+ end
62
+ end
63
+
64
+ describe String do
65
+ describe "#to_endeca_params" do
66
+ it "should URI escape the contents" do
67
+ '|'.to_endeca_params.should == '%7C'
68
+ end
69
+
70
+ it "should URI escape a colon" do
71
+ ':'.to_endeca_params.should == "%3A"
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "#inherited_accessor" do
77
+
78
+ class SomeParent
79
+ inherited_accessor :a_hash, {:m => "my_value"}
80
+ inherited_accessor :a_string, "fibby foo"
81
+ inherited_accessor :a_array, ["atlanta"]
82
+ end
83
+
84
+ class A < SomeParent; end
85
+ class B < SomeParent; end
86
+
87
+ before do
88
+ A.a_hash[:a] = "a_value"
89
+ A.a_string = "a_string"
90
+
91
+ B.a_hash[:a] = "b_value"
92
+ B.a_string = "b_string"
93
+ end
94
+
95
+ it "should retrieve default value" do
96
+ SomeParent.a_hash[:m].should == "my_value"
97
+ SomeParent.a_string.should == "fibby foo"
98
+ end
99
+
100
+ it "should retrieve A hash value correctly" do
101
+ A.a_hash[:a].should == "a_value"
102
+ end
103
+
104
+ it "should retrieve A string value correctly" do
105
+ A.a_string.should == "a_string"
106
+ end
107
+
108
+ it "should retrieve A array value correctly" do
109
+ original = A.a_array
110
+
111
+ A.a_array << "jacksonville"
112
+ A.a_array.should == ["atlanta", "jacksonville"]
113
+
114
+ A.a_array = original
115
+ end
116
+
117
+ it "should retrieve B hash value correctly" do
118
+ B.a_hash[:a].should == "b_value"
119
+ end
120
+
121
+ it "should retrieve B string value correctly" do
122
+ B.a_string.should == "b_string"
123
+ end
124
+
125
+ it "should retrieve B array value correctly" do
126
+ original = B.a_array
127
+
128
+ B.a_array << "new york"
129
+ B.a_array.should == ["atlanta", "new york"]
130
+
131
+ B.a_array = original
132
+ end
133
+ end
134
+ # EOF
@@ -0,0 +1,33 @@
1
+ require File.join(File.dirname(__FILE__), %w[.. spec_helper])
2
+
3
+ describe Endeca::Benchmarking do
4
+ class Helper
5
+ extend Endeca::Benchmarking
6
+ end
7
+
8
+ describe "#benchmark" do
9
+ before do
10
+ @logger = mock('Logger')
11
+
12
+ Endeca.stub!(:logger).and_return(@logger)
13
+ Endeca.stub!(:debug => true, :benchmark => true)
14
+
15
+ Benchmark.stub!(:ms => 1)
16
+ end
17
+
18
+ it "should log the title and the time to the Endeca logger" do
19
+ @logger.should_receive(:debug).with("metric: 1.0ms")
20
+ Endeca.bm(:metric){ 1 }
21
+ end
22
+ end
23
+
24
+ describe "#add_bm_detail" do
25
+ it "should add info to the current thread" do
26
+ Endeca.stub!(:analyze?).and_return(true)
27
+
28
+ Endeca.send(:add_bm_detail, :metric, 1.1, 'query query')
29
+
30
+ Thread.current[:endeca]["metric_detail"][0].should == {:detail => "query query", :time => 1.1}
31
+ end
32
+ end
33
+ end