active_aggregate 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 653177242ed87c2c3965933b6a7932644528af9ad7a5a861d950c0e5b3bd7078
4
+ data.tar.gz: fcb515097fb8001c7cc314e7bca0131b1ab83ea155a3d69300537b77126bff9a
5
+ SHA512:
6
+ metadata.gz: f086dc4d9a449ba36d25d775e85c52b2bd61299a4cedfe104b3a715917cd9e0037affc4410bfa1c2b609d8000a5410c279fe4d0dd1a15034373c0301429e17a3
7
+ data.tar.gz: f6d84f33cf8c04003f3436df053fd5e86932c1edb7e04217768e5863b42f693fc1a9110e53b9939c1360b2483f9130c7f7b16de37e4df8ebd7720cfaa5efe2d6
@@ -0,0 +1,10 @@
1
+ # active_aggregate
2
+
3
+ - active_aggregate is a little helper support to queries by mongoDB aggregate more easily.
4
+ - A toolkit for building queries like ActiveRelation. Rich support for more flexible merge conditons, states
5
+
6
+ ## Getting started
7
+
8
+ ```ruby
9
+ gem install active_aggregate
10
+ ```
@@ -0,0 +1,55 @@
1
+ module ActiveAggregate::Concern
2
+ extend ActiveSupport::Concern
3
+
4
+ class_methods do
5
+ attr_reader :model, :criteria
6
+
7
+ delegate :query, :query_criteria, :group, :project, :pipeline, :group_by, :to_a,
8
+ :where, :in, :any_off, :all_of,
9
+ to: :all
10
+
11
+ def define_for(model)
12
+ @model = model
13
+ @criteria = model.all
14
+ end
15
+
16
+ def scope(name, *options)
17
+ raise TypeError, 'set defined scope for model first' unless model
18
+ scope_name = name.to_sym
19
+ scopes[scope_name] = ActiveAggregate::Relation.new(self, *options)
20
+ singleton_class.send(:define_method, scope_name) do |*args|
21
+ return scopes[scope_name].generate(*args)
22
+ end
23
+ end
24
+
25
+ def all
26
+ raise TypeError, 'set defined scope for model first' unless model
27
+ ActiveAggregate::Relation.new(self).generate
28
+ end
29
+
30
+ def scope_names
31
+ scopes.keys
32
+ end
33
+
34
+ def scope?(scope_name)
35
+ scopes.key?(scope_name.to_sym)
36
+ end
37
+
38
+ def respond_to_missing?(method_name, include_private = false)
39
+ scope?(method_name) || super
40
+ end
41
+
42
+ def method_missing(scope_name, *args, &block)
43
+ name = scope_name.to_sym
44
+ if scope?(name)
45
+ merge(public_send(name, *args, &block))
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def scopes
52
+ @scopes ||= {}
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,184 @@
1
+ class ActiveAggregate::Relation
2
+ include ActiveSupport::Concern
3
+
4
+ attr_reader :scope_class, :body
5
+
6
+ def initialize(scope_class, body = nil, options = {})
7
+ @scope_class = scope_class
8
+ if body.respond_to?(:call)
9
+ init_default_value(options)
10
+ @body = body
11
+ else
12
+ init_default_value(body.nil? ? options : body)
13
+ end
14
+ end
15
+
16
+ delegate :scope?, :scope_names, :model, to: :scope_class
17
+ delegate :selector, to: :criteria
18
+ delegate :to_a, :count, :first, to: :aggregate
19
+ delegate :select, :find, :last, :group_by, :each_with_object, :each,
20
+ :map, :reduce, :reject,
21
+ to: :to_a
22
+
23
+ def query(options)
24
+ case options
25
+ when Hash
26
+ merge(self.class.new(scope_class, options))
27
+ when self.class
28
+ merge(options)
29
+ else
30
+ self
31
+ end
32
+ end
33
+
34
+ def query_criteria(options)
35
+ query(criteria: options)
36
+ end
37
+
38
+ def group(options)
39
+ query(group: options)
40
+ end
41
+
42
+ def pipeline(options)
43
+ query(pipeline: options)
44
+ end
45
+
46
+ def project(options)
47
+ query(project: options)
48
+ end
49
+
50
+ def sort(options)
51
+ @sort = options
52
+ self
53
+ end
54
+
55
+ def limit(options)
56
+ @limit = options
57
+ self
58
+ end
59
+
60
+ def expose
61
+ {
62
+ pipeline: @pipeline,
63
+ criteria: @criteria,
64
+ group: @group,
65
+ project: @project,
66
+ sort: @sort,
67
+ limit: @limit
68
+ }
69
+ end
70
+
71
+ def generate_project
72
+ return @project if @project.present?
73
+ selector.keys.each_with_object({}) { |con, hashes| hashes[con] = "$#{con}" }
74
+ end
75
+
76
+ def selector
77
+ @criteria ? @criteria.selector : {}
78
+ end
79
+
80
+ def generate_execute_pipeline(select_all: false, aggregate: {})
81
+ merge(aggregate) if aggregate.present?
82
+ [].tap do |execute_pipeline|
83
+ execute_pipeline << { '$match': @criteria.selector } if @criteria
84
+ execute_pipeline << { '$group': @group } if @group.present?
85
+ execute_pipeline << { '$sort': @sort } if @sort.present?
86
+ execute_pipeline << { '$project': project_selector } if select_all || @project.present?
87
+ execute_pipeline << { '$limit': @limit } if @limit.present?
88
+ execute_pipeline.push(*@pipeline)
89
+ end
90
+ end
91
+
92
+ def generate(*args)
93
+ return self if body.nil?
94
+ result = scope_class.instance_exec(*args, &body)
95
+ case result
96
+ when Hash
97
+ init_default_value(merge_exposed(result))
98
+ self
99
+ when self.class
100
+ merge(result)
101
+ else
102
+ self
103
+ end
104
+ end
105
+
106
+ def aggregate(options = {}, *args)
107
+ model.collection.aggregate(generate_execute_pipeline(options), *args)
108
+ end
109
+
110
+ def add_pipeline(*stages)
111
+ @pipeline.push(*stages.flatten)
112
+ end
113
+
114
+ def override(*stages)
115
+ @pipeline = stages.flatten
116
+ end
117
+
118
+ def respond_to_missing?(method_name, include_private = false)
119
+ scope?(scope_name) || super
120
+ end
121
+
122
+ def method_missing(scope_name, *args, &block)
123
+ name = scope_name.to_sym
124
+ if scope?(scope_name)
125
+ merge(scope_class.public_send(name, *args, &block))
126
+ else
127
+ super
128
+ end
129
+ end
130
+
131
+ def where(*args)
132
+ query_criteria(model.where(*args))
133
+ end
134
+
135
+ def in(*args)
136
+ query_criteria(model.in(*args))
137
+ end
138
+
139
+ def any_of(*args)
140
+ query_criteria(model.any_of(*args))
141
+ end
142
+
143
+ def all_of(*args)
144
+ query_criteria(model.all_of(*args))
145
+ end
146
+
147
+ def merge(relation)
148
+ if !relation.is_a?(ActiveAggregate::Relation) || relation.scope_class != scope_class
149
+ raise TypeError, "#relation much be an ActiveAggregate::Relation of #{scope_class}"
150
+ end
151
+ self.class.new(scope_class, merge_exposed(relation.expose))
152
+ end
153
+
154
+ def merge_exposed(exposed)
155
+ {
156
+ pipeline: @pipeline + (exposed[:pipeline] || []),
157
+ criteria: [@criteria, format_criteria(exposed[:criteria])].compact.reduce(&:merge),
158
+ group: [@group, exposed[:group]].compact.reduce(&:deep_merge),
159
+ project: [@project, exposed[:project]].compact.reduce(&:deep_merge),
160
+ sort: exposed[:sort] || nil,
161
+ limit: exposed[:limit] || nil
162
+ }
163
+ end
164
+
165
+ private
166
+
167
+ def init_default_value(criteria: nil, pipeline: [], group: nil, project: nil, sort: nil, limit: nil)
168
+ @criteria = format_criteria(criteria)
169
+ @pipeline = pipeline.present? ? pipeline : []
170
+ @group = group.present? ? group : {}
171
+ @project = project.present? ? project : {}
172
+ @sort = sort
173
+ @limit = limit
174
+ end
175
+
176
+ def format_criteria(criteria)
177
+ case criteria
178
+ when Mongoid::Criteria
179
+ criteria
180
+ when self.class
181
+ format_criteria(criteria.expose[:criteria])
182
+ end
183
+ end
184
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_aggregate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Phan Quang Tien
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mongoid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.1
27
+ description: A toolkit for building queries like ActiveRelation. Rich support for
28
+ more flexible merge conditons, states
29
+ email:
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - README.md
35
+ - lib/active_aggregate/concern.rb
36
+ - lib/active_aggregate/relation.rb
37
+ homepage: https://github.com/phantien133/active_aggregate
38
+ licenses: []
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 2.2.3
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.0.6
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: active_aggregate is a little helper support to queries by mongoDB aggregate
59
+ more easily
60
+ test_files: []