active_aggregate 0.0.2

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.
@@ -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: []