CloudSesame 0.6.8 → 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.
- 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
|