puffs 0.1.1
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 +7 -0
- data/.DS_Store +0 -0
- data/.byebug_history +4 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +52 -0
- data/LICENSE.txt +21 -0
- data/Rakefile +6 -0
- data/app/controllers/cats_controller.rb +26 -0
- data/app/controllers/house_controller.rb +3 -0
- data/app/controllers/humans_controller.rb +3 -0
- data/app/models/cat.rb +7 -0
- data/app/models/house.rb +7 -0
- data/app/models/human.rb +8 -0
- data/app/views/cats_controller/index.html.erb +5 -0
- data/app/views/cats_controller/new.html.erb +11 -0
- data/app/views/cats_controller/show.html.erb +1 -0
- data/app/views/dogs_controller/index.html.erb +8 -0
- data/app/views/dogs_controller/new.html.erb +17 -0
- data/app/views/dogs_controller/show.html.erb +1 -0
- data/app/views/my_controller/counting_show.html.erb +1 -0
- data/app/views/my_controller/show.html.erb +1 -0
- data/bin/puffs +68 -0
- data/bin/rake +37 -0
- data/config/routes.rb +9 -0
- data/db/migrate/201600202411059024_create_cats.sql +5 -0
- data/db/migrate/201600202411059044_create_houses.sql +4 -0
- data/db/migrate/201600202411059044_create_humans.sql +6 -0
- data/db/seeds.rb +26 -0
- data/exit +1 -0
- data/lib/controller_base.rb +74 -0
- data/lib/db_connection.rb +112 -0
- data/lib/puffs.rb +7 -0
- data/lib/relation.rb +206 -0
- data/lib/router.rb +77 -0
- data/lib/server_connection.rb +19 -0
- data/lib/session.rb +28 -0
- data/lib/sql_object/associatable.rb +97 -0
- data/lib/sql_object/sql_object.rb +156 -0
- data/lib/version.rb +3 -0
- data/puffs.gemspec +33 -0
- metadata +132 -0
data/lib/relation.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require_relative './../lib/db_connection'
|
2
|
+
require_relative 'sql_object/sql_object'
|
3
|
+
|
4
|
+
class SQLRelation
|
5
|
+
def self.build_association(base, included, method_name)
|
6
|
+
base.included_relations << included
|
7
|
+
|
8
|
+
assoc_options = base.klass.assoc_options[method_name]
|
9
|
+
has_many = assoc_options.class == HasManyOptions
|
10
|
+
|
11
|
+
if has_many
|
12
|
+
i_send = assoc_options.foreign_key
|
13
|
+
b_send = assoc_options.primary_key
|
14
|
+
else
|
15
|
+
i_send = assoc_options.primary_key
|
16
|
+
b_send = assoc_options.foreign_key
|
17
|
+
end
|
18
|
+
|
19
|
+
match = proc do
|
20
|
+
selection = included.select do |i_sql_obj|
|
21
|
+
i_sql_obj.send(i_send) == self.send(b_send)
|
22
|
+
end
|
23
|
+
|
24
|
+
associated = has_many ? selection : selection.first
|
25
|
+
|
26
|
+
#After we find our values iteratively, we overwrite the method again
|
27
|
+
#to the result values to reduce future lookup time to O(1).
|
28
|
+
new_match = proc { associated }
|
29
|
+
SQLObject.define_singleton_method_by_proc(
|
30
|
+
self, method_name, new_match)
|
31
|
+
|
32
|
+
associated
|
33
|
+
end
|
34
|
+
|
35
|
+
#we overwrite the association method for each SQLObject in the
|
36
|
+
#collection so that it points to our cached relation and doesn't fire a query.
|
37
|
+
base.collection.each do |b_sql_obj|
|
38
|
+
SQLObject.define_singleton_method_by_proc(
|
39
|
+
b_sql_obj, method_name, match)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :klass, :collection, :loaded, :sql_count, :sql_limit
|
44
|
+
attr_accessor :included_relations
|
45
|
+
|
46
|
+
def initialize(options)
|
47
|
+
defaults =
|
48
|
+
{
|
49
|
+
klass: nil,
|
50
|
+
loaded: false,
|
51
|
+
collection: []
|
52
|
+
}
|
53
|
+
|
54
|
+
@klass = options[:klass]
|
55
|
+
@collection = options[:collection] || defaults[:collection]
|
56
|
+
@loaded = options[:loaded] || defaults[:loaded]
|
57
|
+
end
|
58
|
+
|
59
|
+
def <<(item)
|
60
|
+
if item.class == klass
|
61
|
+
@collection << item
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def count
|
66
|
+
@sql_count = true
|
67
|
+
load
|
68
|
+
end
|
69
|
+
|
70
|
+
def included_relations
|
71
|
+
@included_relations ||= []
|
72
|
+
end
|
73
|
+
|
74
|
+
def includes(klass)
|
75
|
+
includes_params << klass
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def includes_params
|
80
|
+
@includes_params ||= []
|
81
|
+
end
|
82
|
+
|
83
|
+
def limit(n)
|
84
|
+
@sql_limit = n
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def load
|
89
|
+
if !loaded
|
90
|
+
puts "LOADING #{table_name}"
|
91
|
+
results = DBConnection.execute(<<-SQL, sql_params[:values])
|
92
|
+
SELECT
|
93
|
+
#{sql_count ? "COUNT(*)" : self.table_name.to_s + ".*"}
|
94
|
+
FROM
|
95
|
+
#{self.table_name}
|
96
|
+
#{sql_params[:where]}
|
97
|
+
#{sql_params[:params]}
|
98
|
+
#{order_by_string}
|
99
|
+
#{"LIMIT #{sql_limit}" if sql_limit};
|
100
|
+
SQL
|
101
|
+
|
102
|
+
results = sql_count ? results.first.values.first : parse_all(results)
|
103
|
+
end
|
104
|
+
|
105
|
+
results = results || self
|
106
|
+
|
107
|
+
unless includes_params.empty?
|
108
|
+
results = load_includes(results)
|
109
|
+
end
|
110
|
+
|
111
|
+
results
|
112
|
+
end
|
113
|
+
|
114
|
+
def load_includes(relation)
|
115
|
+
includes_params.each do |param|
|
116
|
+
if relation.klass.has_association?(param)
|
117
|
+
puts "LOADING #{param.to_s}"
|
118
|
+
assoc = klass.assoc_options[param]
|
119
|
+
f_k = assoc.foreign_key
|
120
|
+
p_k = assoc.primary_key
|
121
|
+
includes_table = assoc.table_name.to_s
|
122
|
+
in_ids = relation.collection.map do |sqlobject|
|
123
|
+
sqlobject.id
|
124
|
+
end.join(", ")
|
125
|
+
|
126
|
+
has_many = assoc.class == HasManyOptions
|
127
|
+
|
128
|
+
results = DBConnection.execute(<<-SQL)
|
129
|
+
SELECT
|
130
|
+
#{includes_table}.*
|
131
|
+
FROM
|
132
|
+
#{includes_table}
|
133
|
+
WHERE
|
134
|
+
#{includes_table}.#{has_many ? f_k : p_k}
|
135
|
+
IN
|
136
|
+
(#{in_ids});
|
137
|
+
SQL
|
138
|
+
included = assoc.model_class.parse_all(results)
|
139
|
+
SQLRelation.build_association(relation, included, param)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
relation
|
144
|
+
end
|
145
|
+
|
146
|
+
def method_missing(method, *args, &block)
|
147
|
+
self.to_a.send(method, *args, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
def order(params)
|
151
|
+
if params.is_a?(Hash)
|
152
|
+
order_params_hash.merge!(params)
|
153
|
+
else
|
154
|
+
order_params_hash.merge!(params => :asc)
|
155
|
+
end
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
def order_params_hash
|
160
|
+
@order_params_hash ||= {}
|
161
|
+
end
|
162
|
+
|
163
|
+
def order_by_string
|
164
|
+
hash_string = order_params_hash.map do |column, asc_desc|
|
165
|
+
"#{column} #{asc_desc.to_s.upcase}"
|
166
|
+
end.join(", ")
|
167
|
+
|
168
|
+
hash_string.empty? ? "" : "ORDER BY #{hash_string}"
|
169
|
+
end
|
170
|
+
|
171
|
+
def parse_all(attributes)
|
172
|
+
klass.parse_all(attributes).where(where_params_hash).includes(includes_params)
|
173
|
+
end
|
174
|
+
|
175
|
+
def sql_params
|
176
|
+
params, values = [], []
|
177
|
+
|
178
|
+
i = 1
|
179
|
+
where_params_hash.map do |attribute, value|
|
180
|
+
params << "#{attribute} = $#{i}"
|
181
|
+
values << value
|
182
|
+
i += 1
|
183
|
+
end
|
184
|
+
|
185
|
+
{ params: params.join(" AND "),
|
186
|
+
where: params.empty? ? nil : "WHERE",
|
187
|
+
values: values }
|
188
|
+
end
|
189
|
+
|
190
|
+
def table_name
|
191
|
+
klass.table_name
|
192
|
+
end
|
193
|
+
|
194
|
+
def to_a
|
195
|
+
self.load.collection
|
196
|
+
end
|
197
|
+
|
198
|
+
def where_params_hash
|
199
|
+
@where_params_hash ||= {}
|
200
|
+
end
|
201
|
+
|
202
|
+
def where(params)
|
203
|
+
where_params_hash.merge!(params)
|
204
|
+
self
|
205
|
+
end
|
206
|
+
end
|
data/lib/router.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
project_root = File.dirname(File.absolute_path(__FILE__))
|
2
|
+
Dir.glob(project_root + '/../app/controllers/*.rb') { |file| require file }
|
3
|
+
|
4
|
+
class Route
|
5
|
+
attr_reader :pattern, :http_method, :controller_class, :action_name
|
6
|
+
|
7
|
+
def initialize(pattern, http_method, controller_class, action_name)
|
8
|
+
@pattern = pattern
|
9
|
+
@http_method = http_method
|
10
|
+
@controller_class = controller_class
|
11
|
+
@action_name = action_name
|
12
|
+
end
|
13
|
+
|
14
|
+
# checks if pattern matches path and method matches request method
|
15
|
+
def matches?(req)
|
16
|
+
pattern =~ req.path && req.request_method == http_method.to_s.upcase
|
17
|
+
end
|
18
|
+
|
19
|
+
# use pattern to pull out route params (save for later?)
|
20
|
+
# instantiate controller and call controller action
|
21
|
+
def run(req, res)
|
22
|
+
matches = pattern.match(req.path)
|
23
|
+
route_params = {}
|
24
|
+
|
25
|
+
matches.names.each do |name|
|
26
|
+
route_params[name] = matches[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
controller_class.new(req, res, route_params).invoke_action(action_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Router
|
34
|
+
attr_reader :routes
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@routes = []
|
38
|
+
end
|
39
|
+
|
40
|
+
# simply adds a new route to the list of routes
|
41
|
+
def add_route(pattern, method, controller_class, action_name)
|
42
|
+
routes << Route.new(pattern, method, controller_class, action_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# evaluate the proc in the context of the instance
|
46
|
+
# for syntactic sugar :)
|
47
|
+
def draw(&proc)
|
48
|
+
instance_eval(&proc)
|
49
|
+
end
|
50
|
+
|
51
|
+
# make each of these methods that
|
52
|
+
# when called add route
|
53
|
+
[:get, :post, :put, :delete].each do |http_method|
|
54
|
+
define_method(http_method) do |pattern, controller_class, action_name|
|
55
|
+
add_route(pattern, http_method, controller_class, action_name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# should return the route that matches this request
|
60
|
+
def match(req)
|
61
|
+
routes.each do |route|
|
62
|
+
return route if route.matches?(req)
|
63
|
+
end
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# either throw 404 or call run on a matched route
|
68
|
+
def run(req, res)
|
69
|
+
route = match(req)
|
70
|
+
if route
|
71
|
+
route.run(req, res)
|
72
|
+
else
|
73
|
+
res.status = 404
|
74
|
+
res.body = ["Sorry, Charlie. That page doesn't exist."]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require_relative 'router'
|
3
|
+
require_relative '../config/routes'
|
4
|
+
|
5
|
+
class ServerConnection
|
6
|
+
def self.start
|
7
|
+
app = Proc.new do |env|
|
8
|
+
req = Rack::Request.new(env)
|
9
|
+
res = Rack::Response.new
|
10
|
+
ROUTER.run(req, res)
|
11
|
+
res.finish
|
12
|
+
end
|
13
|
+
|
14
|
+
Rack::Server.start(
|
15
|
+
app: app,
|
16
|
+
Port: 3000
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
data/lib/session.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class Session
|
4
|
+
# find the cookie for this app
|
5
|
+
# deserialize the cookie into a hash
|
6
|
+
def initialize(req)
|
7
|
+
cookie = req.cookies['_rails_lite_app']
|
8
|
+
if cookie
|
9
|
+
@session_data = JSON.parse(cookie)
|
10
|
+
else
|
11
|
+
@session_data = {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@session_data[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, val)
|
20
|
+
@session_data[key] = val
|
21
|
+
end
|
22
|
+
|
23
|
+
# serialize the hash into json and save in a cookie
|
24
|
+
# add to the responses cookies
|
25
|
+
def store_session(res)
|
26
|
+
res.set_cookie("_rails_lite_app", { path: '/', value: @session_data.to_json })
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
|
3
|
+
class AssocOptions
|
4
|
+
attr_accessor(
|
5
|
+
:foreign_key,
|
6
|
+
:class_name,
|
7
|
+
:primary_key
|
8
|
+
)
|
9
|
+
|
10
|
+
def model_class
|
11
|
+
class_name.constantize
|
12
|
+
end
|
13
|
+
|
14
|
+
def table_name
|
15
|
+
model_class.table_name
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class BelongsToOptions < AssocOptions
|
20
|
+
def initialize(name, options = {})
|
21
|
+
@primary_key = options[:primary_key] || :id
|
22
|
+
@foreign_key = options[:foreign_key] || "#{name}_id".to_sym
|
23
|
+
@class_name = options[:class_name] || name.to_s.capitalize
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class HasManyOptions < AssocOptions
|
28
|
+
def initialize(name, self_class_name, options = {})
|
29
|
+
@primary_key = options[:primary_key] || :id
|
30
|
+
@foreign_key = options[:foreign_key] || "#{self_class_name.to_s.underscore}_id".to_sym
|
31
|
+
@class_name = options[:class_name] || name.to_s.singularize.camelcase
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
module Associatable
|
36
|
+
def belongs_to(name, options = {})
|
37
|
+
options = BelongsToOptions.new(name, options)
|
38
|
+
assoc_options[name] = options
|
39
|
+
|
40
|
+
define_method(name) do
|
41
|
+
foreign_key_value = self.send(options.foreign_key)
|
42
|
+
return nil if foreign_key_value.nil?
|
43
|
+
|
44
|
+
options.model_class
|
45
|
+
.where(options.primary_key => foreign_key_value)
|
46
|
+
.first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def has_many(name, options = {})
|
51
|
+
options = HasManyOptions.new(name, self.to_s, options)
|
52
|
+
assoc_options[name] = options
|
53
|
+
|
54
|
+
define_method(name) do
|
55
|
+
target_key_value = self.send(options.primary_key)
|
56
|
+
return nil if target_key_value.nil?
|
57
|
+
options.model_class
|
58
|
+
.where(options.foreign_key => target_key_value)
|
59
|
+
.to_a
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def assoc_options
|
64
|
+
@assoc_options ||= {}
|
65
|
+
end
|
66
|
+
|
67
|
+
def has_one_through(name, through_name, source_name)
|
68
|
+
through_options = assoc_options[through_name]
|
69
|
+
|
70
|
+
define_method(name) do
|
71
|
+
source_options =
|
72
|
+
through_options.model_class.assoc_options[source_name]
|
73
|
+
through_pk = through_options.primary_key
|
74
|
+
key_val = self.send(through_options.foreign_key)
|
75
|
+
|
76
|
+
source_options.model_class.includes(through_options.model_class)
|
77
|
+
.where(through_pk => key_val).first
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def has_many_through(name, through_name, source_name)
|
82
|
+
through_options = assoc_options[through_name]
|
83
|
+
define_method(name) do
|
84
|
+
through_fk = through_options.foreign_key
|
85
|
+
through_class = through_options.model_class
|
86
|
+
key_val = self.send(through_options.primary_key)
|
87
|
+
|
88
|
+
#2 queries, we could reduce to 1 by writing SQLRelation::join.
|
89
|
+
through_class.where(through_fk => key_val)
|
90
|
+
.includes(source_name)
|
91
|
+
.load
|
92
|
+
.included_relations
|
93
|
+
.first
|
94
|
+
.to_a
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require_relative '../../lib/db_connection'
|
2
|
+
require_relative 'associatable'
|
3
|
+
require_relative '../relation'
|
4
|
+
require 'active_support/inflector'
|
5
|
+
|
6
|
+
class SQLObject
|
7
|
+
extend Associatable
|
8
|
+
|
9
|
+
RELATION_METHODS = [
|
10
|
+
:limit, :includes, :where, :order
|
11
|
+
]
|
12
|
+
|
13
|
+
RELATION_METHODS.each do |method|
|
14
|
+
define_singleton_method(method) do |arg|
|
15
|
+
SQLRelation.new(klass: self).send(method, arg)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.all
|
20
|
+
where({})
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.columns
|
24
|
+
DBConnection.columns(table_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.count
|
28
|
+
all.count
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.define_singleton_method_by_proc(obj, name, block)
|
32
|
+
metaclass = class << obj; self; end
|
33
|
+
metaclass.send(:define_method, name, block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.destroy_all!
|
37
|
+
self.all.each do |entry|
|
38
|
+
entry.destroy!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.finalize!
|
43
|
+
self.columns.each do |column|
|
44
|
+
define_method(column) do
|
45
|
+
attributes[column]
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method("#{column}=") do |new_value|
|
49
|
+
attributes[column] = new_value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.find(id)
|
55
|
+
where(id: id).first
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.first
|
59
|
+
all.limit(1).first
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.has_association?(association)
|
63
|
+
assoc_options.keys.include?(association)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.last
|
67
|
+
all.order(id: :desc).limit(1).first
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.parse_all(results)
|
71
|
+
relation = SQLRelation.new(klass: self, loaded: true)
|
72
|
+
results.each do |result|
|
73
|
+
relation << self.new(result)
|
74
|
+
end
|
75
|
+
|
76
|
+
relation
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.table_name=(table_name)
|
80
|
+
@table_name = table_name
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.table_name
|
84
|
+
@table_name ||= self.to_s.downcase.tableize
|
85
|
+
end
|
86
|
+
|
87
|
+
def initialize(params = {})
|
88
|
+
params.each do |attr_name, value|
|
89
|
+
unless self.class.columns.include?(attr_name.to_sym)
|
90
|
+
raise "unknown attribute '#{attr_name}'"
|
91
|
+
end
|
92
|
+
|
93
|
+
self.send("#{attr_name}=", value)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def attributes
|
98
|
+
@attributes ||= {}
|
99
|
+
end
|
100
|
+
|
101
|
+
def attribute_values
|
102
|
+
self.class.columns.map do |column|
|
103
|
+
self.send(column)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def destroy!
|
108
|
+
if self.class.find(id)
|
109
|
+
DBConnection.execute(<<-SQL)
|
110
|
+
DELETE
|
111
|
+
FROM
|
112
|
+
#{self.class.table_name}
|
113
|
+
WHERE
|
114
|
+
id = #{id}
|
115
|
+
SQL
|
116
|
+
return self
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def insert
|
121
|
+
columns = self.class.columns.reject { |col| col == :id }
|
122
|
+
column_values = columns.map {|attr_name| send(attr_name)}
|
123
|
+
column_names = columns.join(", ")
|
124
|
+
bind_params = (1..columns.length).map {|n| "$#{n}"}.join(", ")
|
125
|
+
result = DBConnection.execute(<<-SQL, column_values)
|
126
|
+
INSERT INTO
|
127
|
+
#{self.class.table_name} (#{column_names})
|
128
|
+
VALUES
|
129
|
+
(#{bind_params})
|
130
|
+
RETURNING id;
|
131
|
+
SQL
|
132
|
+
self.id = result.first['id']
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def save
|
137
|
+
self.class.find(id) ? update : insert
|
138
|
+
end
|
139
|
+
|
140
|
+
def update
|
141
|
+
set_line = self.class.columns.map do |column|
|
142
|
+
#bobby tables says hi!
|
143
|
+
"#{column} = \'#{self.send(column)}\'"
|
144
|
+
end.join(", ")
|
145
|
+
|
146
|
+
DBConnection.execute(<<-SQL)
|
147
|
+
UPDATE
|
148
|
+
#{self.class.table_name}
|
149
|
+
SET
|
150
|
+
#{set_line}
|
151
|
+
WHERE
|
152
|
+
id = #{id}
|
153
|
+
SQL
|
154
|
+
self
|
155
|
+
end
|
156
|
+
end
|
data/lib/version.rb
ADDED
data/puffs.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "puffs"
|
9
|
+
spec.version = Puffs::VERSION
|
10
|
+
spec.authors = ["Zachary Moroni"]
|
11
|
+
spec.email = ["zachary.moroni@gmail.com"]
|
12
|
+
|
13
|
+
spec.summary = %q{A simple ORM and MVC inspired by Rails.}
|
14
|
+
spec.description = %q{Coming Soon...}
|
15
|
+
spec.homepage = "http://github.com/snackzone"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
19
|
+
# delete this section to allow pushing this gem to any host.
|
20
|
+
if spec.respond_to?(:metadata)
|
21
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
22
|
+
else
|
23
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
24
|
+
end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
spec.bindir = "bin"
|
28
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
33
|
+
end
|