pup 1.0.0
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/.coveralls.yml +1 -0
- data/.gitignore +10 -0
- data/.hound.yml +5 -0
- data/.rspec +2 -0
- data/.rubocop.yml +242 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +174 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/db/data.sqlite +0 -0
- data/lib/pup.rb +12 -0
- data/lib/pup/application.rb +54 -0
- data/lib/pup/controller/base_controller.rb +57 -0
- data/lib/pup/dependencies/method_override.rb +11 -0
- data/lib/pup/dependencies/request_handler.rb +38 -0
- data/lib/pup/model/columns_builder.rb +41 -0
- data/lib/pup/model/model.rb +101 -0
- data/lib/pup/model/orm_methods.rb +56 -0
- data/lib/pup/routing/route.rb +44 -0
- data/lib/pup/routing/route_helpers.rb +31 -0
- data/lib/pup/routing/router.rb +84 -0
- data/lib/pup/utilities/array.rb +9 -0
- data/lib/pup/utilities/object.rb +6 -0
- data/lib/pup/utilities/string.rb +18 -0
- data/lib/pup/version.rb +3 -0
- data/pup.gemspec +50 -0
- metadata +286 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "pup"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/db/data.sqlite
ADDED
Binary file
|
data/lib/pup.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "pup/version"
|
2
|
+
require "pup/utilities/string"
|
3
|
+
require "pup/utilities/object"
|
4
|
+
require "pup/utilities/array"
|
5
|
+
require "pup/model/model"
|
6
|
+
require "pup/routing/router"
|
7
|
+
require "pup/controller/base_controller"
|
8
|
+
require "pup/application"
|
9
|
+
|
10
|
+
module Pup
|
11
|
+
# Your code goes here...
|
12
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "pup/dependencies/request_handler"
|
3
|
+
require "pup/dependencies/method_override"
|
4
|
+
|
5
|
+
module Pup
|
6
|
+
class Application
|
7
|
+
attr_reader :request, :verb, :path, :router
|
8
|
+
|
9
|
+
alias_method :routes, :router
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@router = Routing::Router.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
env = MethodOverride.apply_to(env)
|
17
|
+
@request = Rack::Request.new(env)
|
18
|
+
|
19
|
+
if router.has_routes?
|
20
|
+
respond_to_request
|
21
|
+
else
|
22
|
+
pup_default_response
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to_request
|
27
|
+
route = router.get_match(request.request_method, request.path_info)
|
28
|
+
if route
|
29
|
+
handler = RequestHandler.new(request, route)
|
30
|
+
handler.response
|
31
|
+
else
|
32
|
+
page_not_found
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def pup_default_response
|
37
|
+
Rack::Response.new(
|
38
|
+
"<center><b>Pup ::: </b>MVC Framework for web masters</center>",
|
39
|
+
200,
|
40
|
+
"Content-Type" => "text/html"
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def page_not_found
|
45
|
+
Rack::Response.new(
|
46
|
+
"<center><h1>404 Error</h1>Page not found</center>",
|
47
|
+
404,
|
48
|
+
"Content-Type" => "text/html"
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
private :respond_to_request, :pup_default_response, :page_not_found
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "active_support/core_ext/hash/indifferent_access"
|
2
|
+
require "erubis"
|
3
|
+
|
4
|
+
module Pup
|
5
|
+
class BaseController
|
6
|
+
attr_reader :request
|
7
|
+
|
8
|
+
def initialize(request)
|
9
|
+
@request ||= request
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_response
|
13
|
+
@response
|
14
|
+
end
|
15
|
+
|
16
|
+
def render(view_name, locals = {})
|
17
|
+
template = get_template(view_name)
|
18
|
+
parameters = build_view_params(locals)
|
19
|
+
response_body = Erubis::Eruby.new(template).result(parameters)
|
20
|
+
make_response(response_body)
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_template(view_name)
|
24
|
+
filename = File.join(
|
25
|
+
APP_ROOT, "app", "views", controller_name, "#{view_name}.erb"
|
26
|
+
)
|
27
|
+
File.read(filename)
|
28
|
+
end
|
29
|
+
|
30
|
+
def build_view_params(locals)
|
31
|
+
hash = {}
|
32
|
+
vars = instance_variables
|
33
|
+
vars.each { |name| hash[name] = instance_variable_get(name) }
|
34
|
+
|
35
|
+
hash.merge(locals)
|
36
|
+
end
|
37
|
+
|
38
|
+
def make_response(body, status = 200, headers = {})
|
39
|
+
@response = Rack::Response.new(body, status, headers)
|
40
|
+
end
|
41
|
+
|
42
|
+
def params
|
43
|
+
request.params.with_indifferent_access
|
44
|
+
end
|
45
|
+
|
46
|
+
def redirect_to(location)
|
47
|
+
make_response([], 302, "Location" => location)
|
48
|
+
end
|
49
|
+
|
50
|
+
def controller_name
|
51
|
+
self.class.to_s.gsub("Controller", "").to_snakecase
|
52
|
+
end
|
53
|
+
|
54
|
+
private :get_template, :build_view_params, :make_response, :params,
|
55
|
+
:redirect_to, :controller_name
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Pup
|
2
|
+
class RequestHandler
|
3
|
+
attr_reader :request, :route, :response
|
4
|
+
|
5
|
+
def initialize(request, route)
|
6
|
+
@request = request
|
7
|
+
@route = route
|
8
|
+
process_request
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_request
|
12
|
+
params = collage_parameters
|
13
|
+
|
14
|
+
request.instance_variable_set "@params", params
|
15
|
+
|
16
|
+
controller_constant = route.controller
|
17
|
+
controller_class = controller_constant.new(request)
|
18
|
+
|
19
|
+
@response = controller_response(controller_class, route.action.to_sym)
|
20
|
+
end
|
21
|
+
|
22
|
+
def collage_parameters
|
23
|
+
route_url_params = route.get_url_parameters(request.path_info)
|
24
|
+
request.params.merge(route_url_params)
|
25
|
+
end
|
26
|
+
|
27
|
+
def controller_response(controller, action)
|
28
|
+
controller.send(action)
|
29
|
+
|
30
|
+
unless controller.get_response
|
31
|
+
controller.render(action)
|
32
|
+
end
|
33
|
+
controller.get_response
|
34
|
+
end
|
35
|
+
|
36
|
+
private :process_request, :collage_parameters, :controller_response
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Pup
|
2
|
+
module ColumnsBuilder
|
3
|
+
private
|
4
|
+
|
5
|
+
def fields_builder(fields)
|
6
|
+
columns_definition = ""
|
7
|
+
fields.each do |field, constraints|
|
8
|
+
columns_definition += field.to_s
|
9
|
+
|
10
|
+
constraints.each do |constraint_type, value|
|
11
|
+
columns_definition += " "
|
12
|
+
columns_definition += send(constraint_type, value)
|
13
|
+
end
|
14
|
+
columns_definition += ","
|
15
|
+
end
|
16
|
+
columns_definition[0..-2]
|
17
|
+
end
|
18
|
+
|
19
|
+
def type(value)
|
20
|
+
value.to_s.upcase
|
21
|
+
end
|
22
|
+
|
23
|
+
def primary_key(value)
|
24
|
+
return "PRIMARY KEY" if value
|
25
|
+
""
|
26
|
+
end
|
27
|
+
|
28
|
+
def nullable(value)
|
29
|
+
return "NOT NULL" unless value
|
30
|
+
"NULL"
|
31
|
+
end
|
32
|
+
|
33
|
+
def default(value)
|
34
|
+
"DEFAULT `#{value}`"
|
35
|
+
end
|
36
|
+
|
37
|
+
def autoincrement(value)
|
38
|
+
"AUTOINCREMENT" if value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
require "pup/model/columns_builder"
|
3
|
+
require "pup/model/orm_methods"
|
4
|
+
|
5
|
+
module Pup
|
6
|
+
class Model
|
7
|
+
extend Pup::ColumnsBuilder
|
8
|
+
extend Pup::OrmMethods
|
9
|
+
|
10
|
+
DB ||= SQLite3::Database.new(File.join("db", "data.sqlite"))
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
end
|
14
|
+
|
15
|
+
def save
|
16
|
+
if id
|
17
|
+
query = "UPDATE #{self.class.table_name} "\
|
18
|
+
"SET #{update_field_set} "\
|
19
|
+
"WHERE id = ?"
|
20
|
+
DB.execute(query, table_values, id)
|
21
|
+
else
|
22
|
+
query = "INSERT INTO #{self.class.table_name} "\
|
23
|
+
"(#{table_fields}) "\
|
24
|
+
"VALUES(#{values_placeholders})"
|
25
|
+
DB.execute(query, table_values)
|
26
|
+
|
27
|
+
self.id = DB.execute("SELECT last_insert_rowid()")
|
28
|
+
end
|
29
|
+
self.class.find(id)
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(parameters)
|
33
|
+
parameters.each do |key, value|
|
34
|
+
send("#{key}=", value)
|
35
|
+
end
|
36
|
+
|
37
|
+
save
|
38
|
+
end
|
39
|
+
|
40
|
+
def destroy
|
41
|
+
DB.execute("DELETE FROM #{self.class.table_name} WHERE id= ?", id)
|
42
|
+
end
|
43
|
+
|
44
|
+
def table_fields
|
45
|
+
columns_except_id.join(", ")
|
46
|
+
end
|
47
|
+
|
48
|
+
def columns_except_id
|
49
|
+
self.class.columns_array.without_id.with_value(self)
|
50
|
+
end
|
51
|
+
|
52
|
+
def values_placeholders
|
53
|
+
count = columns_except_id.count
|
54
|
+
(["?"] * count).join(", ")
|
55
|
+
end
|
56
|
+
|
57
|
+
def table_values
|
58
|
+
values = []
|
59
|
+
columns_except_id.each do |field|
|
60
|
+
values << send(field)
|
61
|
+
end
|
62
|
+
values
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_field_set
|
66
|
+
values = []
|
67
|
+
columns_except_id.each do |field|
|
68
|
+
values << "#{field} = ? "
|
69
|
+
end
|
70
|
+
values.join(", ")
|
71
|
+
end
|
72
|
+
|
73
|
+
class << self
|
74
|
+
attr_reader :table_name, :fields
|
75
|
+
|
76
|
+
def to_table(table_name)
|
77
|
+
@table_name = table_name.to_s
|
78
|
+
@fields = {}
|
79
|
+
property :id, type: :integer, primary_key: true
|
80
|
+
end
|
81
|
+
|
82
|
+
def property(field_name, options = {})
|
83
|
+
@fields[field_name] = options
|
84
|
+
attr_accessor field_name
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_table
|
88
|
+
query = "CREATE TABLE IF NOT EXISTS #{@table_name}"\
|
89
|
+
"(#{fields_builder(@fields)})"
|
90
|
+
DB.execute(query)
|
91
|
+
end
|
92
|
+
|
93
|
+
def columns_array
|
94
|
+
@columns_array ||= fields.keys
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private :table_fields, :columns_except_id, :values_placeholders,
|
99
|
+
:table_values, :update_field_set
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Pup
|
2
|
+
module OrmMethods
|
3
|
+
# columns_array and table_name are defined in the extending
|
4
|
+
# class-model/base
|
5
|
+
|
6
|
+
DB ||= SQLite3::Database.new(File.join("db", "data.sqlite"))
|
7
|
+
|
8
|
+
def create(parameters)
|
9
|
+
model = new
|
10
|
+
parameters.each do |key, value|
|
11
|
+
model.send("#{key}=", value)
|
12
|
+
end
|
13
|
+
|
14
|
+
model.save
|
15
|
+
end
|
16
|
+
|
17
|
+
def all
|
18
|
+
fields = columns_array.join(", ")
|
19
|
+
data = DB.execute("SELECT id, #{fields} FROM #{table_name}")
|
20
|
+
data.map! do |row|
|
21
|
+
row_to_model(row)
|
22
|
+
end
|
23
|
+
|
24
|
+
data
|
25
|
+
end
|
26
|
+
|
27
|
+
def row_to_model(row)
|
28
|
+
model = new
|
29
|
+
|
30
|
+
columns_array.each_with_index do |field, index|
|
31
|
+
model.send("#{field}=", row[index + 1]) if row
|
32
|
+
end
|
33
|
+
|
34
|
+
model
|
35
|
+
end
|
36
|
+
|
37
|
+
def find(id)
|
38
|
+
fields = columns_array.join(", ")
|
39
|
+
row = DB.execute(
|
40
|
+
"SELECT id, #{fields} FROM #{table_name} WHERE id = ?",
|
41
|
+
id
|
42
|
+
).first
|
43
|
+
row_to_model(row) if row
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy(id)
|
47
|
+
DB.execute("DELETE FROM #{table_name} WHERE id= ?", id)
|
48
|
+
end
|
49
|
+
|
50
|
+
def destroy_all
|
51
|
+
DB.execute("DELETE FROM #{table_name}")
|
52
|
+
end
|
53
|
+
|
54
|
+
private :row_to_model
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Pup
|
2
|
+
module Routing
|
3
|
+
class Route
|
4
|
+
attr_reader :controller_name, :action, :path_regex, :url_placeholders
|
5
|
+
def initialize(path_regex, to, url_placeholders = {})
|
6
|
+
@controller_name, @action = get_controller_and_action(to)
|
7
|
+
@path_regex = path_regex
|
8
|
+
@url_placeholders = url_placeholders
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_controller_and_action(to)
|
12
|
+
controller, action = to.split("#")
|
13
|
+
controller_name = controller + "_controller"
|
14
|
+
[controller_name, action]
|
15
|
+
end
|
16
|
+
|
17
|
+
def controller
|
18
|
+
Object.const_get(controller_name.to_camelcase)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_url_parameters(actual_path)
|
22
|
+
parameters = {}
|
23
|
+
path = actual_path.split("/")
|
24
|
+
url_placeholders.each do |index, key|
|
25
|
+
parameters[key] = path[index.to_i]
|
26
|
+
end
|
27
|
+
parameters
|
28
|
+
end
|
29
|
+
|
30
|
+
def check_path(path)
|
31
|
+
(path_regex =~ path) == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
controller_name == other.controller_name &&
|
36
|
+
action == other.action &&
|
37
|
+
path_regex == other.path_regex &&
|
38
|
+
url_placeholders == other.url_placeholders
|
39
|
+
end
|
40
|
+
|
41
|
+
private :get_controller_and_action
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|