puffs 0.2.05 → 0.2.06
Sign up to get free protection for your applications and to get access to all the features.
- 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
|