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