mongoid-casino 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +20 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +93 -0
- data/Rakefile +9 -0
- data/casino.gemspec +30 -0
- data/lib/casino.rb +13 -0
- data/lib/casino/collection.rb +195 -0
- data/lib/casino/dimension.rb +11 -0
- data/lib/casino/focus.rb +26 -0
- data/lib/casino/intersection.rb +30 -0
- data/lib/casino/intersection/match/all.rb +29 -0
- data/lib/casino/intersection/match/base.rb +49 -0
- data/lib/casino/intersection/match/equivalence.rb +25 -0
- data/lib/casino/intersection/match/expression.rb +15 -0
- data/lib/casino/intersection/match/greater.rb +43 -0
- data/lib/casino/intersection/match/include.rb +15 -0
- data/lib/casino/intersection/match/lesser.rb +31 -0
- data/lib/casino/intersection/match/recurse.rb +19 -0
- data/lib/casino/lobby.rb +21 -0
- data/lib/casino/projection.rb +76 -0
- data/lib/casino/query.rb +23 -0
- data/lib/casino/question.rb +9 -0
- data/lib/casino/store.rb +49 -0
- data/lib/casino/version.rb +3 -0
- data/spec/fabricators/model_fabricator.rb +12 -0
- data/spec/fabricators/note_fabricator.rb +3 -0
- data/spec/fixtures/collections/collection.rb +3 -0
- data/spec/fixtures/collections/emails_by_day.rb +28 -0
- data/spec/fixtures/models/model.rb +8 -0
- data/spec/fixtures/models/note.rb +7 -0
- data/spec/lib/casino/collection_spec.rb +146 -0
- data/spec/lib/casino/dimension_spec.rb +27 -0
- data/spec/lib/casino/focus_spec.rb +53 -0
- data/spec/lib/casino/intersection/match/all_spec.rb +24 -0
- data/spec/lib/casino/intersection/match/base_spec.rb +159 -0
- data/spec/lib/casino/intersection/match/equivalence_spec.rb +26 -0
- data/spec/lib/casino/intersection/match/expression_spec.rb +26 -0
- data/spec/lib/casino/intersection/match/greater_spec.rb +72 -0
- data/spec/lib/casino/intersection/match/include_spec.rb +27 -0
- data/spec/lib/casino/intersection/match/lesser_spec.rb +74 -0
- data/spec/lib/casino/intersection/match/recurse_spec.rb +24 -0
- data/spec/lib/casino/intersection_spec.rb +33 -0
- data/spec/lib/casino/lobby_spec.rb +20 -0
- data/spec/lib/casino/projection_spec.rb +81 -0
- data/spec/lib/casino/query_spec.rb +42 -0
- data/spec/lib/casino/question_spec.rb +10 -0
- data/spec/lib/casino/store_spec.rb +60 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/mongoid.yml +6 -0
- metadata +234 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Casino
|
2
|
+
class Intersection
|
3
|
+
module Match
|
4
|
+
class All < Equivalence
|
5
|
+
|
6
|
+
def eligible?
|
7
|
+
key == '$and'
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate
|
11
|
+
matchers.map(&:evaluate).all?
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def matchers
|
17
|
+
field.reduce(Array.new) do |list, element|
|
18
|
+
list + build_bases_with_element_keys(element)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_bases_with_element_keys(element)
|
23
|
+
element.keys.map { |key| Base.new(document, key, element, value) }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Casino
|
2
|
+
class Intersection
|
3
|
+
module Match
|
4
|
+
class Base
|
5
|
+
attr_accessor :document, :field, :key, :selector, :value
|
6
|
+
|
7
|
+
def initialize(document, key, selector, value = nil)
|
8
|
+
self.document = document
|
9
|
+
self.field = selector[key]
|
10
|
+
self.key = key
|
11
|
+
self.selector = selector
|
12
|
+
self.value = document_value || value
|
13
|
+
end
|
14
|
+
|
15
|
+
def evaluate
|
16
|
+
matcher.evaluate
|
17
|
+
end
|
18
|
+
|
19
|
+
def match_arguments
|
20
|
+
[ key, value, document, field ]
|
21
|
+
end
|
22
|
+
|
23
|
+
def matchers
|
24
|
+
matcher_classes.map { |class_name| matcher_for class_name }
|
25
|
+
end
|
26
|
+
|
27
|
+
def matcher
|
28
|
+
matchers.find { |matcher| matcher.eligible? }
|
29
|
+
end
|
30
|
+
|
31
|
+
def matcher_for(class_name)
|
32
|
+
class_name = String(class_name).humanize
|
33
|
+
Match.const_get(class_name).new(*match_arguments)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def matcher_classes
|
39
|
+
%w(all include recurse greater lesser expression equivalence)
|
40
|
+
end
|
41
|
+
|
42
|
+
def document_value
|
43
|
+
document.send(key) if document.respond_to?(key)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Casino
|
2
|
+
class Intersection
|
3
|
+
module Match
|
4
|
+
class Equivalence
|
5
|
+
attr_accessor :document, :field, :key, :value
|
6
|
+
|
7
|
+
def initialize(key, value, document, field)
|
8
|
+
self.key = key
|
9
|
+
self.document = document
|
10
|
+
self.field = field
|
11
|
+
self.value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def eligible?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def evaluate
|
19
|
+
value == field
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Casino
|
2
|
+
class Intersection
|
3
|
+
module Match
|
4
|
+
class Greater < Equivalence
|
5
|
+
|
6
|
+
def eligible?
|
7
|
+
%w($gt $gte).include? key
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate
|
11
|
+
greater_than? ? greater_than! : greater_than_or_equal!
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def greater_than?
|
17
|
+
key == '$gt'
|
18
|
+
end
|
19
|
+
|
20
|
+
def greater_than!
|
21
|
+
evolved_value > evolved_field
|
22
|
+
end
|
23
|
+
|
24
|
+
def greater_than_or_equal!
|
25
|
+
evolved_value >= evolved_field
|
26
|
+
end
|
27
|
+
|
28
|
+
def evolved_value
|
29
|
+
evolve value
|
30
|
+
end
|
31
|
+
|
32
|
+
def evolved_field
|
33
|
+
evolve field
|
34
|
+
end
|
35
|
+
|
36
|
+
def evolve(instance)
|
37
|
+
instance.class.evolve(instance)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Casino
|
2
|
+
class Intersection
|
3
|
+
module Match
|
4
|
+
class Lesser < Greater
|
5
|
+
|
6
|
+
def eligible?
|
7
|
+
%w($lt $lte).include? key
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate
|
11
|
+
lesser_than? ? lesser_than! : lesser_than_or_equal!
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def lesser_than?
|
17
|
+
key == '$lt'
|
18
|
+
end
|
19
|
+
|
20
|
+
def lesser_than!
|
21
|
+
evolved_value < evolved_field
|
22
|
+
end
|
23
|
+
|
24
|
+
def lesser_than_or_equal!
|
25
|
+
evolved_value <= evolved_field
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Casino
|
2
|
+
class Intersection
|
3
|
+
module Match
|
4
|
+
class Recurse < Equivalence
|
5
|
+
|
6
|
+
def eligible?
|
7
|
+
field.is_a? Hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def evaluate
|
11
|
+
keys = field.keys
|
12
|
+
bases = keys.map { |key| Base.new(document, key, field, value) }
|
13
|
+
bases.map(&:evaluate).all?
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/casino/lobby.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Casino
|
2
|
+
class Lobby
|
3
|
+
attr_accessor :collection
|
4
|
+
|
5
|
+
def initialize(klass)
|
6
|
+
self.collection = klass
|
7
|
+
end
|
8
|
+
|
9
|
+
def registry
|
10
|
+
@registry ||= Hash.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_registry(object)
|
14
|
+
key = object.class.name.downcase.split("::").last.to_sym
|
15
|
+
registry[key] ||= Array.new
|
16
|
+
registry[key] << object
|
17
|
+
registry[key] = registry[key].uniq
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Casino
|
2
|
+
class Projection
|
3
|
+
attr_accessor :target, :pipeline
|
4
|
+
|
5
|
+
def initialize(klass, pipeline = [])
|
6
|
+
self.target = klass
|
7
|
+
self.pipeline = pipeline
|
8
|
+
end
|
9
|
+
|
10
|
+
def results
|
11
|
+
target.collection.aggregate(pipeline)
|
12
|
+
end
|
13
|
+
|
14
|
+
def mongoize(hash)
|
15
|
+
hash.keys.map { |key| build_mongoized_hash(hash, key) }.reduce(&:merge)
|
16
|
+
end
|
17
|
+
|
18
|
+
def group(conditions)
|
19
|
+
clone_with group: conditions
|
20
|
+
end
|
21
|
+
alias :pivot :group
|
22
|
+
|
23
|
+
def project(conditions)
|
24
|
+
clone_with project: conditions
|
25
|
+
end
|
26
|
+
alias :describe :project
|
27
|
+
alias :build :project
|
28
|
+
|
29
|
+
def unwind(conditions)
|
30
|
+
clone_with unwind: conditions
|
31
|
+
end
|
32
|
+
alias :expand :unwind
|
33
|
+
|
34
|
+
def match(conditions)
|
35
|
+
clone_with match: conditions
|
36
|
+
end
|
37
|
+
alias :where :match
|
38
|
+
alias :find :match
|
39
|
+
|
40
|
+
def sort(conditions)
|
41
|
+
clone_with sort: conditions
|
42
|
+
end
|
43
|
+
alias :order :sort
|
44
|
+
|
45
|
+
def skip(conditions)
|
46
|
+
clone_with skip: conditions
|
47
|
+
end
|
48
|
+
alias :offset :skip
|
49
|
+
|
50
|
+
def limit(conditions)
|
51
|
+
clone_with limit: conditions
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def clone_with(condition)
|
57
|
+
self.class.new target, pipeline + [mongoize(condition)]
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_mongoized_hash(hash, key)
|
61
|
+
new_key = mongo_ready(key)
|
62
|
+
value = hash[key]
|
63
|
+
value = value.is_a?(Hash) ? mongoize(value) : mongo_ready(value)
|
64
|
+
{ new_key => evolve(value) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def evolve(value)
|
68
|
+
value.class.respond_to?(:evolve) ? value.class.evolve(value) : value
|
69
|
+
end
|
70
|
+
|
71
|
+
def mongo_ready(object)
|
72
|
+
object.is_a?(Symbol) ? "$#{object}" : object
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/casino/query.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Casino
|
2
|
+
class Query
|
3
|
+
attr_accessor :label, :criteria, :conditions
|
4
|
+
|
5
|
+
def initialize(label, conditions, *criteria)
|
6
|
+
self.label = label
|
7
|
+
self.conditions = conditions
|
8
|
+
self.criteria = criteria
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge(dimension)
|
12
|
+
self.class.new(label, build_conditions(dimension), *criteria)
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_conditions(dimension)
|
16
|
+
criteria.map do |condition|
|
17
|
+
[ dimension.approach[:operator],
|
18
|
+
{ dimension.field => condition } ]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/casino/store.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Casino
|
2
|
+
class Store
|
3
|
+
attr_accessor :collection_name
|
4
|
+
|
5
|
+
delegate :drop, :find, :indexes, :insert, to: :collection
|
6
|
+
|
7
|
+
delegate :count, :distinct, :each, :first, :limit, :remove, :remove_all,
|
8
|
+
:select, :skip, :sort, :update, :update_all, :upsert, to: :query
|
9
|
+
|
10
|
+
delegate :where, :in, to: :criteria
|
11
|
+
|
12
|
+
def initialize(collection_name)
|
13
|
+
self.collection_name = collection_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def collection
|
17
|
+
@collection ||= session[collection_name.to_sym]
|
18
|
+
end
|
19
|
+
|
20
|
+
def query
|
21
|
+
@query ||= collection.find
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge(*documents)
|
25
|
+
documents.each do |document|
|
26
|
+
criteria = find _id: document['_id']
|
27
|
+
criteria.upsert document
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def criteria
|
32
|
+
@criteria ||= build_criteria
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def session
|
38
|
+
@session ||= Mongoid.default_session
|
39
|
+
end
|
40
|
+
|
41
|
+
def build_criteria
|
42
|
+
klass = Class.new
|
43
|
+
klass.send(:include, Mongoid::Document)
|
44
|
+
klass.store_in collection: collection_name
|
45
|
+
klass.scoped
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
random_number = -> { rand(3) + 1 }
|
2
|
+
|
3
|
+
Fabricator(:model) do
|
4
|
+
created_at { Date.today }
|
5
|
+
source { %w(Google Facebook Twitter).shuffle.first }
|
6
|
+
end
|
7
|
+
|
8
|
+
Fabricator(:model_with_notes, from: :model) do
|
9
|
+
after_create do |model|
|
10
|
+
random_number.call.times { model.notes << Fabricate(:note, model: model) }
|
11
|
+
end
|
12
|
+
end
|