CloudSesame 0.1.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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +76 -0
- data/Guardfile +70 -0
- data/cloud_sesame.gemspec +25 -0
- data/lib/abstract_object.rb +67 -0
- data/lib/cloud_sesame.rb +77 -0
- data/lib/cloud_sesame/config/credential.rb +8 -0
- data/lib/cloud_sesame/domain/base.rb +70 -0
- data/lib/cloud_sesame/domain/client.rb +28 -0
- data/lib/cloud_sesame/domain/config.rb +8 -0
- data/lib/cloud_sesame/domain/context.rb +35 -0
- data/lib/cloud_sesame/query/ast/and.rb +9 -0
- data/lib/cloud_sesame/query/ast/compound_array.rb +73 -0
- data/lib/cloud_sesame/query/ast/leaf.rb +8 -0
- data/lib/cloud_sesame/query/ast/literal.rb +27 -0
- data/lib/cloud_sesame/query/ast/multi_branch.rb +27 -0
- data/lib/cloud_sesame/query/ast/not.rb +13 -0
- data/lib/cloud_sesame/query/ast/operator.rb +24 -0
- data/lib/cloud_sesame/query/ast/or.rb +9 -0
- data/lib/cloud_sesame/query/ast/prefix_literal.rb +17 -0
- data/lib/cloud_sesame/query/ast/root.rb +24 -0
- data/lib/cloud_sesame/query/ast/single_branch.rb +24 -0
- data/lib/cloud_sesame/query/ast/value.rb +38 -0
- data/lib/cloud_sesame/query/builder.rb +78 -0
- data/lib/cloud_sesame/query/dsl.rb +62 -0
- data/lib/cloud_sesame/query/dsl/and.rb +19 -0
- data/lib/cloud_sesame/query/dsl/base.rb +23 -0
- data/lib/cloud_sesame/query/dsl/filter_query.rb +47 -0
- data/lib/cloud_sesame/query/dsl/literal.rb +41 -0
- data/lib/cloud_sesame/query/dsl/or.rb +19 -0
- data/lib/cloud_sesame/query/dsl/range.rb +41 -0
- data/lib/cloud_sesame/query/dsl/scope.rb +24 -0
- data/lib/cloud_sesame/query/error/missing_operator_symbol.rb +8 -0
- data/lib/cloud_sesame/query/error/missing_query.rb +8 -0
- data/lib/cloud_sesame/query/node/abstract.rb +14 -0
- data/lib/cloud_sesame/query/node/facet.rb +14 -0
- data/lib/cloud_sesame/query/node/filter_query.rb +17 -0
- data/lib/cloud_sesame/query/node/page.rb +27 -0
- data/lib/cloud_sesame/query/node/query.rb +29 -0
- data/lib/cloud_sesame/query/node/query_options.rb +37 -0
- data/lib/cloud_sesame/query/node/query_options_field.rb +20 -0
- data/lib/cloud_sesame/query/node/query_parser.rb +27 -0
- data/lib/cloud_sesame/query/node/request.rb +71 -0
- data/lib/cloud_sesame/query/node/sort.rb +37 -0
- data/spec/abstract_object_spec.rb +103 -0
- data/spec/cloud_sesame/domain/base_spec.rb +27 -0
- data/spec/cloud_sesame/domain/context_spec.rb +24 -0
- data/spec/cloud_sesame/query/ast/and_spec.rb +13 -0
- data/spec/cloud_sesame/query/ast/literal_spec.rb +37 -0
- data/spec/cloud_sesame/query/ast/multi_branch_spec.rb +65 -0
- data/spec/cloud_sesame/query/ast/operator_spec.rb +30 -0
- data/spec/cloud_sesame/query/ast/or_spec.rb +13 -0
- data/spec/cloud_sesame/query/ast/root_spec.rb +43 -0
- data/spec/cloud_sesame/query/ast/value_spec.rb +40 -0
- data/spec/cloud_sesame/query/builder_spec.rb +12 -0
- data/spec/cloud_sesame/query/dsl/base_spec.rb +31 -0
- data/spec/cloud_sesame/query/dsl/filter_query_spec.rb +158 -0
- data/spec/cloud_sesame/query/dsl_spec.rb +96 -0
- data/spec/cloud_sesame/query/node/abstract_spec.rb +19 -0
- data/spec/cloud_sesame/query/node/facet_spec.rb +42 -0
- data/spec/cloud_sesame/query/node/filter_query_spec.rb +29 -0
- data/spec/cloud_sesame/query/node/page_spec.rb +58 -0
- data/spec/cloud_sesame/query/node/query_options_field_spec.rb +27 -0
- data/spec/cloud_sesame/query/node/query_options_spec.rb +56 -0
- data/spec/cloud_sesame/query/node/query_parser_spec.rb +37 -0
- data/spec/cloud_sesame/query/node/query_spec.rb +46 -0
- data/spec/cloud_sesame/query/node/request_spec.rb +71 -0
- data/spec/cloud_sesame/query/node/sort_spec.rb +76 -0
- data/spec/cloud_sesame_spec.rb +58 -0
- data/spec/spec_helper.rb +12 -0
- metadata +218 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Query
|
3
|
+
module Node
|
4
|
+
class Request < Abstract
|
5
|
+
|
6
|
+
# CHILDREN
|
7
|
+
# =========================================
|
8
|
+
|
9
|
+
def query
|
10
|
+
@query ||= Query.new(context[:query, true])
|
11
|
+
end
|
12
|
+
|
13
|
+
def query_options
|
14
|
+
@query_options ||= QueryOptions.new(context[:query_options, true])
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_parser
|
18
|
+
@query_parser ||= QueryParser.new(context[:query_parser, true])
|
19
|
+
end
|
20
|
+
|
21
|
+
def filter_query
|
22
|
+
@filter_query ||= FilterQuery.new(context[:filter_query, true])
|
23
|
+
end
|
24
|
+
|
25
|
+
def facet
|
26
|
+
@facet ||= Facet.new(context[:facet, true])
|
27
|
+
end
|
28
|
+
|
29
|
+
def page
|
30
|
+
@page ||= Page.new(context[:page, true])
|
31
|
+
end
|
32
|
+
|
33
|
+
def sort
|
34
|
+
@sort ||= Sort.new(context[:sort, true])
|
35
|
+
end
|
36
|
+
|
37
|
+
# EVALUATION
|
38
|
+
# =========================================
|
39
|
+
|
40
|
+
def compile
|
41
|
+
compiled = [
|
42
|
+
query,
|
43
|
+
query_options,
|
44
|
+
query_parser,
|
45
|
+
filter_query,
|
46
|
+
page,
|
47
|
+
sort
|
48
|
+
].each_with_object({}) do |node, compiled|
|
49
|
+
compiled.merge!(node.compile || {})
|
50
|
+
end
|
51
|
+
|
52
|
+
convert_to_structured_query(compiled) if compiled[:query].empty?
|
53
|
+
|
54
|
+
compiled
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def convert_to_structured_query(compiled)
|
60
|
+
replace(compiled, :query, :filter_query).merge!(query_parser.structured.compile)
|
61
|
+
end
|
62
|
+
|
63
|
+
def replace(hash, key1, key2)
|
64
|
+
hash[key1] = hash.delete key2
|
65
|
+
hash
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CloudSesame
|
2
|
+
module Query
|
3
|
+
module Node
|
4
|
+
class Sort < Abstract
|
5
|
+
|
6
|
+
attr_reader :sorting_attributes
|
7
|
+
|
8
|
+
def sorting_attributes
|
9
|
+
@sorting_attributes ||= deserialize context[:sort]
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](attribute)
|
13
|
+
sorting_attributes[attribute.to_sym]
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(attribute, order = nil)
|
17
|
+
sorting_attributes[attribute.to_sym] = order.to_sym if order
|
18
|
+
end
|
19
|
+
|
20
|
+
def compile
|
21
|
+
(result = serialize(sorting_attributes)).empty? ? {} : { sort: result }
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def serialize(hash = {})
|
27
|
+
hash.to_a.map { |i| i.join(' ') }.join(',')
|
28
|
+
end
|
29
|
+
|
30
|
+
def deserialize(string)
|
31
|
+
Hash[*((string || "").split(',').map { |i| i.strip.split(' ').map(&:to_sym) }.flatten)]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AbstractObject do
|
4
|
+
|
5
|
+
# define TestConfig Class
|
6
|
+
class TestConfig < AbstractObject; end
|
7
|
+
|
8
|
+
describe '.accept' do
|
9
|
+
before { TestConfig.accept :access_key }
|
10
|
+
|
11
|
+
it 'should define attribute accessor using attribute name' do
|
12
|
+
expect(TestConfig.new).to respond_to(:access_key, :access_key=)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should store attribute name in definitions' do
|
16
|
+
definitions = TestConfig.instance_variable_get(:@definitions)
|
17
|
+
expect(definitions).to include(access_key: :access_key)
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when accept attribute aliases' do
|
21
|
+
before { TestConfig.accept :access_key, as: [:access_key_id] }
|
22
|
+
|
23
|
+
it 'should define attribute accessor using default attribute name' do
|
24
|
+
expect(TestConfig.new).to respond_to(:access_key, :access_key=)
|
25
|
+
end
|
26
|
+
it 'should define attribute accessor using aliases' do
|
27
|
+
expect(TestConfig.new).to respond_to(:access_key_id, :access_key_id=)
|
28
|
+
end
|
29
|
+
it 'should store aliases in definitions' do
|
30
|
+
definitions = TestConfig.instance_variable_get(:@definitions)
|
31
|
+
expect(definitions).to include(access_key: :access_key, access_key_id: :access_key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when accept attribute with default value' do
|
36
|
+
let(:default_value) { SecureRandom.hex }
|
37
|
+
before { TestConfig.accept :access_key, default: default_value }
|
38
|
+
|
39
|
+
it 'should return default value if attribute hasn\'t being set' do
|
40
|
+
expect(TestConfig.new.access_key).to eq default_value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#initialize' do
|
47
|
+
|
48
|
+
shared_examples 'acceptable attributes' do
|
49
|
+
it 'should set the acceptable attributes' do
|
50
|
+
expect(TestConfig.new(attributes_passed_in).to_hash).to include attributes_accepted
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
shared_examples 'unacceptable attributes' do
|
55
|
+
it 'should filter out the unacceptable attributes' do
|
56
|
+
expect(TestConfig.new(attributes_passed_in).to_hash).to_not include attributes_not_accepted
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when attributes passed is a hash' do
|
61
|
+
let(:attributes_accepted) { { access_key: 1 } }
|
62
|
+
|
63
|
+
context 'and attribute name is used' do
|
64
|
+
let(:attributes_passed_in) { { access_key: 1 } }
|
65
|
+
include_examples 'acceptable attributes'
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'and attribute alias is used' do
|
69
|
+
let(:attributes_passed_in) { { access_key_id: 1 } }
|
70
|
+
include_examples 'acceptable attributes'
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'and attribute is not in the definitions' do
|
74
|
+
let(:attributes_not_accepted) { { secret_key: 2 } }
|
75
|
+
let(:attributes_passed_in) { { secret_key: 2 } }
|
76
|
+
include_examples 'unacceptable attributes'
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
context 'when attributes passed is a config object' do
|
82
|
+
let(:attributes_accepted) { { access_key: 1 } }
|
83
|
+
let(:attributes_not_accepted) { { secret_key: 2 } }
|
84
|
+
let(:attributes_passed_in) { TestConfig.new(access_key: 1, secret_key: 2) }
|
85
|
+
include_examples 'acceptable attributes'
|
86
|
+
include_examples 'unacceptable attributes'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'to_hash' do
|
91
|
+
let(:config) { TestConfig.new(access_key: 1) }
|
92
|
+
|
93
|
+
it 'should return config attributes in hash' do
|
94
|
+
expect(config.to_hash).to be_a(Hash)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should not be able to manipulate the config through the hash' do
|
98
|
+
config.to_hash[:secret_key] = 2
|
99
|
+
expect(config.to_hash).to_not include(secret_key: 2)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CloudSesame
|
4
|
+
module Domain
|
5
|
+
describe Base do
|
6
|
+
|
7
|
+
let(:searchable_class) { "Test" }
|
8
|
+
subject { Base.new(searchable_class) }
|
9
|
+
|
10
|
+
describe '#initalize' do
|
11
|
+
it 'should set searchable class' do
|
12
|
+
expect(subject.searchable_class).to eq "Test"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#field' do
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#client' do
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#query' do
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module CloudSesame
|
4
|
+
module Domain
|
5
|
+
describe Context do
|
6
|
+
|
7
|
+
describe '#initialize' do
|
8
|
+
it 'should set default value to nil' do
|
9
|
+
expect(subject.table).to eq({})
|
10
|
+
end
|
11
|
+
it 'should accept an default value' do
|
12
|
+
context = Context.new(:default_value)
|
13
|
+
expect(context.table).to eq(:default_value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#[]' do
|
18
|
+
# it 'should return an '
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CloudSesame
|
4
|
+
module Query
|
5
|
+
module AST
|
6
|
+
describe Literal do
|
7
|
+
let(:literal) { Literal.new('description', 'Shoes') }
|
8
|
+
let(:value) { Value.new("Shoes") }
|
9
|
+
|
10
|
+
# before { allow(Value).to receive(:new).and_return(value) }
|
11
|
+
|
12
|
+
describe '#initialize' do
|
13
|
+
it 'should store field' do
|
14
|
+
expect(literal.field).to eq('description')
|
15
|
+
end
|
16
|
+
it 'should create value node and store it' do
|
17
|
+
expect(Value).to receive(:new).with('Shoes').and_return(value)
|
18
|
+
expect(literal.value).to be_kind_of Value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#compile' do
|
23
|
+
context 'when options is empty' do
|
24
|
+
it 'should compile value' do
|
25
|
+
expect_any_instance_of(Value).to receive(:compile).and_return("'Shoes'")
|
26
|
+
literal.compile
|
27
|
+
end
|
28
|
+
it 'should join the field and compiled value with colon' do
|
29
|
+
expect(literal.compile).to eq("description:'Shoes'")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CloudSesame
|
4
|
+
module Query
|
5
|
+
module AST
|
6
|
+
describe MultiBranch do
|
7
|
+
let(:context) { {} }
|
8
|
+
let(:proc) { Proc.new { } }
|
9
|
+
let(:node) { MultiBranch.new(context, &proc) }
|
10
|
+
|
11
|
+
describe '#initialize' do
|
12
|
+
it 'should store the context' do
|
13
|
+
expect(node.context).to eq context
|
14
|
+
end
|
15
|
+
it 'should accepts an block and eval it' do
|
16
|
+
proc = Proc.new { test }
|
17
|
+
expect_any_instance_of(MultiBranch).to receive(:test)
|
18
|
+
MultiBranch.new(context, &proc)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#children' do
|
23
|
+
it 'should instantiate an empty array' do
|
24
|
+
expect(CompoundArray).to receive(:new).and_call_original
|
25
|
+
node.children
|
26
|
+
end
|
27
|
+
it 'should return the array' do
|
28
|
+
node.children.concat([1,2,3])
|
29
|
+
expect(node.children).to eq [1,2,3]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#compile_children' do
|
34
|
+
let(:children) {
|
35
|
+
[Value.new('value1'),
|
36
|
+
Value.new('value2')]
|
37
|
+
}
|
38
|
+
it 'should compile its children' do
|
39
|
+
children.each do |child|
|
40
|
+
expect(child).to receive(:compile)
|
41
|
+
end
|
42
|
+
|
43
|
+
node.children.concat(children)
|
44
|
+
node.compile_children
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should join its compiled children with a space' do
|
48
|
+
children.each do |child|
|
49
|
+
expect(child).to receive(:compile).and_call_original
|
50
|
+
end
|
51
|
+
node.children.concat(children)
|
52
|
+
expect(node.compile_children).to eq("'value1' 'value2'")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'Query DSL' do
|
57
|
+
it 'should be included' do
|
58
|
+
expect(node).to respond_to(:and, :or, :literal)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CloudSesame
|
4
|
+
module Query
|
5
|
+
module AST
|
6
|
+
describe Operator do
|
7
|
+
let(:proc) { Proc.new {} }
|
8
|
+
let(:operator) { Operator.new({}, &proc )}
|
9
|
+
before { Operator.symbol = :symbol }
|
10
|
+
|
11
|
+
describe '#compile' do
|
12
|
+
it 'should raise an error if operator symbol has not being set' do
|
13
|
+
Operator.symbol = nil
|
14
|
+
expect{ operator.compile }.to raise_error(Error::MissingOperatorSymbol)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return nil if children are empty' do
|
18
|
+
expect(operator.compile).to eq(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should return a string with the symbol and compile children' do
|
22
|
+
operator.children.push Literal.new(:price, 10)
|
23
|
+
expect(operator.compile).to eq("(symbol price:10)")
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module CloudSesame
|
4
|
+
module Query
|
5
|
+
module AST
|
6
|
+
describe Root do
|
7
|
+
|
8
|
+
let(:root) { Root.new({}) }
|
9
|
+
|
10
|
+
describe '#compile' do
|
11
|
+
before { root.children.concat(children) }
|
12
|
+
|
13
|
+
context 'when it has multiple children' do
|
14
|
+
let(:children) {
|
15
|
+
[Literal.new(:tags, "flash_deal"),
|
16
|
+
Literal.new(:tags, "sales")]
|
17
|
+
}
|
18
|
+
it 'should inject an default operator' do
|
19
|
+
expect(root.compile).to eq "(and tags:'flash_deal' tags:'sales')"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
context 'when it has 1 child' do
|
23
|
+
let(:children) {
|
24
|
+
[Literal.new(:tags, "flash_deal")]
|
25
|
+
}
|
26
|
+
it 'should compile the child' do
|
27
|
+
expect(root).to receive(:compile_children)
|
28
|
+
root.compile
|
29
|
+
end
|
30
|
+
end
|
31
|
+
context 'when it has no children' do
|
32
|
+
let(:children) { [] }
|
33
|
+
it 'should compile the child' do
|
34
|
+
expect(root).to receive(:compile_children)
|
35
|
+
root.compile
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|