Class: Puffs::SQLRelation

Inherits:
Object
  • Object
show all
Defined in:
lib/relation.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ SQLRelation

Returns a new instance of SQLRelation.



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/relation.rb', line 46

def initialize(options)
  defaults =
  {
    klass: nil,
    loaded: false,
    collection: []
  }

  @klass      = options[:klass]
  @collection = options[:collection] || defaults[:collection]
  @loaded     = options[:loaded]     || defaults[:loaded]
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

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



146
147
148
# File 'lib/relation.rb', line 146

def method_missing(method, *args, &block)
  self.to_a.send(method, *args, &block)
end

Instance Attribute Details

#collectionObject (readonly)

Returns the value of attribute collection.



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

def collection
  @collection
end

#included_relationsObject

Returns the value of attribute included_relations.



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

def included_relations
  @included_relations
end

#klassObject (readonly)

Returns the value of attribute klass.



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

def klass
  @klass
end

#loadedObject (readonly)

Returns the value of attribute loaded.



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

def loaded
  @loaded
end

#sql_countObject (readonly)

Returns the value of attribute sql_count.



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

def sql_count
  @sql_count
end

#sql_limitObject (readonly)

Returns the value of attribute sql_limit.



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

def sql_limit
  @sql_limit
end

Class Method Details

.build_association(base, included, method_name) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/relation.rb', line 5

def self.build_association(base, included, method_name)
  base.included_relations << included

  assoc_options = base.klass.assoc_options[method_name]
  has_many = assoc_options.class == HasManyOptions

  if has_many
    i_send = assoc_options.foreign_key
    b_send = assoc_options.primary_key
  else
    i_send = assoc_options.primary_key
    b_send = assoc_options.foreign_key
  end

  match = proc do
    selection = included.select do |i_sql_obj|
      i_sql_obj.send(i_send) == self.send(b_send)
    end

    associated = has_many ? selection : selection.first

    #After we find our values iteratively, we overwrite the method again
    #to the result values to reduce future lookup time to O(1).
    new_match = proc { associated }
    Puffs::SQLObject.define_singleton_method_by_proc(
      self, method_name, new_match)

    associated
  end

  #we overwrite the association method for each SQLObject in the
  #collection so that it points to our cached relation and doesn't fire a query.
  base.collection.each do |b_sql_obj|
    Puffs::SQLObject.define_singleton_method_by_proc(
      b_sql_obj, method_name, match)
  end
end

Instance Method Details

#<<(item) ⇒ Object



59
60
61
62
63
# File 'lib/relation.rb', line 59

def <<(item)
  if item.class == klass
    @collection << item
  end
end

#countObject



65
66
67
68
# File 'lib/relation.rb', line 65

def count
  @sql_count = true
  load
end

#includes(klass) ⇒ Object



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

def includes(klass)
  includes_params << klass
  self
end

#includes_paramsObject



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

def includes_params
  @includes_params ||= []
end

#limit(n) ⇒ Object



83
84
85
86
# File 'lib/relation.rb', line 83

def limit(n)
  @sql_limit = n
  self
end

#loadObject



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/relation.rb', line 88

def load
  if !loaded
    puts "LOADING #{table_name}"
      results = DBConnection.execute(<<-SQL, sql_params[:values])
      SELECT
        #{sql_count ? "COUNT(*)" : self.table_name.to_s + ".*"}
      FROM
        #{self.table_name}
      #{sql_params[:where]}
        #{sql_params[:params]}
      #{order_by_string}
      #{"LIMIT #{sql_limit}" if sql_limit};
    SQL

    results = sql_count ? results.first.values.first : parse_all(results)
  end

  results = results || self

  unless includes_params.empty?
    results = load_includes(results)
  end

  results
end

#load_includes(relation) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/relation.rb', line 114

def load_includes(relation)
  includes_params.each do |param|
    if relation.klass.has_association?(param)
      puts "LOADING #{param.to_s}"
      assoc = klass.assoc_options[param]
      f_k = assoc.foreign_key
      p_k = assoc.primary_key
      includes_table = assoc.table_name.to_s
      in_ids = relation.collection.map do |sqlobject|
        sqlobject.id
      end.join(", ")

      has_many = assoc.class == HasManyOptions

      results = DBConnection.execute(<<-SQL)
        SELECT
          #{includes_table}.*
        FROM
          #{includes_table}
        WHERE
          #{includes_table}.#{has_many ? f_k : p_k}
        IN
          (#{in_ids});
      SQL
      included = assoc.model_class.parse_all(results)
      Puffs::SQLRelation.build_association(relation, included, param)
    end
  end

  relation
end

#order(params) ⇒ Object



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

def order(params)
  if params.is_a?(Hash)
    order_params_hash.merge!(params)
  else
    order_params_hash.merge!(params => :asc)
  end
  self
end

#order_by_stringObject



163
164
165
166
167
168
169
# File 'lib/relation.rb', line 163

def order_by_string
  hash_string = order_params_hash.map do |column, asc_desc|
    "#{column} #{asc_desc.to_s.upcase}"
  end.join(", ")

  hash_string.empty? ? "" : "ORDER BY #{hash_string}"
end

#order_params_hashObject



159
160
161
# File 'lib/relation.rb', line 159

def order_params_hash
  @order_params_hash ||= {}
end

#parse_all(attributes) ⇒ Object



171
172
173
# File 'lib/relation.rb', line 171

def parse_all(attributes)
  klass.parse_all(attributes).where(where_params_hash).includes(includes_params)
end

#sql_paramsObject



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

def sql_params
  params, values = [], []

  i = 1
  where_params_hash.map do |attribute, value|
    params << "#{attribute} = $#{i}"
    values << value
    i += 1
  end

  { params: params.join(" AND "),
    where: params.empty? ? nil : "WHERE",
    values: values }
end

#table_nameObject



190
191
192
# File 'lib/relation.rb', line 190

def table_name
  klass.table_name
end

#to_aObject



194
195
196
# File 'lib/relation.rb', line 194

def to_a
  self.load.collection
end

#where(params) ⇒ Object



202
203
204
205
# File 'lib/relation.rb', line 202

def where(params)
  where_params_hash.merge!(params)
  self
end

#where_params_hashObject



198
199
200
# File 'lib/relation.rb', line 198

def where_params_hash
  @where_params_hash ||= {}
end