matrack 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.codeclimate.yml +2 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.rubocop.yml +237 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +73 -0
- data/Rakefile +6 -0
- data/bin/matrack +7 -0
- data/bin/setup +7 -0
- data/circle.yml +6 -0
- data/generators/generate/Gemfile +3 -0
- data/generators/generate/README.md +20 -0
- data/generators/generate/application.html.erb +18 -0
- data/generators/generate/application.rb +19 -0
- data/generators/generate/application_controller.rb +3 -0
- data/generators/generate/config.ru +12 -0
- data/generators/generate/invalid.html.erb +24 -0
- data/generators/generate/routes.rb +11 -0
- data/generators/generator.rb +78 -0
- data/generators/generator_base.rb +14 -0
- data/generators/mvc_gen.rb +57 -0
- data/index.html +604 -0
- data/lib/matrack.rb +32 -0
- data/lib/matrack/base_controller.rb +63 -0
- data/lib/matrack/dependencies.rb +6 -0
- data/lib/matrack/helper_tags.rb +59 -0
- data/lib/matrack/route.rb +15 -0
- data/lib/matrack/router.rb +42 -0
- data/lib/matrack/session.rb +21 -0
- data/lib/matrack/utility.rb +33 -0
- data/lib/matrack/version.rb +3 -0
- data/lib/orm/base_model.rb +57 -0
- data/lib/orm/data_manager.rb +57 -0
- data/lib/orm/data_utility.rb +33 -0
- data/lib/orm/fetch_queries.rb +53 -0
- data/lib/orm/queries.rb +47 -0
- data/lib/resources.rb +16 -0
- data/matrack.gemspec +43 -0
- metadata +268 -0
data/lib/matrack.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative "resources"
|
2
|
+
|
3
|
+
module Matrack
|
4
|
+
class Application
|
5
|
+
attr_reader :router
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@router = Router.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
route = router.route_for(env)
|
13
|
+
if route
|
14
|
+
route.execute(env)
|
15
|
+
response_handler(route)
|
16
|
+
else
|
17
|
+
controller = BaseController.new(env)
|
18
|
+
body = controller.invalid_route
|
19
|
+
[404, {}, [body]]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def response_handler(route)
|
24
|
+
controller = route.mat_controller
|
25
|
+
if controller.response
|
26
|
+
controller.response
|
27
|
+
else
|
28
|
+
controller.render(route.action)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Matrack
|
2
|
+
class BaseController
|
3
|
+
include Matrack::HelperTags
|
4
|
+
attr_reader :request, :response
|
5
|
+
attr_accessor :session
|
6
|
+
|
7
|
+
def initialize(env)
|
8
|
+
@request = Rack::Request.new(env)
|
9
|
+
@session = Session.new(env)
|
10
|
+
end
|
11
|
+
|
12
|
+
def params
|
13
|
+
request.params
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_response(body, status = 200, headers = {})
|
17
|
+
@response = Rack::Response.new body, status, headers
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(*args)
|
21
|
+
create_response(render_template(*args))
|
22
|
+
end
|
23
|
+
|
24
|
+
def render_template(view_name, locals = {})
|
25
|
+
template = Tilt::ERBTemplate.new(File.join(APP_PATH, "app", "views",
|
26
|
+
"layout", "application.html.erb"))
|
27
|
+
title = view_name.to_s.tr("_", " ").capitalize
|
28
|
+
view = "#{view_name}.html.erb"
|
29
|
+
view_template = Tilt::ERBTemplate.new(File.join(APP_PATH, "app", "views",
|
30
|
+
controller_name, view))
|
31
|
+
template.render(self, title: title) do
|
32
|
+
view_template.render(self, locals.merge!(get_instance_vars))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def invalid_route
|
37
|
+
template = Tilt::ERBTemplate.new(File.join(APP_PATH, "app", "views",
|
38
|
+
"layout", "invalid.html.erb"))
|
39
|
+
template.render(self)
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_instance_vars
|
43
|
+
vars = {}
|
44
|
+
instance_variables.each do |var|
|
45
|
+
key = var.to_s.delete("@").to_sym
|
46
|
+
vars[key] = instance_variable_get(var)
|
47
|
+
end
|
48
|
+
vars
|
49
|
+
end
|
50
|
+
|
51
|
+
def controller_name
|
52
|
+
self.class.to_s.gsub(/Controller$/, "").to_snake_case
|
53
|
+
end
|
54
|
+
|
55
|
+
def authenticate(password)
|
56
|
+
Matrack::DataManager.password_hash(password)
|
57
|
+
end
|
58
|
+
|
59
|
+
def redirect_to(address)
|
60
|
+
create_response([], 302, "location" => address)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Matrack
|
2
|
+
module HelperTags
|
3
|
+
|
4
|
+
def email_validator
|
5
|
+
'[A-Za-z0-9_\-\.]+\@[A-Za-z0-9_\-\.]+\.[A-Za-z]{2,4}'
|
6
|
+
end
|
7
|
+
|
8
|
+
def textbox_tag(name, value = "", class_name = "", holder = "", req = nil,
|
9
|
+
disabled = nil)
|
10
|
+
tag = "<input type='text' name='#{name}' value='#{value}' "
|
11
|
+
tag += "placeholder='#{holder}' class='#{class_name}'"
|
12
|
+
tag += "required='#{req}'" unless req.nil?
|
13
|
+
tag += disabled.nil? ? ">" : " disabled='#{disabled}'>"
|
14
|
+
end
|
15
|
+
|
16
|
+
def password_tag(name, class_name = "", holder = "", req = nil,
|
17
|
+
disabled = nil)
|
18
|
+
tag = "<input type='password' name='#{name}' "
|
19
|
+
tag += "placeholder='#{holder}' class='#{class_name}'"
|
20
|
+
tag += "required='#{req}'" unless req.nil?
|
21
|
+
tag += disabled.nil? ? ">" : " disabled='#{disabled}'>"
|
22
|
+
end
|
23
|
+
|
24
|
+
def emailbox_tag(name, value = "", class_name = "", holder = "", req = nil,
|
25
|
+
disabled = nil, pattern = nil)
|
26
|
+
tag = "<input type='email' name='#{name}' value='#{value}' "
|
27
|
+
tag += "placeholder='#{holder}' class='#{class_name}'"
|
28
|
+
tag += "required='#{req}'" unless req.nil?
|
29
|
+
regex_pattern = pattern.nil? ? "#{email_validator}" : "#{pattern}"
|
30
|
+
tag += " pattern='#{regex_pattern}'"
|
31
|
+
tag += disabled.nil? ? ">" : " disabled='#{disabled}'>"
|
32
|
+
end
|
33
|
+
|
34
|
+
def submit_tag(name, value = "Submit", class_name = "")
|
35
|
+
tag = "<input type='submit' name='#{name}' value='#{value}' "
|
36
|
+
tag += "class='#{class_name}'>"
|
37
|
+
end
|
38
|
+
|
39
|
+
def link_tag(ref, class_name = "", title = "Link")
|
40
|
+
"<a href='#{ref}' class='#{class_name}'>#{title}</a>"
|
41
|
+
end
|
42
|
+
|
43
|
+
def image_tag(name, alt = "image")
|
44
|
+
"<img src='../../images/#{name}' alt= '#{alt}'>"
|
45
|
+
end
|
46
|
+
|
47
|
+
def include_stylesheet(name)
|
48
|
+
"<link href='../../css/#{name}.css' rel='stylesheet' type= 'text/css'>"
|
49
|
+
end
|
50
|
+
|
51
|
+
def include_javascript(name)
|
52
|
+
"<script src='../../js/#{name}.js' type='text/javascript'></script>"
|
53
|
+
end
|
54
|
+
|
55
|
+
def display_favicon
|
56
|
+
"<link rel='icon' type='image/png' href='../../images/favicon.png' />"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Route
|
2
|
+
attr_reader :matclass, :action, :mat_controller
|
3
|
+
|
4
|
+
def initialize(route_info)
|
5
|
+
matklass = route_info.last[:matclass].to_camel_case
|
6
|
+
@matclass = Object.const_get(matklass+"Controller")
|
7
|
+
@path = route_info.first
|
8
|
+
@action = route_info.last[:method].to_sym
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute(env)
|
12
|
+
@mat_controller = matclass.new(env)
|
13
|
+
@mat_controller.send(action)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Router
|
2
|
+
attr_reader :routes
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@routes = Hash.new { |hash, key| hash[key] = [] }
|
6
|
+
end
|
7
|
+
|
8
|
+
def draw(&block)
|
9
|
+
instance_eval(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def root(root_path)
|
13
|
+
get "/", root_path
|
14
|
+
end
|
15
|
+
|
16
|
+
%w(get post put delete).each do |request_type|
|
17
|
+
define_method(request_type) do | path, options|
|
18
|
+
request_type = request_type.to_sym
|
19
|
+
if options.is_a? Hash
|
20
|
+
@routes[request_type] << [path, case_parser(options[:to])]
|
21
|
+
else
|
22
|
+
@routes[request_type] << [path, case_parser(options)]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def route_for(env)
|
28
|
+
path = env["PATH_INFO"]
|
29
|
+
verb = env["REQUEST_METHOD"].downcase.to_sym
|
30
|
+
route_info = routes[verb].detect do |route|
|
31
|
+
route.first == path || route.first == path.sub("/", "")
|
32
|
+
end
|
33
|
+
Route.new(route_info) if route_info
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def case_parser(str)
|
39
|
+
matclass, method = str.split("#")
|
40
|
+
{ matclass: matclass.to_camel_case, method: method.to_snake_case }
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Matrack
|
2
|
+
class Session
|
3
|
+
attr_accessor :request
|
4
|
+
|
5
|
+
def initialize(env)
|
6
|
+
@request = Rack::Request.new(env)
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](key)
|
10
|
+
request.session[key.to_sym]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(key, value)
|
14
|
+
request.session[key.to_sym] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def clear
|
18
|
+
request.session.clear
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class ::String
|
2
|
+
def to_snake_case
|
3
|
+
gsub("::", "/").
|
4
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
5
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').tr("-", "_").downcase
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_camel_case
|
9
|
+
return self if self !~ /_/ && self =~ /[A-Z]+.*/
|
10
|
+
split("_").map(&:capitalize).join
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_b
|
14
|
+
return true if !self.nil? && (self == true || downcase == "true")
|
15
|
+
return false if self.nil? || downcase == "false" || self == false
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def titleize
|
20
|
+
split.map!(&:capitalize).join(" ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def pluralize
|
24
|
+
self + "s" unless self.end_with? "s"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class ::Array
|
30
|
+
def hash_getter
|
31
|
+
first.select{|k| k.is_a? String}
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Matrack
|
2
|
+
class BaseModel < Queries
|
3
|
+
def initialize(hash = {})
|
4
|
+
hash.each_pair { |k,v| send("#{k}=", v) }
|
5
|
+
self
|
6
|
+
end
|
7
|
+
|
8
|
+
@@query_string = ""
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def create_table
|
13
|
+
query_string = @@query_string.sub(/,[\s]$/,"")
|
14
|
+
create_table_fields(table_name,query_string)
|
15
|
+
@@query_string = ""
|
16
|
+
end
|
17
|
+
|
18
|
+
def property(name, type = "str", desc = {} )
|
19
|
+
field_hash = { name => type }
|
20
|
+
if verify_col_type(field_hash) == true
|
21
|
+
db_str = DataUtility.type_mapper(field_hash)
|
22
|
+
@@query_string += query_builder(db_str.keys, db_str.values, desc)
|
23
|
+
@@query_string += ", " unless @@query_string == ""
|
24
|
+
get_and_set_property(name)
|
25
|
+
else
|
26
|
+
puts db_error(verify_col_type(field_hash))
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_and_set_property(name)
|
32
|
+
define_method(name) do
|
33
|
+
instance_variable_get("@#{name}")
|
34
|
+
end
|
35
|
+
define_method("#{name}=") do |value|
|
36
|
+
instance_variable_set("@#{name}", "#{value}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def create(field_hash)
|
41
|
+
passwordify(field_hash)
|
42
|
+
attributes = "#{field_hash.keys}".gsub(/:/, "").gsub(/\[|\]/,"")
|
43
|
+
values = "#{field_hash.values}".gsub(/\[|\]/,"").gsub(/\"/,"'")
|
44
|
+
db_conn.execute "INSERT INTO #{table_name} (#{attributes}) VALUES (#{
|
45
|
+
values});"
|
46
|
+
end
|
47
|
+
|
48
|
+
def passwordify(field_hash)
|
49
|
+
if field_hash.keys.include? :password
|
50
|
+
hashed_pass = password_hash(field_hash[:password])
|
51
|
+
field_hash[:password] = hashed_pass
|
52
|
+
end
|
53
|
+
field_hash
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
require "digest/sha1"
|
3
|
+
|
4
|
+
module Matrack
|
5
|
+
class DataManager
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def db_conn
|
9
|
+
db = SQLite3::Database.new "#{APP_PATH}/db/app.sqlite3"
|
10
|
+
db.results_as_hash = true
|
11
|
+
db
|
12
|
+
end
|
13
|
+
|
14
|
+
def db_error(message)
|
15
|
+
DataUtility.db_error(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def query_builder(name, type, desc)
|
19
|
+
name = name.join
|
20
|
+
type = type.join
|
21
|
+
primary_key = "PRIMARY KEY" if desc[:primary_key] == true
|
22
|
+
primary_key += " AUTOINCREMENT" if desc[:primary_key] == true &&
|
23
|
+
type == "INTEGER"
|
24
|
+
primary_key = primary_key ? primary_key : ""
|
25
|
+
null_value = desc[:nullable].nil? ? "NULL" : "NOT NULL"
|
26
|
+
null_value = "NOT NULL" if desc[:primary_key] == true
|
27
|
+
"#{name} #{type} #{primary_key} #{null_value}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def allowed_field_types
|
31
|
+
["int", "str", "time", "date"]
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_table_fields(table_name, qry_str)
|
35
|
+
begin
|
36
|
+
db_conn.execute "CREATE TABLE IF NOT EXISTS #{table_name} (#{qry_str});"
|
37
|
+
rescue
|
38
|
+
puts db_error("Can not create table")
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def verify_col_type(field_hash)
|
44
|
+
return true if valid_col_types?(field_hash)
|
45
|
+
"Invalid column type(s) specified"
|
46
|
+
end
|
47
|
+
|
48
|
+
def valid_col_types?(field_hash)
|
49
|
+
field_hash.values.all? { |val| allowed_field_types.include? val.to_s }
|
50
|
+
end
|
51
|
+
|
52
|
+
def password_hash(password)
|
53
|
+
Digest::SHA1.hexdigest(password)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class DataUtility
|
2
|
+
class << self
|
3
|
+
def type_mapper(field_hash)
|
4
|
+
db_data = {}
|
5
|
+
field_hash.each_pair do |k, v|
|
6
|
+
v = v.to_s
|
7
|
+
if v == "str" || v == "time" || v == "date"
|
8
|
+
db_data[k] = "VARCHAR"
|
9
|
+
elsif v == "int"
|
10
|
+
db_data[k] = "INTEGER"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
db_data
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_table_query(db_data)
|
17
|
+
db_str = ""
|
18
|
+
db_data.each_pair do |k,v|
|
19
|
+
db_str +=" " + "#{k}" + " " + "#{v}"
|
20
|
+
db_str += "," unless k == db_data.keys.last
|
21
|
+
end
|
22
|
+
db_str
|
23
|
+
end
|
24
|
+
|
25
|
+
def db_error(message)
|
26
|
+
<<-EOS
|
27
|
+
*************************************************
|
28
|
+
DB ERROR - #{message.to_s.titleize}
|
29
|
+
*************************************************
|
30
|
+
EOS
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Matrack
|
2
|
+
class FetchQueries < DataManager
|
3
|
+
class << self
|
4
|
+
def table_name
|
5
|
+
self.to_s.to_snake_case + "s"
|
6
|
+
end
|
7
|
+
|
8
|
+
def uniq_id
|
9
|
+
"#{self.to_s.to_snake_case}_id"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(query)
|
13
|
+
objs = []
|
14
|
+
rows = db_conn.execute query
|
15
|
+
rows.each{|row| objs << self.new(row.reject!{ |k| !k.is_a? String }) }
|
16
|
+
objs
|
17
|
+
end
|
18
|
+
|
19
|
+
def all
|
20
|
+
execute "SELECT * FROM #{table_name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def find(id)
|
24
|
+
execute("SELECT * FROM #{table_name} WHERE (#{uniq_id} = #{id})").first
|
25
|
+
end
|
26
|
+
|
27
|
+
def last
|
28
|
+
offset = count - 1
|
29
|
+
execute("SELECT * FROM #{table_name} LIMIT(1) OFFSET(#{offset})").first
|
30
|
+
end
|
31
|
+
|
32
|
+
def limit(num)
|
33
|
+
execute "SELECT * FROM #{table_name} LIMIT(#{num})"
|
34
|
+
end
|
35
|
+
|
36
|
+
def first
|
37
|
+
execute("SELECT * FROM #{table_name} LIMIT(1)").first
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_by(col, val)
|
41
|
+
execute("SELECT * FROM #{table_name} WHERE (#{col} = '#{val}')
|
42
|
+
LIMIT 1").first
|
43
|
+
end
|
44
|
+
|
45
|
+
# firstname: "name", lastname: "last"
|
46
|
+
def find_cols(col_hash)
|
47
|
+
clause = col_hash.map{|k,v| "#{k.to_s} = " "'#{v}'"}.join(" AND ")
|
48
|
+
execute("SELECT * FROM #{table_name} WHERE (#{clause})
|
49
|
+
LIMIT 1").first
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|