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.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
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
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
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,11 @@
1
+ require "rack"
2
+
3
+ class MethodOverride
4
+ def self.apply_to(env)
5
+ request = Rack::Request.new(env)
6
+ env["REQUEST_METHOD"] = request.params["_method"].
7
+ upcase if request.params["_method"]
8
+
9
+ env
10
+ end
11
+ 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