CloudSesame 0.6.8 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +12 -0
- data/.gitignore +1 -1
- data/Gemfile.lock +13 -13
- data/Guardfile +1 -0
- data/cloud_sesame.gemspec +2 -2
- data/coverage/.last_run.json +1 -1
- data/coverage/.resultset.json +679 -591
- data/lib/cloud_sesame/config/credential.rb +6 -6
- data/lib/cloud_sesame/context.rb +0 -5
- data/lib/cloud_sesame/domain/base.rb +147 -128
- data/lib/cloud_sesame/domain/client.rb +25 -25
- data/lib/cloud_sesame/domain/client_module/caching/base.rb +19 -23
- data/lib/cloud_sesame/domain/client_module/caching/no_cache.rb +11 -11
- data/lib/cloud_sesame/domain/client_module/caching/rails_cache.rb +32 -28
- data/lib/cloud_sesame/domain/client_module/caching.rb +19 -28
- data/lib/cloud_sesame/domain/client_module/retry.rb +6 -6
- data/lib/cloud_sesame/domain/config.rb +6 -6
- data/lib/cloud_sesame/query/ast/abstract/multi_expression_operator.rb +37 -0
- data/lib/cloud_sesame/query/ast/abstract/operator.rb +27 -0
- data/lib/cloud_sesame/query/ast/abstract/single_expression_operator.rb +39 -0
- data/lib/cloud_sesame/query/ast/abstract/value.rb +88 -0
- data/lib/cloud_sesame/query/ast/and.rb +1 -1
- data/lib/cloud_sesame/query/ast/date_value.rb +19 -3
- data/lib/cloud_sesame/query/ast/literal.rb +13 -28
- data/lib/cloud_sesame/query/ast/near.rb +4 -4
- data/lib/cloud_sesame/query/ast/not.rb +6 -7
- data/lib/cloud_sesame/query/ast/numeric_value.rb +20 -12
- data/lib/cloud_sesame/query/ast/or.rb +1 -1
- data/lib/cloud_sesame/query/ast/phrase.rb +1 -1
- data/lib/cloud_sesame/query/ast/prefix.rb +1 -1
- data/lib/cloud_sesame/query/ast/range_value.rb +40 -39
- data/lib/cloud_sesame/query/ast/root.rb +2 -6
- data/lib/cloud_sesame/query/ast/string_value.rb +26 -0
- data/lib/cloud_sesame/query/ast/term.rb +1 -1
- data/lib/cloud_sesame/query/ast/value.rb +25 -54
- data/lib/cloud_sesame/query/builder.rb +83 -54
- data/lib/cloud_sesame/query/domain/block.rb +3 -1
- data/lib/cloud_sesame/query/dsl/applied_filter_query.rb +14 -23
- data/lib/cloud_sesame/query/dsl/field_accessors.rb +25 -22
- data/lib/cloud_sesame/query/dsl/field_array_methods.rb +1 -1
- data/lib/cloud_sesame/query/dsl/inspect_method.rb +31 -0
- data/lib/cloud_sesame/query/dsl/sort_methods.rb +5 -4
- data/lib/cloud_sesame/query/node/abstract.rb +1 -2
- data/lib/cloud_sesame/query/node/facet.rb +1 -5
- data/lib/cloud_sesame/query/node/filter_query.rb +15 -2
- data/lib/cloud_sesame/query/node/fuzziness.rb +4 -3
- data/lib/cloud_sesame/query/node/page.rb +2 -2
- data/lib/cloud_sesame/query/node/query.rb +4 -17
- data/lib/cloud_sesame/query/node/query_options.rb +6 -16
- data/lib/cloud_sesame/query/node/query_options_field.rb +3 -1
- data/lib/cloud_sesame/query/node/query_parser.rb +8 -10
- data/lib/cloud_sesame/query/node/request.rb +57 -31
- data/lib/cloud_sesame/query/node/sort.rb +25 -11
- data/lib/cloud_sesame.rb +15 -12
- data/lib/{abstract_object.rb → services/abstract_object.rb} +0 -0
- data/lib/services/class_specific.rb +44 -0
- data/lib/services/lazy_object.rb +19 -0
- data/spec/cloud_sesame/domain/base_spec.rb +26 -11
- data/spec/cloud_sesame/domain/client_module/caching/base_spec.rb +2 -1
- data/spec/cloud_sesame/domain/client_module/caching/no_cache_spec.rb +2 -3
- data/spec/cloud_sesame/domain/client_module/caching/rails_cache_spec.rb +6 -6
- data/spec/cloud_sesame/domain/client_module/caching_spec.rb +33 -32
- data/spec/cloud_sesame/query/ast/abstract/multi_expression_operator_spec.rb +67 -0
- data/spec/cloud_sesame/query/ast/abstract/operator_spec.rb +29 -0
- data/spec/cloud_sesame/query/ast/abstract/single_expression_operator_spec.rb +70 -0
- data/spec/cloud_sesame/query/ast/abstract/value_spec.rb +118 -0
- data/spec/cloud_sesame/query/ast/and_spec.rb +1 -1
- data/spec/cloud_sesame/query/ast/or_spec.rb +1 -1
- data/spec/cloud_sesame/query/ast/range_value_spec.rb +5 -11
- data/spec/cloud_sesame/query/builder_spec.rb +10 -4
- data/spec/cloud_sesame/query/domain/block_spec.rb +15 -8
- data/spec/cloud_sesame/query/dsl/{block_methods_spec.rb → block_styled_operators_spec.rb} +0 -0
- data/spec/cloud_sesame/query/dsl/field_accessors_spec.rb +88 -50
- data/spec/cloud_sesame/query/dsl/field_array_methods_spec.rb +0 -2
- data/spec/cloud_sesame/query/node/facet_spec.rb +1 -15
- data/spec/cloud_sesame/query/node/filter_query_spec.rb +16 -2
- data/spec/cloud_sesame/query/node/page_spec.rb +3 -2
- data/spec/cloud_sesame/query/node/query_options_spec.rb +1 -1
- data/spec/cloud_sesame/query/node/query_parser_spec.rb +1 -7
- data/spec/cloud_sesame/query/node/query_spec.rb +10 -28
- data/spec/cloud_sesame/query/node/sort_spec.rb +19 -28
- data/spec/cloud_sesame_spec.rb +2 -156
- data/spec/helpers/benchmark_helper.rb +12 -0
- data/spec/integration/filter_query_spec.rb +0 -0
- data/spec/profiling_spec.rb +155 -0
- data/spec/{abstract_object_spec.rb → services/abstract_object_spec.rb} +0 -0
- data/spec/services/class_specific_spec.rb +135 -0
- data/spec/spec_helper.rb +3 -0
- metadata +32 -22
- data/lib/active_support/core_ext/object/deep_dup.rb +0 -53
- data/lib/active_support/core_ext/object/duplicable.rb +0 -98
- data/lib/cloud_sesame/query/ast/multi_expression_operator.rb +0 -35
- data/lib/cloud_sesame/query/ast/operator.rb +0 -25
- data/lib/cloud_sesame/query/ast/single_expression_operator.rb +0 -35
- data/lib/cloud_sesame/query/scope.rb +0 -21
- data/profiler.rb +0 -91
- data/spec/cloud_sesame/query/ast/multi_expression_operator_spec.rb +0 -76
- data/spec/cloud_sesame/query/ast/operator_spec.rb +0 -29
- data/spec/cloud_sesame/query/ast/single_expression_operator_spec.rb +0 -78
- data/spec/context_spec.rb +0 -17
@@ -5,30 +5,12 @@ module CloudSesame
|
|
5
5
|
module Node
|
6
6
|
describe Sort do
|
7
7
|
|
8
|
-
let(:
|
9
|
-
let(:node) { Sort.new(
|
10
|
-
|
11
|
-
describe '#initialize' do
|
12
|
-
context 'when arguments passed in' do
|
13
|
-
let(:arguments) {{ sort: "score asc,price desc" }}
|
14
|
-
it 'should accept sort argument and initialize sorting_attributes' do
|
15
|
-
expect(node.sorting_attributes).to eq({ score: :asc, price: :desc })
|
16
|
-
end
|
17
|
-
it 'should initialize an empty sorting_attributes when arguments sort is empty' do
|
18
|
-
arguments[:sort] = ""
|
19
|
-
expect(node.sorting_attributes).to eq({ })
|
20
|
-
end
|
21
|
-
end
|
22
|
-
context 'when arguments not passed in' do
|
23
|
-
it 'should initialize an empty sorting_attributes' do
|
24
|
-
expect(node.sorting_attributes).to eq({ })
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
8
|
+
let(:context) {{ fields: {} }}
|
9
|
+
let(:node) { Sort.new(context) }
|
28
10
|
|
29
11
|
describe '#[]' do
|
30
12
|
context 'with existing attribute' do
|
31
|
-
|
13
|
+
before { node.attributes = { score: :asc, price: :desc }}
|
32
14
|
it 'should return the sorting order of the attribute' do
|
33
15
|
expect(node[:score]).to eq :asc
|
34
16
|
expect(node[:price]).to eq :desc
|
@@ -44,7 +26,7 @@ module CloudSesame
|
|
44
26
|
|
45
27
|
describe '#[]=' do
|
46
28
|
context 'with existing attribute' do
|
47
|
-
|
29
|
+
before { node.attributes = { score: :asc, price: :desc }}
|
48
30
|
it 'should override the sorting order of the attribute' do
|
49
31
|
expect{ node[:score] = :desc }.to change{ node[:score] }.from(:asc).to(:desc)
|
50
32
|
end
|
@@ -56,16 +38,25 @@ module CloudSesame
|
|
56
38
|
end
|
57
39
|
end
|
58
40
|
|
59
|
-
describe '#
|
41
|
+
describe '#compile' do
|
60
42
|
context 'with existing sorting attributes' do
|
61
|
-
|
62
|
-
|
63
|
-
|
43
|
+
context 'when context contains aliase field' do
|
44
|
+
let(:context) {{ fields: { aliased: { as: :real_name } } }}
|
45
|
+
before { node.attributes = { aliased: :asc, price: :desc }}
|
46
|
+
it 'should return serialized sort attributes with actual field name' do
|
47
|
+
expect(node.compile).to eq "real_name asc,price desc"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
context 'when context contains no alias field' do
|
51
|
+
before { node.attributes = { score: :asc, price: :desc }}
|
52
|
+
it 'should return serialized sort attributes' do
|
53
|
+
expect(node.compile).to eq "score asc,price desc"
|
54
|
+
end
|
64
55
|
end
|
65
56
|
end
|
66
57
|
context 'with empty sorting attributes' do
|
67
|
-
it 'should return
|
68
|
-
expect(node.compile).to eq
|
58
|
+
it 'should return nil' do
|
59
|
+
expect(node.compile).to eq nil
|
69
60
|
end
|
70
61
|
end
|
71
62
|
end
|
data/spec/cloud_sesame_spec.rb
CHANGED
@@ -1,158 +1,4 @@
|
|
1
|
-
|
2
|
-
# require 'ruby-prof'
|
3
|
-
# require 'benchmark'
|
1
|
+
describe CloudSesame do
|
4
2
|
|
5
|
-
# describe CloudSesame do
|
6
3
|
|
7
|
-
|
8
|
-
# # =====================================
|
9
|
-
# class ::Rails
|
10
|
-
# def self.cache
|
11
|
-
# @cache ||= FakeCache.new
|
12
|
-
# end
|
13
|
-
# end
|
14
|
-
|
15
|
-
# class ::FakeCache
|
16
|
-
# def table
|
17
|
-
# @table ||= {}
|
18
|
-
# end
|
19
|
-
# def fetch(key, &block)
|
20
|
-
# table[key] ||= (block.call if block_given?)
|
21
|
-
# end
|
22
|
-
# end
|
23
|
-
|
24
|
-
# # AWS initializer
|
25
|
-
# # =======================================================
|
26
|
-
# require 'yaml'
|
27
|
-
# YAML.load_file('aws.yml').each do |key, value|
|
28
|
-
# ENV["AWS_#{ key }"] = value
|
29
|
-
# end
|
30
|
-
|
31
|
-
# # Domain Initializer /config/initializers/cloudsearch.rb
|
32
|
-
# # =======================================================
|
33
|
-
# require 'cloud_sesame'
|
34
|
-
|
35
|
-
# CloudSesame::Domain::Client.configure do |config|
|
36
|
-
# config.access_key = ENV['AWS_ACCESS_KEY_ID']
|
37
|
-
# config.secret_key = ENV['AWS_SECRET_ACCESS_KEY']
|
38
|
-
# end
|
39
|
-
|
40
|
-
# # Usage Example
|
41
|
-
# # =======================================================
|
42
|
-
# class Product
|
43
|
-
# include CloudSesame
|
44
|
-
|
45
|
-
# def self.greeting
|
46
|
-
# "hello world!"
|
47
|
-
# end
|
48
|
-
|
49
|
-
# define_cloudsearch do
|
50
|
-
# # Product CloudSesame Config
|
51
|
-
# config.endpoint = ENV['AWS_ENDPOINT']
|
52
|
-
# config.region = ENV['AWS_REGION']
|
53
|
-
|
54
|
-
# caching_with :RailsCache
|
55
|
-
|
56
|
-
# default_size 100
|
57
|
-
|
58
|
-
# define_sloppiness 3
|
59
|
-
|
60
|
-
# define_fuzziness do
|
61
|
-
# max_fuzziness 3
|
62
|
-
# min_char_size 6
|
63
|
-
# fuzzy_percent 0.17
|
64
|
-
# end
|
65
|
-
|
66
|
-
# field :searchable_text, query: { weight: 2 }
|
67
|
-
# field :description, query: true
|
68
|
-
# field :tags
|
69
|
-
|
70
|
-
# field :affiliate_advertiser_ext_id, facet: { size: 50 }
|
71
|
-
# field :currency, facet: true
|
72
|
-
# field :discount_percentage, facet: { buckets: %w([10,100] [25,100] [50,100] [70,100]), method: 'interval' }
|
73
|
-
# field :manufacturer_name, facet: { size: 50 }
|
74
|
-
# field :price, facet: { buckets: %w([0,25] [25,50] [50,100] [100,200] [200,}), method: 'interval' }
|
75
|
-
# field :category_string, facet: { sort: 'bucket', size: 10_000 }
|
76
|
-
# field :created_at
|
77
|
-
|
78
|
-
# end
|
79
|
-
|
80
|
-
# end
|
81
|
-
|
82
|
-
# class Coupon
|
83
|
-
# include CloudSesame
|
84
|
-
|
85
|
-
# VALID_COUPON_RANK = 20
|
86
|
-
|
87
|
-
# define_cloudsearch do
|
88
|
-
# config.endpoint = ENV['AWS_ENDPOINT']
|
89
|
-
# config.region = ENV['AWS_REGION']
|
90
|
-
|
91
|
-
# default_size 10
|
92
|
-
|
93
|
-
# define_sloppiness 3
|
94
|
-
|
95
|
-
# define_fuzziness do
|
96
|
-
# max_fuzziness 3
|
97
|
-
# min_char_size 6
|
98
|
-
# fuzzy_percent 0.17
|
99
|
-
# end
|
100
|
-
|
101
|
-
# field :end_date, as: :date1
|
102
|
-
# field :rank, as: :num1, default: -> { gte VALID_COUPON_RANK }
|
103
|
-
# field :searchable_text, query: true
|
104
|
-
# field :affiliate_advertiser_search_ext_id, as: :string1, facet: { size: 50 }
|
105
|
-
# field :countries, as: :string_array1
|
106
|
-
# field :deal_types, as: :string_array2, facet: {}
|
107
|
-
# field :tags, as: :string_array3, facet: {}
|
108
|
-
# field :type, default: -> { 'Catalog::CouponSearchable' }
|
109
|
-
# end
|
110
|
-
|
111
|
-
# end
|
112
|
-
|
113
|
-
# # @tags = [1, 2]
|
114
|
-
# # n = 10_000
|
115
|
-
# # q = nil
|
116
|
-
# # result = RubyProf.profile do
|
117
|
-
# # n.times do
|
118
|
-
# q = Product.cloudsearch.query("black jacket").sort(price: :asc).page(1).size(1000)
|
119
|
-
# .price { gt 100 }
|
120
|
-
# .and {
|
121
|
-
# or! {
|
122
|
-
# tags *@tags
|
123
|
-
# tags
|
124
|
-
# tags nil
|
125
|
-
# and! {
|
126
|
-
# tags.not "3", "4"
|
127
|
-
# }
|
128
|
-
# and!.not {
|
129
|
-
# tags.start_with "5", "6"
|
130
|
-
# tags.not.start_with("7")
|
131
|
-
# tags.not.near("8", distance: 7)
|
132
|
-
# tags start_with("9"), near("10")
|
133
|
-
# tags term("11", boost: 2)
|
134
|
-
# tags.not phrase "12"
|
135
|
-
# }
|
136
|
-
# or!.not {
|
137
|
-
# price(25..100)
|
138
|
-
# price 100...200
|
139
|
-
# price gte(200).lt(300)
|
140
|
-
# price gte(300)
|
141
|
-
# }
|
142
|
-
# or! {
|
143
|
-
# created_at Date.today - 7
|
144
|
-
# created_at gte(Date.today)
|
145
|
-
# created_at gte(Date.today).lt(Date.today + 3)
|
146
|
-
# }
|
147
|
-
# }
|
148
|
-
# }
|
149
|
-
|
150
|
-
# # end
|
151
|
-
# # end
|
152
|
-
# # printer = RubyProf::FlatPrinter.new(result)
|
153
|
-
# # printer.print(STDOUT, {})
|
154
|
-
|
155
|
-
# binding.pry
|
156
|
-
|
157
|
-
|
158
|
-
# end
|
4
|
+
end
|
File without changes
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
fdescribe CloudSesame do
|
4
|
+
# FAKE RAILS AND RAILS CACHE
|
5
|
+
# =====================================
|
6
|
+
class ::Rails
|
7
|
+
def self.cache
|
8
|
+
@cache ||= FakeCache.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ::FakeCache
|
13
|
+
def table
|
14
|
+
@table ||= {}
|
15
|
+
end
|
16
|
+
def fetch(key, &block)
|
17
|
+
table[key] ||= (block.call if block_given?)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# AWS initializer
|
22
|
+
# =======================================================
|
23
|
+
require 'yaml'
|
24
|
+
YAML.load_file('aws.yml').each do |key, value|
|
25
|
+
ENV["AWS_#{ key }"] = value
|
26
|
+
end
|
27
|
+
|
28
|
+
# Domain Initializer /config/initializers/cloudsearch.rb
|
29
|
+
# =======================================================
|
30
|
+
require 'cloud_sesame'
|
31
|
+
|
32
|
+
CloudSesame::Domain::Client.configure do |config|
|
33
|
+
config.access_key = ENV['AWS_ACCESS_KEY_ID']
|
34
|
+
config.secret_key = ENV['AWS_SECRET_ACCESS_KEY']
|
35
|
+
end
|
36
|
+
|
37
|
+
# Usage Example
|
38
|
+
# =======================================================
|
39
|
+
class Product
|
40
|
+
include CloudSesame
|
41
|
+
|
42
|
+
def self.greeting
|
43
|
+
"hello world!"
|
44
|
+
end
|
45
|
+
|
46
|
+
define_cloudsearch do
|
47
|
+
# Product CloudSesame Config
|
48
|
+
config.endpoint = ENV['AWS_ENDPOINT']
|
49
|
+
config.region = ENV['AWS_REGION']
|
50
|
+
|
51
|
+
caching_with :RailsCache
|
52
|
+
|
53
|
+
default_size 100
|
54
|
+
|
55
|
+
define_sloppiness 3
|
56
|
+
|
57
|
+
define_fuzziness do
|
58
|
+
max_fuzziness 3
|
59
|
+
min_char_size 6
|
60
|
+
fuzzy_percent 0.17
|
61
|
+
end
|
62
|
+
|
63
|
+
field :searchable_text, query: { weight: 2 }, type: :string
|
64
|
+
field :description, query: true, type: :string
|
65
|
+
field :tags, type: :string
|
66
|
+
|
67
|
+
field :affiliate_advertiser_ext_id, facet: { size: 50 }
|
68
|
+
field :currency, facet: true
|
69
|
+
field :discount_percentage, facet: { buckets: %w([10,100] [25,100] [50,100] [70,100]), method: 'interval' }
|
70
|
+
field :manufacturer_name, facet: { size: 50 }
|
71
|
+
field :price, facet: { buckets: %w([0,25] [25,50] [50,100] [100,200] [200,}), method: 'interval' }
|
72
|
+
field :category_string, facet: { sort: 'bucket', size: 10_000 }
|
73
|
+
field :created_at, type: :datetime
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class Coupon
|
80
|
+
include CloudSesame
|
81
|
+
|
82
|
+
VALID_COUPON_RANK = 20
|
83
|
+
|
84
|
+
define_cloudsearch do
|
85
|
+
config.endpoint = ENV['AWS_ENDPOINT']
|
86
|
+
config.region = ENV['AWS_REGION']
|
87
|
+
|
88
|
+
default_size 10
|
89
|
+
|
90
|
+
define_sloppiness 3
|
91
|
+
|
92
|
+
define_fuzziness do
|
93
|
+
max_fuzziness 3
|
94
|
+
min_char_size 6
|
95
|
+
fuzzy_percent 0.17
|
96
|
+
end
|
97
|
+
|
98
|
+
field :end_date, as: :date1
|
99
|
+
field :rank, as: :num1, default: -> { gte VALID_COUPON_RANK }
|
100
|
+
field :searchable_text, query: true
|
101
|
+
field :affiliate_advertiser_search_ext_id, as: :string1, facet: { size: 50 }
|
102
|
+
field :countries, as: :string_array1
|
103
|
+
field :deal_types, as: :string_array2, facet: {}
|
104
|
+
field :tags, as: :string_array3, facet: {}
|
105
|
+
field :type, default: -> { 'Catalog::CouponSearchable' }
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
@tags = [1, 2]
|
111
|
+
q = nil
|
112
|
+
profile 1 do
|
113
|
+
q = Product.cloudsearch
|
114
|
+
.query("black leather jacket")
|
115
|
+
.sort(price: :asc, created_at: :desc)
|
116
|
+
.page(3)
|
117
|
+
.size(1000)
|
118
|
+
.price { gt 1 }
|
119
|
+
.tags { "2" }
|
120
|
+
.and {
|
121
|
+
or! {
|
122
|
+
tags *@tags
|
123
|
+
tags nil
|
124
|
+
and! {
|
125
|
+
tags.not 3, 4
|
126
|
+
}
|
127
|
+
and!.not {
|
128
|
+
tags.start_with "5", "6"
|
129
|
+
tags.not.start_with("7")
|
130
|
+
tags.not.near("8", distance: 7)
|
131
|
+
tags start_with("9"), near("10")
|
132
|
+
tags term("11", boost: 2)
|
133
|
+
tags.not phrase "12"
|
134
|
+
}
|
135
|
+
or!.not {
|
136
|
+
price(25..100)
|
137
|
+
price 100...200
|
138
|
+
price gte(200).lt(300)
|
139
|
+
price gte(300)
|
140
|
+
}
|
141
|
+
or! {
|
142
|
+
created_at Date.today - 7
|
143
|
+
created_at gte(Date.today)
|
144
|
+
created_at gte(Date.today).lt(Date.today + 3)
|
145
|
+
}
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
q.compile
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
binding.pry
|
154
|
+
|
155
|
+
end
|
File without changes
|
@@ -0,0 +1,135 @@
|
|
1
|
+
describe ClassSpecific do
|
2
|
+
|
3
|
+
class SearchableClass; end
|
4
|
+
|
5
|
+
let(:searchable) { :SearchableClass }
|
6
|
+
|
7
|
+
describe '.construct_class' do
|
8
|
+
let(:specific_class) { SearchableClass }
|
9
|
+
let(:klass) { Class.new { extend ClassSpecific } }
|
10
|
+
|
11
|
+
context 'when sub-class has not being defined previously' do
|
12
|
+
context 'and specific class passed in has a constant name' do
|
13
|
+
it 'should define a sub-class under the extended class' do
|
14
|
+
expect{ klass.construct_class(specific_class) }.to change{ klass.constants.size }.by(1)
|
15
|
+
end
|
16
|
+
it 'should name the sub-class after the class passed in' do
|
17
|
+
expect{ klass.construct_class(specific_class) }.to change{ klass.constants }.by([searchable])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'and specific class passed in does not have constant name' do
|
22
|
+
let(:specific_class) { Class.new }
|
23
|
+
it 'should not define a sub-class under the extended class' do
|
24
|
+
expect{ klass.construct_class(specific_class) }.to_not change{ klass.constants.size }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'and callback is given' do
|
29
|
+
context 'and callback args is not given' do
|
30
|
+
it 'should invoke callback after construct' do
|
31
|
+
expect do |b|
|
32
|
+
klass.after_construct(&b)
|
33
|
+
klass.construct_class(specific_class)
|
34
|
+
end.to yield_with_args(specific_class)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
context 'and callback is not given' do
|
38
|
+
let(:callback_args) { [1, 2, 3] }
|
39
|
+
it 'should invoke callback and yield callback args after construct' do
|
40
|
+
expect do |b|
|
41
|
+
klass.after_construct(&b)
|
42
|
+
klass.construct_class(specific_class, callback_args: callback_args)
|
43
|
+
end.to yield_with_args(specific_class, *callback_args)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'when sub-class has being defined previously' do
|
51
|
+
it 'should not define a sub-class again' do
|
52
|
+
klass.construct_class(specific_class)
|
53
|
+
expect(Class).to_not receive(:new).with(klass)
|
54
|
+
expect{ klass.construct_class(specific_class) }.to_not change{ klass.constants.size }
|
55
|
+
end
|
56
|
+
it 'should not invoke callback twice' do
|
57
|
+
expect do |b|
|
58
|
+
klass.after_construct(&b)
|
59
|
+
klass.construct_class(specific_class)
|
60
|
+
klass.construct_class(specific_class)
|
61
|
+
end.to yield_control.exactly(1).times
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '.construct_module' do
|
68
|
+
let(:specific_class) { SearchableClass }
|
69
|
+
let(:custom_module) { Module.new { extend ClassSpecific } }
|
70
|
+
|
71
|
+
context 'when sub-class has not being defined previously' do
|
72
|
+
context 'and specific class passed in has a constant name' do
|
73
|
+
it 'should define a sub-class under the extended class' do
|
74
|
+
expect{ custom_module.construct_module(specific_class) }.to change{ custom_module.constants.size }.by(1)
|
75
|
+
end
|
76
|
+
it 'should name the sub-class after the class passed in' do
|
77
|
+
expect{ custom_module.construct_module(specific_class) }.to change{ custom_module.constants }.by([searchable])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context 'and specific class passed in does not have constant name' do
|
82
|
+
let(:specific_class) { Class.new }
|
83
|
+
it 'should not define a sub-class under the extended class' do
|
84
|
+
expect{ custom_module.construct_module(specific_class) }.to_not change{ custom_module.constants.size }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'and callback is given' do
|
89
|
+
context 'and callback args is not given' do
|
90
|
+
it 'should invoke callback after construct' do
|
91
|
+
expect do |b|
|
92
|
+
custom_module.after_construct(&b)
|
93
|
+
custom_module.construct_module(specific_class)
|
94
|
+
end.to yield_with_args(specific_class)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
context 'and callback is not given' do
|
98
|
+
let(:callback_args) { [1, 2, 3] }
|
99
|
+
it 'should invoke callback and yield callback args after construct' do
|
100
|
+
expect do |b|
|
101
|
+
custom_module.after_construct(&b)
|
102
|
+
custom_module.construct_module(specific_class, callback_args: callback_args)
|
103
|
+
end.to yield_with_args(specific_class, *callback_args)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
context 'when sub-class has being defined previously' do
|
111
|
+
it 'should not define a sub-class again' do
|
112
|
+
custom_module.construct_module(specific_class)
|
113
|
+
expect(Module).to_not receive(:new).with(custom_module)
|
114
|
+
expect{ custom_module.construct_module(specific_class) }.to_not change{ custom_module.constants.size }
|
115
|
+
end
|
116
|
+
it 'should not invoke callback twice' do
|
117
|
+
expect do |b|
|
118
|
+
custom_module.after_construct(&b)
|
119
|
+
custom_module.construct_module(specific_class)
|
120
|
+
custom_module.construct_module(specific_class)
|
121
|
+
end.to yield_control.exactly(1).times
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe '.after_construct' do
|
127
|
+
let(:specific_class) { SearchableClass }
|
128
|
+
let(:klass) { Class.new { extend ClassSpecific } }
|
129
|
+
let(:block) { Proc.new {} }
|
130
|
+
it 'should store the block as __construct_callback__' do
|
131
|
+
expect{ klass.after_construct(&block) }.to change{ klass.instance_variable_get(:@__construct_callback__) }.from(nil).to(block)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -6,10 +6,13 @@ require 'bundler/setup'
|
|
6
6
|
Bundler.setup
|
7
7
|
|
8
8
|
require 'pry'
|
9
|
+
require 'helpers/benchmark_helper'
|
9
10
|
require 'cloud_sesame'
|
10
11
|
|
11
12
|
RSpec.configure do |config|
|
12
13
|
# For running just wanted tests in guard
|
13
14
|
config.filter_run :focus => true
|
14
15
|
config.run_all_when_everything_filtered = true
|
16
|
+
|
17
|
+
config.extend BenchmarkHelper
|
15
18
|
end
|