puffs 0.2.05 → 0.2.06
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.
- checksums.yaml +4 -4
- data/lib/relation.rb +56 -62
- data/lib/sql_object/associatable.rb +96 -76
- data/lib/sql_object/searchable.rb +32 -0
- data/lib/sql_object/sql_object.rb +15 -45
- data/puffs.gemspec +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffc8090b37bd60c44f6e9ab1c3b03a13efdd8245
|
4
|
+
data.tar.gz: ee5f09a1613a4536a6ce8e22bae477d532b5b94e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29c2b60efd307a57c81242884f8285d17496fdbec2363063132d28b9b0315b5dfcfbcf7d3694c0c68dd4f450c785849ef9b36e606f7e80cde6c1bb18d7a7c27d
|
7
|
+
data.tar.gz: 37f8b2b9207f48566161989d72892203640cae3fa92b2f544f1ccd1cb8d0344c802b5f2e7d0dd8b0404ca8e5bc1817f10616ae87520aa68575d8582319c51773
|
data/lib/relation.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
require_relative './../lib/db_connection'
|
2
|
-
require_relative 'sql_object/sql_object'
|
3
|
-
|
4
1
|
module Puffs
|
2
|
+
# Queries made through Puffs::SQLObject return instance of SQLRelation
|
5
3
|
class SQLRelation
|
6
4
|
def self.build_association(base, included, method_name)
|
7
5
|
base.included_relations << included
|
@@ -19,13 +17,13 @@ module Puffs
|
|
19
17
|
|
20
18
|
match = proc do
|
21
19
|
selection = included.select do |i_sql_obj|
|
22
|
-
i_sql_obj.send(i_send) ==
|
20
|
+
i_sql_obj.send(i_send) == send(b_send)
|
23
21
|
end
|
24
22
|
|
25
23
|
associated = has_many ? selection : selection.first
|
26
24
|
|
27
|
-
#After we find our values iteratively, we overwrite the method again
|
28
|
-
#to the result values to reduce future lookup time to O(1).
|
25
|
+
# After we find our values iteratively, we overwrite the method again
|
26
|
+
# to the result values to reduce future lookup time to O(1).
|
29
27
|
new_match = proc { associated }
|
30
28
|
Puffs::SQLObject.define_singleton_method_by_proc(
|
31
29
|
self, method_name, new_match)
|
@@ -33,8 +31,9 @@ module Puffs
|
|
33
31
|
associated
|
34
32
|
end
|
35
33
|
|
36
|
-
#
|
37
|
-
#collection so that it points to our cached relation and
|
34
|
+
# We overwrite the association method for each SQLObject in the
|
35
|
+
# collection so that it points to our cached relation and
|
36
|
+
# doesn't fire a query.
|
38
37
|
base.collection.each do |b_sql_obj|
|
39
38
|
Puffs::SQLObject.define_singleton_method_by_proc(
|
40
39
|
b_sql_obj, method_name, match)
|
@@ -46,21 +45,20 @@ module Puffs
|
|
46
45
|
|
47
46
|
def initialize(options)
|
48
47
|
defaults =
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
48
|
+
{
|
49
|
+
loaded: false,
|
50
|
+
collection: []
|
51
|
+
}
|
52
|
+
|
53
|
+
merged_options = defaults.merge(options)
|
54
54
|
|
55
55
|
@klass = options[:klass]
|
56
|
-
@collection =
|
57
|
-
@loaded =
|
56
|
+
@collection = merged_options[:collection]
|
57
|
+
@loaded = merged_options[:loaded]
|
58
58
|
end
|
59
59
|
|
60
60
|
def <<(item)
|
61
|
-
if item.class == klass
|
62
|
-
@collection << item
|
63
|
-
end
|
61
|
+
@collection << item if item.class == klass
|
64
62
|
end
|
65
63
|
|
66
64
|
def count
|
@@ -87,13 +85,13 @@ module Puffs
|
|
87
85
|
end
|
88
86
|
|
89
87
|
def load
|
90
|
-
|
88
|
+
unless loaded
|
91
89
|
puts "LOADING #{table_name}"
|
92
|
-
|
90
|
+
results = Puffs::DBConnection.execute(<<-SQL, sql_params[:values])
|
93
91
|
SELECT
|
94
|
-
#{sql_count ?
|
92
|
+
#{sql_count ? 'COUNT(*)' : table_name.to_s + '.*'}
|
95
93
|
FROM
|
96
|
-
#{
|
94
|
+
#{table_name}
|
97
95
|
#{sql_params[:where]}
|
98
96
|
#{sql_params[:params]}
|
99
97
|
#{order_by_string}
|
@@ -103,56 +101,49 @@ module Puffs
|
|
103
101
|
results = sql_count ? results.first.values.first : parse_all(results)
|
104
102
|
end
|
105
103
|
|
106
|
-
results
|
107
|
-
|
108
|
-
unless includes_params.empty?
|
109
|
-
results = load_includes(results)
|
110
|
-
end
|
111
|
-
|
104
|
+
results ||= self
|
105
|
+
results = load_includes(results) unless includes_params.empty?
|
112
106
|
results
|
113
107
|
end
|
114
108
|
|
115
109
|
def load_includes(relation)
|
116
110
|
includes_params.each do |param|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
included = assoc.model_class.parse_all(results)
|
140
|
-
SQLRelation.build_association(relation, included, param)
|
141
|
-
end
|
111
|
+
next unless relation.klass.has_association?(param)
|
112
|
+
|
113
|
+
puts "LOADING #{param}"
|
114
|
+
assoc = klass.assoc_options[param]
|
115
|
+
f_k = assoc.foreign_key
|
116
|
+
p_k = assoc.primary_key
|
117
|
+
includes_table = assoc.table_name.to_s
|
118
|
+
in_ids = relation.collection.map(&:id).join(', ')
|
119
|
+
has_many = assoc.class == HasManyOptions
|
120
|
+
|
121
|
+
results = Puffs::DBConnection.execute(<<-SQL)
|
122
|
+
SELECT
|
123
|
+
#{includes_table}.*
|
124
|
+
FROM
|
125
|
+
#{includes_table}
|
126
|
+
WHERE
|
127
|
+
#{includes_table}.#{has_many ? f_k : p_k}
|
128
|
+
IN
|
129
|
+
(#{in_ids});
|
130
|
+
SQL
|
131
|
+
included = assoc.model_class.parse_all(results)
|
132
|
+
SQLRelation.build_association(relation, included, param)
|
142
133
|
end
|
143
134
|
|
144
135
|
relation
|
145
136
|
end
|
146
137
|
|
147
138
|
def method_missing(method, *args, &block)
|
148
|
-
|
139
|
+
to_a.send(method, *args, &block)
|
149
140
|
end
|
150
141
|
|
151
142
|
def order(params)
|
152
143
|
if params.is_a?(Hash)
|
153
144
|
order_params_hash.merge!(params)
|
154
145
|
else
|
155
|
-
order_params_hash
|
146
|
+
order_params_hash[params] = :asc
|
156
147
|
end
|
157
148
|
self
|
158
149
|
end
|
@@ -164,17 +155,20 @@ module Puffs
|
|
164
155
|
def order_by_string
|
165
156
|
hash_string = order_params_hash.map do |column, asc_desc|
|
166
157
|
"#{column} #{asc_desc.to_s.upcase}"
|
167
|
-
end.join(
|
158
|
+
end.join(', ')
|
168
159
|
|
169
|
-
hash_string.empty? ?
|
160
|
+
hash_string.empty? ? '' : "ORDER BY #{hash_string}"
|
170
161
|
end
|
171
162
|
|
172
163
|
def parse_all(attributes)
|
173
|
-
klass.parse_all(attributes)
|
164
|
+
klass.parse_all(attributes)
|
165
|
+
.where(where_params_hash)
|
166
|
+
.includes(includes_params)
|
174
167
|
end
|
175
168
|
|
176
169
|
def sql_params
|
177
|
-
params
|
170
|
+
params = []
|
171
|
+
values = []
|
178
172
|
|
179
173
|
i = 1
|
180
174
|
where_params_hash.map do |attribute, value|
|
@@ -183,8 +177,8 @@ module Puffs
|
|
183
177
|
i += 1
|
184
178
|
end
|
185
179
|
|
186
|
-
{ params: params.join(
|
187
|
-
where: params.empty? ? nil :
|
180
|
+
{ params: params.join(' AND '),
|
181
|
+
where: params.empty? ? nil : 'WHERE',
|
188
182
|
values: values }
|
189
183
|
end
|
190
184
|
|
@@ -193,7 +187,7 @@ module Puffs
|
|
193
187
|
end
|
194
188
|
|
195
189
|
def to_a
|
196
|
-
|
190
|
+
load.collection
|
197
191
|
end
|
198
192
|
|
199
193
|
def where_params_hash
|
@@ -1,99 +1,119 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
3
|
+
# Used in HasManyOptions and BelongsToOptions
|
4
|
+
class AssocOptions
|
5
|
+
attr_accessor(
|
6
|
+
:foreign_key,
|
7
|
+
:class_name,
|
8
|
+
:primary_key
|
9
|
+
)
|
10
|
+
|
11
|
+
def model_class
|
12
|
+
class_name.constantize
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
15
|
+
def table_name
|
16
|
+
model_class.table_name
|
18
17
|
end
|
18
|
+
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
# Used to build belongs_to associations.
|
21
|
+
class BelongsToOptions < AssocOptions
|
22
|
+
def initialize(name, options = {})
|
23
|
+
defaults = {
|
24
|
+
primary_key: :id,
|
25
|
+
foreign_key: "#{name}_id".to_sym,
|
26
|
+
class_name: name.to_s.capitalize
|
27
|
+
}
|
28
|
+
|
29
|
+
merged_options = defaults.merge(options)
|
30
|
+
|
31
|
+
@primary_key = merged_options[:primary_key]
|
32
|
+
@foreign_key = merged_options[:foreign_key]
|
33
|
+
@class_name = merged_options[:class_name]
|
26
34
|
end
|
35
|
+
end
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
37
|
+
# Used to build has_many associations.
|
38
|
+
class HasManyOptions < AssocOptions
|
39
|
+
def initialize(name, self_class_name, options = {})
|
40
|
+
defaults = {
|
41
|
+
primary_key: :id,
|
42
|
+
foreign_key: "#{self_class_name.to_s.underscore}_id".to_sym,
|
43
|
+
class_name: name.to_s.singularize.camelcase
|
44
|
+
}
|
45
|
+
|
46
|
+
merged_options = defaults.merge(options)
|
47
|
+
|
48
|
+
@primary_key = merged_options[:primary_key]
|
49
|
+
@foreign_key = merged_options[:foreign_key]
|
50
|
+
@class_name = merged_options[:class_name]
|
34
51
|
end
|
52
|
+
end
|
35
53
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
54
|
+
# Used to map association methods.
|
55
|
+
module Associatable
|
56
|
+
def belongs_to(name, options = {})
|
57
|
+
options = BelongsToOptions.new(name, options)
|
58
|
+
assoc_options[name] = options
|
40
59
|
|
41
|
-
|
42
|
-
|
43
|
-
|
60
|
+
define_method(name) do
|
61
|
+
foreign_key_value = send(options.foreign_key)
|
62
|
+
return nil if foreign_key_value.nil?
|
44
63
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
64
|
+
options.model_class
|
65
|
+
.where(options.primary_key => foreign_key_value)
|
66
|
+
.first
|
49
67
|
end
|
68
|
+
end
|
50
69
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
define_method(name) do
|
56
|
-
target_key_value = self.send(options.primary_key)
|
57
|
-
return nil if target_key_value.nil?
|
58
|
-
options.model_class
|
59
|
-
.where(options.foreign_key => target_key_value)
|
60
|
-
.to_a
|
61
|
-
end
|
62
|
-
end
|
70
|
+
def has_many(name, options = {})
|
71
|
+
options = HasManyOptions.new(name, to_s, options)
|
72
|
+
assoc_options[name] = options
|
63
73
|
|
64
|
-
|
65
|
-
|
74
|
+
define_method(name) do
|
75
|
+
target_key_value = send(options.primary_key)
|
76
|
+
return nil if target_key_value.nil?
|
77
|
+
options.model_class
|
78
|
+
.where(options.foreign_key => target_key_value)
|
79
|
+
.to_a
|
66
80
|
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def assoc_options
|
84
|
+
@assoc_options ||= {}
|
85
|
+
end
|
67
86
|
|
68
|
-
|
69
|
-
|
87
|
+
def has_one_through(name, through_name, source_name)
|
88
|
+
through_options = assoc_options[through_name]
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
90
|
+
define_method(name) do
|
91
|
+
source_options =
|
92
|
+
through_options.model_class.assoc_options[source_name]
|
93
|
+
through_pk = through_options.primary_key
|
94
|
+
key_val = send(through_options.foreign_key)
|
76
95
|
|
77
|
-
|
78
|
-
|
79
|
-
|
96
|
+
source_options.model_class
|
97
|
+
.includes(through_options.model_class)
|
98
|
+
.where(through_pk => key_val)
|
99
|
+
.first
|
80
100
|
end
|
101
|
+
end
|
81
102
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
103
|
+
def has_many_through(name, through_name, source_name)
|
104
|
+
through_options = assoc_options[through_name]
|
105
|
+
define_method(name) do
|
106
|
+
through_fk = through_options.foreign_key
|
107
|
+
through_class = through_options.model_class
|
108
|
+
key_val = send(through_options.primary_key)
|
109
|
+
|
110
|
+
# 2 queries, we could reduce to 1 by writing Puffs::SQLRelation.join.
|
111
|
+
through_class.where(through_fk => key_val)
|
112
|
+
.includes(source_name)
|
113
|
+
.load
|
114
|
+
.included_relations
|
115
|
+
.first
|
116
|
+
.to_a
|
97
117
|
end
|
98
118
|
end
|
99
119
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Search methods for Puffs::SQLObject
|
2
|
+
module Searchable
|
3
|
+
RELATION_METHODS = [
|
4
|
+
:limit, :includes, :where, :order
|
5
|
+
].freeze
|
6
|
+
|
7
|
+
RELATION_METHODS.each do |method|
|
8
|
+
define_method(method) do |arg|
|
9
|
+
Puffs::SQLRelation.new(klass: self).send(method, arg)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def all
|
14
|
+
where({})
|
15
|
+
end
|
16
|
+
|
17
|
+
def count
|
18
|
+
all.count
|
19
|
+
end
|
20
|
+
|
21
|
+
def find(id)
|
22
|
+
where(id: id).first
|
23
|
+
end
|
24
|
+
|
25
|
+
def first
|
26
|
+
all.limit(1).first
|
27
|
+
end
|
28
|
+
|
29
|
+
def last
|
30
|
+
all.order(id: :desc).limit(1).first
|
31
|
+
end
|
32
|
+
end
|
@@ -1,48 +1,30 @@
|
|
1
|
+
require 'active_support/inflector'
|
1
2
|
require_relative '../../lib/db_connection'
|
2
3
|
require_relative 'associatable'
|
3
4
|
require_relative '../relation'
|
4
|
-
|
5
|
-
# require_relative '../puffs'
|
5
|
+
require_relative 'searchable'
|
6
6
|
|
7
7
|
module Puffs
|
8
|
+
# Base Model class for Puffs Orm.
|
8
9
|
class SQLObject
|
9
10
|
extend Associatable
|
10
|
-
|
11
|
-
RELATION_METHODS = [
|
12
|
-
:limit, :includes, :where, :order
|
13
|
-
]
|
14
|
-
|
15
|
-
RELATION_METHODS.each do |method|
|
16
|
-
define_singleton_method(method) do |arg|
|
17
|
-
Puffs::SQLRelation.new(klass: self).send(method, arg)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.all
|
22
|
-
where({})
|
23
|
-
end
|
11
|
+
extend Searchable
|
24
12
|
|
25
13
|
def self.columns
|
26
14
|
Puffs::DBConnection.columns(table_name)
|
27
15
|
end
|
28
16
|
|
29
|
-
def self.count
|
30
|
-
all.count
|
31
|
-
end
|
32
|
-
|
33
17
|
def self.define_singleton_method_by_proc(obj, name, block)
|
34
18
|
metaclass = class << obj; self; end
|
35
19
|
metaclass.send(:define_method, name, block)
|
36
20
|
end
|
37
21
|
|
38
22
|
def self.destroy_all!
|
39
|
-
|
40
|
-
entry.destroy!
|
41
|
-
end
|
23
|
+
all.each(&:destroy!)
|
42
24
|
end
|
43
25
|
|
44
26
|
def self.finalize!
|
45
|
-
|
27
|
+
columns.each do |column|
|
46
28
|
define_method(column) do
|
47
29
|
attributes[column]
|
48
30
|
end
|
@@ -53,26 +35,14 @@ module Puffs
|
|
53
35
|
end
|
54
36
|
end
|
55
37
|
|
56
|
-
def self.find(id)
|
57
|
-
where(id: id).first
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.first
|
61
|
-
all.limit(1).first
|
62
|
-
end
|
63
|
-
|
64
38
|
def self.has_association?(association)
|
65
39
|
assoc_options.keys.include?(association)
|
66
40
|
end
|
67
41
|
|
68
|
-
def self.last
|
69
|
-
all.order(id: :desc).limit(1).first
|
70
|
-
end
|
71
|
-
|
72
42
|
def self.parse_all(results)
|
73
43
|
relation = Puffs::SQLRelation.new(klass: self, loaded: true)
|
74
44
|
results.each do |result|
|
75
|
-
relation <<
|
45
|
+
relation << new(result)
|
76
46
|
end
|
77
47
|
|
78
48
|
relation
|
@@ -83,7 +53,7 @@ module Puffs
|
|
83
53
|
end
|
84
54
|
|
85
55
|
def self.table_name
|
86
|
-
@table_name ||=
|
56
|
+
@table_name ||= to_s.downcase.tableize
|
87
57
|
end
|
88
58
|
|
89
59
|
def initialize(params = {})
|
@@ -92,7 +62,7 @@ module Puffs
|
|
92
62
|
raise "unknown attribute '#{attr_name}'"
|
93
63
|
end
|
94
64
|
|
95
|
-
|
65
|
+
send("#{attr_name}=", value)
|
96
66
|
end
|
97
67
|
end
|
98
68
|
|
@@ -102,7 +72,7 @@ module Puffs
|
|
102
72
|
|
103
73
|
def attribute_values
|
104
74
|
self.class.columns.map do |column|
|
105
|
-
|
75
|
+
send(column)
|
106
76
|
end
|
107
77
|
end
|
108
78
|
|
@@ -121,9 +91,9 @@ module Puffs
|
|
121
91
|
|
122
92
|
def insert
|
123
93
|
columns = self.class.columns.reject { |col| col == :id }
|
124
|
-
column_values = columns.map {|attr_name| send(attr_name)}
|
125
|
-
column_names = columns.join(
|
126
|
-
bind_params = (1..columns.length).map {|n| "$#{n}"}.join(
|
94
|
+
column_values = columns.map { |attr_name| send(attr_name) }
|
95
|
+
column_names = columns.join(', ')
|
96
|
+
bind_params = (1..columns.length).map { |n| "$#{n}" }.join(', ')
|
127
97
|
result = Puffs::DBConnection.execute(<<-SQL, column_values)
|
128
98
|
INSERT INTO
|
129
99
|
#{self.class.table_name} (#{column_names})
|
@@ -141,8 +111,8 @@ module Puffs
|
|
141
111
|
|
142
112
|
def update
|
143
113
|
set_line = self.class.columns.map do |column|
|
144
|
-
"#{column} = \'#{
|
145
|
-
end.join(
|
114
|
+
"#{column} = \'#{send(column)}\'"
|
115
|
+
end.join(', ')
|
146
116
|
|
147
117
|
Puffs::DBConnection.execute(<<-SQL)
|
148
118
|
UPDATE
|
data/puffs.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puffs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.06
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zachary Moroni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -186,6 +186,7 @@ files:
|
|
186
186
|
- lib/server_connection.rb
|
187
187
|
- lib/session.rb
|
188
188
|
- lib/sql_object/associatable.rb
|
189
|
+
- lib/sql_object/searchable.rb
|
189
190
|
- lib/sql_object/sql_object.rb
|
190
191
|
- puffs.gemspec
|
191
192
|
- readme.md
|