puffs 0.2.04 → 0.2.05
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/puffs +39 -35
- data/lib/controller_base.rb +55 -55
- data/lib/db_connection.rb +94 -89
- data/lib/puffs.rb +5 -5
- data/lib/relation.rb +161 -159
- data/lib/router.rb +61 -57
- data/lib/server_connection.rb +15 -12
- data/lib/session.rb +28 -20
- data/lib/sql_object/associatable.rb +76 -74
- data/lib/sql_object/sql_object.rb +115 -114
- data/puffs.gemspec +21 -20
- data/readme.md +7 -0
- data/template/config/routes.rb +1 -1
- data/template/db/seeds.rb +23 -21
- metadata +2 -2
@@ -1,97 +1,99 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
module Puffs
|
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
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
def table_name
|
16
|
+
model_class.table_name
|
17
|
+
end
|
16
18
|
end
|
17
|
-
end
|
18
19
|
|
19
|
-
class BelongsToOptions < AssocOptions
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
class BelongsToOptions < AssocOptions
|
21
|
+
def initialize(name, options = {})
|
22
|
+
@primary_key = options[:primary_key] || :id
|
23
|
+
@foreign_key = options[:foreign_key] || "#{name}_id".to_sym
|
24
|
+
@class_name = options[:class_name] || name.to_s.capitalize
|
25
|
+
end
|
24
26
|
end
|
25
|
-
end
|
26
27
|
|
27
|
-
class HasManyOptions < AssocOptions
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
class HasManyOptions < AssocOptions
|
29
|
+
def initialize(name, self_class_name, options = {})
|
30
|
+
@primary_key = options[:primary_key] || :id
|
31
|
+
@foreign_key = options[:foreign_key] || "#{self_class_name.to_s.underscore}_id".to_sym
|
32
|
+
@class_name = options[:class_name] || name.to_s.singularize.camelcase
|
33
|
+
end
|
32
34
|
end
|
33
|
-
end
|
34
35
|
|
35
|
-
module Associatable
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
module Associatable
|
37
|
+
def belongs_to(name, options = {})
|
38
|
+
options = BelongsToOptions.new(name, options)
|
39
|
+
assoc_options[name] = options
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
define_method(name) do
|
42
|
+
foreign_key_value = self.send(options.foreign_key)
|
43
|
+
return nil if foreign_key_value.nil?
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
options.model_class
|
46
|
+
.where(options.primary_key => foreign_key_value)
|
47
|
+
.first
|
48
|
+
end
|
47
49
|
end
|
48
|
-
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
51
|
+
def has_many(name, options = {})
|
52
|
+
options = HasManyOptions.new(name, self.to_s, options)
|
53
|
+
assoc_options[name] = options
|
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
|
60
62
|
end
|
61
|
-
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
def assoc_options
|
65
|
+
@assoc_options ||= {}
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
68
|
+
def has_one_through(name, through_name, source_name)
|
69
|
+
through_options = assoc_options[through_name]
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
71
|
+
define_method(name) do
|
72
|
+
source_options =
|
73
|
+
through_options.model_class.assoc_options[source_name]
|
74
|
+
through_pk = through_options.primary_key
|
75
|
+
key_val = self.send(through_options.foreign_key)
|
75
76
|
|
76
|
-
|
77
|
-
|
77
|
+
source_options.model_class.includes(through_options.model_class)
|
78
|
+
.where(through_pk => key_val).first
|
79
|
+
end
|
78
80
|
end
|
79
|
-
end
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
82
|
+
def has_many_through(name, through_name, source_name)
|
83
|
+
through_options = assoc_options[through_name]
|
84
|
+
define_method(name) do
|
85
|
+
through_fk = through_options.foreign_key
|
86
|
+
through_class = through_options.model_class
|
87
|
+
key_val = self.send(through_options.primary_key)
|
88
|
+
|
89
|
+
#2 queries, we could reduce to 1 by writing Puffs::SQLRelation.join.
|
90
|
+
through_class.where(through_fk => key_val)
|
91
|
+
.includes(source_name)
|
92
|
+
.load
|
93
|
+
.included_relations
|
94
|
+
.first
|
95
|
+
.to_a
|
96
|
+
end
|
95
97
|
end
|
96
98
|
end
|
97
99
|
end
|
@@ -4,154 +4,155 @@ require_relative '../relation'
|
|
4
4
|
require 'active_support/inflector'
|
5
5
|
# require_relative '../puffs'
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
module Puffs
|
8
|
+
class SQLObject
|
9
|
+
extend Associatable
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
RELATION_METHODS = [
|
12
|
+
:limit, :includes, :where, :order
|
13
|
+
]
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
RELATION_METHODS.each do |method|
|
16
|
+
define_singleton_method(method) do |arg|
|
17
|
+
Puffs::SQLRelation.new(klass: self).send(method, arg)
|
18
|
+
end
|
17
19
|
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.all
|
21
|
-
where({})
|
22
|
-
end
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
def self.all
|
22
|
+
where({})
|
23
|
+
end
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
def self.columns
|
26
|
+
Puffs::DBConnection.columns(table_name)
|
27
|
+
end
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
29
|
+
def self.count
|
30
|
+
all.count
|
31
|
+
end
|
36
32
|
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
def self.define_singleton_method_by_proc(obj, name, block)
|
34
|
+
metaclass = class << obj; self; end
|
35
|
+
metaclass.send(:define_method, name, block)
|
40
36
|
end
|
41
|
-
end
|
42
37
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
attributes[column]
|
38
|
+
def self.destroy_all!
|
39
|
+
self.all.each do |entry|
|
40
|
+
entry.destroy!
|
47
41
|
end
|
42
|
+
end
|
48
43
|
|
49
|
-
|
50
|
-
|
44
|
+
def self.finalize!
|
45
|
+
self.columns.each do |column|
|
46
|
+
define_method(column) do
|
47
|
+
attributes[column]
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method("#{column}=") do |new_value|
|
51
|
+
attributes[column] = new_value
|
52
|
+
end
|
51
53
|
end
|
52
54
|
end
|
53
|
-
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
def self.find(id)
|
57
|
+
where(id: id).first
|
58
|
+
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
def self.first
|
61
|
+
all.limit(1).first
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
def self.has_association?(association)
|
65
|
+
assoc_options.keys.include?(association)
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
def self.last
|
69
|
+
all.order(id: :desc).limit(1).first
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.parse_all(results)
|
73
|
+
relation = Puffs::SQLRelation.new(klass: self, loaded: true)
|
74
|
+
results.each do |result|
|
75
|
+
relation << self.new(result)
|
76
|
+
end
|
70
77
|
|
71
|
-
|
72
|
-
relation = Puffs::SQLRelation.new(klass: self, loaded: true)
|
73
|
-
results.each do |result|
|
74
|
-
relation << self.new(result)
|
78
|
+
relation
|
75
79
|
end
|
76
80
|
|
77
|
-
|
78
|
-
|
81
|
+
def self.table_name=(table_name)
|
82
|
+
@table_name = table_name
|
83
|
+
end
|
79
84
|
|
80
|
-
|
81
|
-
|
82
|
-
|
85
|
+
def self.table_name
|
86
|
+
@table_name ||= self.to_s.downcase.tableize
|
87
|
+
end
|
83
88
|
|
84
|
-
|
85
|
-
|
86
|
-
|
89
|
+
def initialize(params = {})
|
90
|
+
params.each do |attr_name, value|
|
91
|
+
unless self.class.columns.include?(attr_name.to_sym)
|
92
|
+
raise "unknown attribute '#{attr_name}'"
|
93
|
+
end
|
87
94
|
|
88
|
-
|
89
|
-
params.each do |attr_name, value|
|
90
|
-
unless self.class.columns.include?(attr_name.to_sym)
|
91
|
-
raise "unknown attribute '#{attr_name}'"
|
95
|
+
self.send("#{attr_name}=", value)
|
92
96
|
end
|
97
|
+
end
|
93
98
|
|
94
|
-
|
99
|
+
def attributes
|
100
|
+
@attributes ||= {}
|
95
101
|
end
|
96
|
-
end
|
97
102
|
|
98
|
-
|
99
|
-
|
100
|
-
|
103
|
+
def attribute_values
|
104
|
+
self.class.columns.map do |column|
|
105
|
+
self.send(column)
|
106
|
+
end
|
107
|
+
end
|
101
108
|
|
102
|
-
|
103
|
-
|
104
|
-
|
109
|
+
def destroy!
|
110
|
+
if self.class.find(id)
|
111
|
+
Puffs::DBConnection.execute(<<-SQL)
|
112
|
+
DELETE
|
113
|
+
FROM
|
114
|
+
#{self.class.table_name}
|
115
|
+
WHERE
|
116
|
+
id = #{id}
|
117
|
+
SQL
|
118
|
+
return self
|
119
|
+
end
|
105
120
|
end
|
106
|
-
end
|
107
121
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
122
|
+
def insert
|
123
|
+
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(", ")
|
127
|
+
result = Puffs::DBConnection.execute(<<-SQL, column_values)
|
128
|
+
INSERT INTO
|
129
|
+
#{self.class.table_name} (#{column_names})
|
130
|
+
VALUES
|
131
|
+
(#{bind_params})
|
132
|
+
RETURNING id;
|
116
133
|
SQL
|
117
|
-
|
134
|
+
self.id = result.first['id']
|
135
|
+
self
|
118
136
|
end
|
119
|
-
end
|
120
137
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
column_names = columns.join(", ")
|
125
|
-
bind_params = (1..columns.length).map {|n| "$#{n}"}.join(", ")
|
126
|
-
result = DBConnection.execute(<<-SQL, column_values)
|
127
|
-
INSERT INTO
|
128
|
-
#{self.class.table_name} (#{column_names})
|
129
|
-
VALUES
|
130
|
-
(#{bind_params})
|
131
|
-
RETURNING id;
|
132
|
-
SQL
|
133
|
-
self.id = result.first['id']
|
134
|
-
self
|
135
|
-
end
|
138
|
+
def save
|
139
|
+
self.class.find(id) ? update : insert
|
140
|
+
end
|
136
141
|
|
137
|
-
|
138
|
-
|
139
|
-
|
142
|
+
def update
|
143
|
+
set_line = self.class.columns.map do |column|
|
144
|
+
"#{column} = \'#{self.send(column)}\'"
|
145
|
+
end.join(", ")
|
140
146
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
#{set_line}
|
152
|
-
WHERE
|
153
|
-
id = #{id}
|
154
|
-
SQL
|
155
|
-
self
|
147
|
+
Puffs::DBConnection.execute(<<-SQL)
|
148
|
+
UPDATE
|
149
|
+
#{self.class.table_name}
|
150
|
+
SET
|
151
|
+
#{set_line}
|
152
|
+
WHERE
|
153
|
+
id = #{id}
|
154
|
+
SQL
|
155
|
+
self
|
156
|
+
end
|
156
157
|
end
|
157
158
|
end
|
data/puffs.gemspec
CHANGED
@@ -4,35 +4,36 @@ lib = File.expand_path('../lib', __FILE__)
|
|
4
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
-
spec.version =
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
7
|
+
spec.name = 'puffs'
|
8
|
+
spec.version = '0.2.05'
|
9
|
+
spec.authors = ['Zachary Moroni']
|
10
|
+
spec.email = ['zachary.moroni@gmail.com']
|
11
11
|
|
12
|
-
spec.summary = %q
|
13
|
-
spec.description =
|
14
|
-
|
15
|
-
spec.
|
12
|
+
spec.summary = %q(A simple ORM and MVC inspired by Rails.)
|
13
|
+
spec.description =
|
14
|
+
%q(Make simple apps the Rails way, but with less overhead.)
|
15
|
+
spec.homepage = 'http://github.com/snackzone/puffs'
|
16
|
+
spec.license = 'MIT'
|
16
17
|
|
17
18
|
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
19
|
# delete this section to allow pushing this gem to any host.
|
19
20
|
if spec.respond_to?(:metadata)
|
20
|
-
spec.metadata['allowed_push_host'] =
|
21
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
21
22
|
else
|
22
|
-
raise
|
23
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
23
24
|
end
|
24
25
|
|
25
26
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
-
spec.executables =
|
27
|
+
spec.executables = 'puffs'
|
27
28
|
|
28
|
-
spec.add_runtime_dependency
|
29
|
-
spec.add_runtime_dependency
|
30
|
-
spec.add_runtime_dependency
|
31
|
-
spec.add_runtime_dependency
|
32
|
-
spec.add_runtime_dependency
|
33
|
-
spec.add_runtime_dependency
|
29
|
+
spec.add_runtime_dependency 'thor', '~> 0.19'
|
30
|
+
spec.add_runtime_dependency 'pg', '~> 0.18'
|
31
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.2', '>= 4.2.5.1'
|
32
|
+
spec.add_runtime_dependency 'pry', '~> 0.10.3'
|
33
|
+
spec.add_runtime_dependency 'rack', '~> 1.6', '>= 1.6.4'
|
34
|
+
spec.add_runtime_dependency 'fileutils', '~> 0.7'
|
34
35
|
|
35
|
-
spec.add_development_dependency
|
36
|
-
spec.add_development_dependency
|
37
|
-
spec.add_development_dependency
|
36
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
37
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
38
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
38
39
|
end
|
data/readme.md
CHANGED
@@ -134,3 +134,10 @@ Puffs Console
|
|
134
134
|
|
135
135
|
Access your DB with `Puffs::SQLObject` methods by simply entering
|
136
136
|
`require 'puffs'` in Pry (or IRB).
|
137
|
+
|
138
|
+
Todos
|
139
|
+
-----
|
140
|
+
|
141
|
+
* [ ] Flash / Flash.now
|
142
|
+
* [ ] CSRF Form Authenticity Token
|
143
|
+
* [ ] Production Database (push to Heroku)
|
data/template/config/routes.rb
CHANGED
data/template/db/seeds.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
module Puffs
|
2
|
+
class Seed
|
3
|
+
def self.populate
|
4
|
+
# Create seeds in this method. Here's an example:
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
6
|
+
# Cat.destroy_all!
|
7
|
+
# Human.destroy_all!
|
8
|
+
# House.destroy_all!
|
9
|
+
#
|
10
|
+
# h1 = House.new(address: '26th and Guerrero').save
|
11
|
+
# h2 = House.new(address: 'Dolores and Market').save
|
12
|
+
# h3 = House.new(address: '123 4th Street').save
|
13
|
+
#
|
14
|
+
# devon = Human.new(fname: 'Devon', lname: 'Watts', house_id: h1.id).save
|
15
|
+
# matt = Human.new(fname: 'Matt', lname: 'Rubens', house_id: h1.id).save
|
16
|
+
# ned = Human.new(fname: 'Ned', lname: 'Ruggeri', house_id: h2.id).save
|
17
|
+
# catless = Human.new(fname: 'Catless', lname: 'Human', house_id: h3.id).save
|
18
|
+
#
|
19
|
+
# Cat.new(name: 'Breakfast', owner_id: devon.id).save
|
20
|
+
# Cat.new(name:'Earl', owner_id: matt.id).save
|
21
|
+
# Cat.new(name: 'Haskell', owner_id: ned.id).save
|
22
|
+
# Cat.new(name: 'Markov', owner_id: ned.id).save
|
23
|
+
# Cat.new(name: 'Stray Cat')
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
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.05
|
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-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|