Class: ActiveAggregate::Relation

Inherits:
Object
  • Object
show all
Includes:
ActiveSupport::Concern
Defined in:
lib/active_aggregate/relation.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scope_class, body = nil, options = {}) ⇒ Relation

Returns a new instance of Relation.



7
8
9
10
11
12
13
14
15
16
# File 'lib/active_aggregate/relation.rb', line 7

def initialize(scope_class, body = nil, options = {})
  @scope_class = scope_class
  @cacheable = true
  if body.respond_to?(:call)
    init_default_value(options)
    @body = body
  else
    init_default_value(body.nil? ? options : body)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(scope_name, *args, &block) ⇒ Object



131
132
133
134
135
136
137
138
# File 'lib/active_aggregate/relation.rb', line 131

def method_missing(scope_name, *args, &block)
  name = scope_name.to_sym
  if scope?(scope_name)
    merge(scope_class.public_send(name, *args, &block))
  else
    super
  end
end

Instance Attribute Details

#bodyObject (readonly)

Returns the value of attribute body.



4
5
6
# File 'lib/active_aggregate/relation.rb', line 4

def body
  @body
end

#cacheableObject

Returns the value of attribute cacheable.



5
6
7
# File 'lib/active_aggregate/relation.rb', line 5

def cacheable
  @cacheable
end

#scope_classObject (readonly)

Returns the value of attribute scope_class.



4
5
6
# File 'lib/active_aggregate/relation.rb', line 4

def scope_class
  @scope_class
end

Instance Method Details

#add_pipeline(*stages) ⇒ Object



119
120
121
# File 'lib/active_aggregate/relation.rb', line 119

def add_pipeline(*stages)
  @pipeline.push(*stages.flatten)
end

#aggregate(options = {}, *args) ⇒ Object



108
109
110
# File 'lib/active_aggregate/relation.rb', line 108

def aggregate(options = {}, *args)
  model.collection.aggregate(generate_execute_pipeline(options), *args)
end

#all_of(*args) ⇒ Object



166
167
168
# File 'lib/active_aggregate/relation.rb', line 166

def all_of(*args)
  query_criteria(model.all_of(*args))
end

#any_of(*args) ⇒ Object



162
163
164
# File 'lib/active_aggregate/relation.rb', line 162

def any_of(*args)
  query_criteria(model.any_of(*args))
end

#exposeObject



62
63
64
65
66
67
68
69
70
71
# File 'lib/active_aggregate/relation.rb', line 62

def expose
  {
    pipeline: @pipeline,
    criteria: @criteria,
    group: @group,
    project: @project,
    sort: @sort,
    limit: @limit
  }
end

#generate(*args) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/active_aggregate/relation.rb', line 94

def generate(*args)
  return self if body.nil?
  result = scope_class.instance_exec(*args, &body)
  case result
  when Hash
    init_default_value(merge_exposed(result))
    self
  when self.class
    merge(result)
  else
    self
  end
end

#generate_execute_pipeline(select_all: false, aggregate: {}) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/active_aggregate/relation.rb', line 82

def generate_execute_pipeline(select_all: false, aggregate: {})
  merge(aggregate) if aggregate.present?
  [].tap do |execute_pipeline|
    execute_pipeline << { '$match': selector } if selector.present?
    execute_pipeline << { '$group': @group } if @group.present?
    execute_pipeline << { '$sort': @sort } if @sort.present?
    execute_pipeline << { '$project': generate_project } if select_all || @project.present?
    execute_pipeline << { '$limit': @limit } if @limit.present?
    execute_pipeline.push(*@pipeline)
  end
end

#generate_projectObject



73
74
75
76
# File 'lib/active_aggregate/relation.rb', line 73

def generate_project
  return @project if @project.present?
  selector.keys.each_with_object({}) { |con, hashes| hashes[con] = "$#{con}" }
end

#group(options) ⇒ Object



40
41
42
# File 'lib/active_aggregate/relation.rb', line 40

def group(options)
  query(group: options)
end

#in(*args) ⇒ Object Also known as: where_in



144
145
146
# File 'lib/active_aggregate/relation.rb', line 144

def in(*args)
  query_criteria(model.in(*args))
end

#limit(options) ⇒ Object



57
58
59
60
# File 'lib/active_aggregate/relation.rb', line 57

def limit(options)
  @limit = options
  self
end

#merge(relation) ⇒ Object



170
171
172
173
174
175
# File 'lib/active_aggregate/relation.rb', line 170

def merge(relation)
  if !relation.is_a?(ActiveAggregate::Relation) || relation.scope_class != scope_class
    raise TypeError, "#relation much be an ActiveAggregate::Relation of #{scope_class}"
  end
  self.class.new(scope_class, merge_exposed(relation.expose))
end

#merge_exposed(exposed) ⇒ Object



177
178
179
180
181
182
183
184
185
186
# File 'lib/active_aggregate/relation.rb', line 177

def merge_exposed(exposed)
  {
    pipeline: @pipeline + (exposed[:pipeline] || []),
    criteria: [@criteria, format_criteria(exposed[:criteria])].compact.reduce(&:merge),
    group: [@group, exposed[:group]].compact.reduce(&:deep_merge),
    project: [@project, exposed[:project]].compact.reduce(&:deep_merge),
    sort: exposed[:sort] || nil,
    limit: exposed[:limit] || nil
  }
end

#override(*stages) ⇒ Object



123
124
125
# File 'lib/active_aggregate/relation.rb', line 123

def override(*stages)
  @pipeline = stages.flatten
end

#pipeline(options) ⇒ Object



44
45
46
# File 'lib/active_aggregate/relation.rb', line 44

def pipeline(options)
  query(pipeline: options)
end

#pluck(*fields) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
# File 'lib/active_aggregate/relation.rb', line 148

def pluck(*fields)
  fields = Array.wrap(fields).map(&:to_s)
  to_a.each_with_object([]) do |doc, result|
    record = if fields.size == 1
               doc[fields.first]
             else
               doc.slice(*fields).values
             end
    result << record unless record.nil?
  end
end

#project(options) ⇒ Object



48
49
50
# File 'lib/active_aggregate/relation.rb', line 48

def project(options)
  query(project: options)
end

#query(options) ⇒ Object



25
26
27
28
29
30
31
32
33
34
# File 'lib/active_aggregate/relation.rb', line 25

def query(options)
  case options
  when Hash
    merge(self.class.new(scope_class, options))
  when self.class
    merge(options)
  else
    self
  end
end

#query_criteria(options) ⇒ Object



36
37
38
# File 'lib/active_aggregate/relation.rb', line 36

def query_criteria(options)
  query(criteria: options)
end

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


127
128
129
# File 'lib/active_aggregate/relation.rb', line 127

def respond_to_missing?(method_name, include_private = false)
  scope?(scope_name) || super
end

#selectorObject



78
79
80
# File 'lib/active_aggregate/relation.rb', line 78

def selector
  @criteria ? @criteria.selector : {}
end

#sort(options) ⇒ Object



52
53
54
55
# File 'lib/active_aggregate/relation.rb', line 52

def sort(options)
  @sort = options
  self
end

#to_aObject Also known as: load



112
113
114
115
# File 'lib/active_aggregate/relation.rb', line 112

def to_a
  return @as_array if cacheable && @as_array
  aggregate.to_a.tap { |array| @as_array ||= array if cacheable }
end

#where(*args) ⇒ Object



140
141
142
# File 'lib/active_aggregate/relation.rb', line 140

def where(*args)
  query_criteria(model.where(*args))
end