endeca 1.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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