puffs 0.2.04 → 0.2.05
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/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
|