tgauge 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/.gitignore +10 -0
- data/Gemfile +4 -0
- data/README.md +36 -0
- data/Rakefile +2 -0
- data/TGauge.gemspec +40 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/tgauge.rb +119 -0
- data/lib/app/controllers/flash.rb +90 -0
- data/lib/app/controllers/session.rb +27 -0
- data/lib/app/controllers/tcontroller_base.rb +91 -0
- data/lib/app/models/associatable.rb +117 -0
- data/lib/app/models/searchable.rb +47 -0
- data/lib/app/models/trecord_base.rb +163 -0
- data/lib/db/db_connection.rb +99 -0
- data/lib/db/router.rb +74 -0
- data/lib/db/server.rb +27 -0
- data/lib/db/show_exceptions.rb +62 -0
- data/lib/db/static_viewer.rb +52 -0
- data/lib/db/templates/error_page.html.erb +62 -0
- data/lib/templates/Gemfile +3 -0
- data/lib/templates/config/routes.rb +7 -0
- data/lib/templates/db/seeds.rb +7 -0
- data/lib/tgauge.rb +16 -0
- data/lib/version.rb +3 -0
- metadata +180 -0
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require_relative 'searchable'
|
3
|
+
require_relative 'associatable'
|
4
|
+
require_relative '../../db/db_connection'
|
5
|
+
|
6
|
+
module TGauge
|
7
|
+
class TRecordBase
|
8
|
+
extend Associatable
|
9
|
+
extend Searchable
|
10
|
+
|
11
|
+
def self.my_attr_accessor(*names)
|
12
|
+
names.each do |name|
|
13
|
+
define_method(name) do
|
14
|
+
instance_variable_get("@" + name.to_s)
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method(name.to_s + "=") do |arg|
|
18
|
+
instance_variable_set("@" + name.to_s, arg)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.my_attr_reader(*names)
|
24
|
+
names.each do |name|
|
25
|
+
define_method(name) do
|
26
|
+
instance_variable_get("@" + name.to_s)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.columns
|
32
|
+
# ...
|
33
|
+
return @columns if @columns
|
34
|
+
|
35
|
+
arr = DBConnection.execute(<<-SQL)
|
36
|
+
SELECT
|
37
|
+
*
|
38
|
+
FROM
|
39
|
+
#{self.table_name}
|
40
|
+
SQL
|
41
|
+
|
42
|
+
@columns = []
|
43
|
+
arr.nfields.times do |i|
|
44
|
+
@columns << arr.fname(i)
|
45
|
+
end
|
46
|
+
|
47
|
+
@columns
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.finalize!
|
51
|
+
columns.each do |column|
|
52
|
+
inst_var = "@" + column.to_s
|
53
|
+
define_method(column) do
|
54
|
+
attributes[column]
|
55
|
+
end
|
56
|
+
define_method(column.to_s + "=") do |arg|
|
57
|
+
attributes[column] = arg
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.table_name=(table_name)
|
63
|
+
@table_name = table_name
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.table_name
|
67
|
+
@table_name = @table_name || self.to_s.tableize
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.all
|
71
|
+
objs_arr = DBConnection.execute(<<-SQL)
|
72
|
+
SELECT
|
73
|
+
#{table_name}.*
|
74
|
+
FROM
|
75
|
+
#{table_name}
|
76
|
+
SQL
|
77
|
+
|
78
|
+
parse_all(objs_arr)
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.parse_all(results)
|
82
|
+
# ...
|
83
|
+
results.map { |obj_hash| self.new(obj_hash) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.find(id)
|
87
|
+
obj = DBConnection.execute(<<-SQL, id)
|
88
|
+
SELECT
|
89
|
+
#{table_name}.*
|
90
|
+
FROM
|
91
|
+
#{table_name}
|
92
|
+
WHERE
|
93
|
+
#{table_name}.id = ?
|
94
|
+
SQL
|
95
|
+
|
96
|
+
parse_all(obj).first
|
97
|
+
end
|
98
|
+
|
99
|
+
def initialize(params = {})
|
100
|
+
# ...
|
101
|
+
params.each do |att_name, val|
|
102
|
+
att_name = att_name.to_sym
|
103
|
+
raise "unknown attribute '#{att_name}'" unless columns.include?(att_name.to_s)
|
104
|
+
self.send(att_name.to_s + "=", val)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def attributes
|
109
|
+
@attributes ||= {}
|
110
|
+
end
|
111
|
+
|
112
|
+
def attribute_values
|
113
|
+
attributes.values
|
114
|
+
end
|
115
|
+
|
116
|
+
def insert
|
117
|
+
cols = columns.reject { |col| col == "id" }
|
118
|
+
attr_count = cols.count
|
119
|
+
column_str = cols.join(", ")
|
120
|
+
quest_str = Array.new(attr_count) {"?"}.join(", ")
|
121
|
+
debugger
|
122
|
+
DBConnection.execute(<<-SQL, attribute_values)
|
123
|
+
INSERT INTO
|
124
|
+
#{table_name} (#{column_str})
|
125
|
+
VALUES
|
126
|
+
(#{quest_str})
|
127
|
+
SQL
|
128
|
+
end
|
129
|
+
|
130
|
+
def update
|
131
|
+
attr_count = columns.count - 1
|
132
|
+
column_str = columns[1..-1].map { |col| "#{col} = ?" }.join(", ")
|
133
|
+
|
134
|
+
DBConnection.execute(<<-SQL, attribute_values)
|
135
|
+
UPDATE
|
136
|
+
#{table_name}
|
137
|
+
SET
|
138
|
+
#{column_str}
|
139
|
+
WHERE
|
140
|
+
id = ?
|
141
|
+
SQL
|
142
|
+
end
|
143
|
+
|
144
|
+
def save
|
145
|
+
if attributes[:id]
|
146
|
+
update
|
147
|
+
else
|
148
|
+
insert
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def columns
|
155
|
+
self.class.columns
|
156
|
+
end
|
157
|
+
|
158
|
+
def table_name
|
159
|
+
self.class.table_name
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'pg'
|
2
|
+
require 'uri'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
PRINT_QUERIES = ENV['PRINT_QUERIES'] == 'true'
|
6
|
+
MIGRATION_FILES = Dir.glob("./db/migrations/*.sql").to_a
|
7
|
+
|
8
|
+
module TGauge
|
9
|
+
class DBConnection
|
10
|
+
def self.app_name
|
11
|
+
YAML.load_file(Dir.pwd + '/config/database.yml')['database']
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.open
|
15
|
+
@postgres = PG::Connection.new(
|
16
|
+
dbname: app_name,
|
17
|
+
port: 5432
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.migrate
|
22
|
+
ensure_migrations_table
|
23
|
+
|
24
|
+
MIGRATION_FILES.each do |file|
|
25
|
+
filename = file.match(/([\w|-]*)\.sql$/)[1]
|
26
|
+
|
27
|
+
unless migrated_files.include?(filename)
|
28
|
+
instance.exec(File.read(file))
|
29
|
+
instance.exec(<<-SQL)
|
30
|
+
INSERT INTO
|
31
|
+
migrations (filename)
|
32
|
+
VALUES
|
33
|
+
('#{filename}')
|
34
|
+
SQL
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.instance
|
40
|
+
open if @db.nil?
|
41
|
+
|
42
|
+
@db
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.execute(*args)
|
46
|
+
print_query(*args)
|
47
|
+
instance.execute(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.last_insert_row_id
|
51
|
+
instance.last_insert_row_id
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.reset
|
55
|
+
commands = [
|
56
|
+
"dropdb #{app_name}",
|
57
|
+
"createdb #{app_name}"
|
58
|
+
]
|
59
|
+
commands.each { |command| `#{command}` }
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def self.ensure_migrations_table
|
65
|
+
table = instance.exec(<<-SQL)
|
66
|
+
SELECT to_regclass('migrations') AS exists
|
67
|
+
SQL
|
68
|
+
|
69
|
+
unless table[0]['exists']
|
70
|
+
instance.exec(<<-SQL)
|
71
|
+
CREATE_TABLE migrations(
|
72
|
+
id SERIAL PRIMARY KEY,
|
73
|
+
filename VARCHAR(255) NOT NULL
|
74
|
+
)
|
75
|
+
SQL
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.migrated_files
|
80
|
+
Set.new instance.exec(<<-SQL).values.flatten
|
81
|
+
SELECT
|
82
|
+
filename
|
83
|
+
FROM
|
84
|
+
migrations
|
85
|
+
SQL
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.print_query(query, *interpolation_args)
|
89
|
+
return unless PRINT_QUERIES
|
90
|
+
|
91
|
+
puts '--------------------'
|
92
|
+
puts query
|
93
|
+
unless interpolation_args.empty?
|
94
|
+
puts "interpolate: #{interpolation_args.inspect}"
|
95
|
+
end
|
96
|
+
puts '--------------------'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/db/router.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
class Route
|
2
|
+
attr_reader :pattern, :http_method, :controller_class, :action_name
|
3
|
+
|
4
|
+
def initialize(pattern, http_method, controller_class, action_name)
|
5
|
+
@pattern = pattern
|
6
|
+
@http_method = http_method
|
7
|
+
@controller_class = controller_class
|
8
|
+
@action_name = action_name
|
9
|
+
end
|
10
|
+
|
11
|
+
# checks if pattern matches path and method matches request method
|
12
|
+
def matches?(req)
|
13
|
+
(@pattern =~ req.path) == 0 && req.request_method.downcase == @http_method.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
# use pattern to pull out route params (save for later?)
|
17
|
+
# instantiate controller and call controller action
|
18
|
+
def run(req, res)
|
19
|
+
match_data = @pattern.match(req.path)
|
20
|
+
route_params = {}
|
21
|
+
match_data.names.each do |key|
|
22
|
+
route_params[key] = match_data[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
controller = controller_class.new(req, res, route_params)
|
26
|
+
controller.invoke_action(@action_name)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Router
|
31
|
+
attr_reader :routes
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
@routes = []
|
35
|
+
end
|
36
|
+
|
37
|
+
# simply adds a new route to the list of routes
|
38
|
+
def add_route(pattern, method, controller_class, action_name)
|
39
|
+
@routes << Route.new(pattern, method, controller_class, action_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
# evaluate the proc in the context of the instance
|
43
|
+
# for syntactic sugar :)
|
44
|
+
def draw(&proc)
|
45
|
+
self.instance_eval(&proc)
|
46
|
+
end
|
47
|
+
|
48
|
+
# make each of these methods that
|
49
|
+
# when called add route
|
50
|
+
[:get, :post, :put, :delete].each do |http_method|
|
51
|
+
define_method(http_method) do |path, controller, action_name|
|
52
|
+
add_route(path, http_method, controller, action_name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# should return the route that matches this request
|
57
|
+
def match(req)
|
58
|
+
@routes.each do |route|
|
59
|
+
return route if route.matches?(req)
|
60
|
+
end
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# either throw 404 or call run on a matched route
|
65
|
+
def run(req, res)
|
66
|
+
route = match(req)
|
67
|
+
if route
|
68
|
+
route.run(req, res)
|
69
|
+
else
|
70
|
+
res.write("Route #{req.path} could not be found.")
|
71
|
+
res.status = 404
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/db/server.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require_relative './config/routes'
|
3
|
+
require_relative 'lib/static_viewer'
|
4
|
+
require_relative 'lib/show_exceptions'
|
5
|
+
|
6
|
+
module TGauge
|
7
|
+
class Server
|
8
|
+
def self.start
|
9
|
+
app = Proc.new do |env|
|
10
|
+
req = Rack::Request.new(env)
|
11
|
+
res = Rack::Response.new
|
12
|
+
ROUTER.run(req, res)
|
13
|
+
res.finish
|
14
|
+
end
|
15
|
+
|
16
|
+
full_app = Rack::Builder.new do
|
17
|
+
use ShowExceptions
|
18
|
+
use StaticViewer
|
19
|
+
run app
|
20
|
+
end.to_app
|
21
|
+
|
22
|
+
Rack::Server.start({
|
23
|
+
app: full_app,
|
24
|
+
Port: 3000
|
25
|
+
})
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'byebug'
|
3
|
+
|
4
|
+
class ShowExceptions
|
5
|
+
attr_reader :app
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
app.call(env)
|
13
|
+
rescue Exception => e
|
14
|
+
render_exception(e)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def render_exception(e)
|
20
|
+
dir_path = File.dirname(__FILE__)
|
21
|
+
template_fname = File.join(dir_path, "templates", "error_page.html.erb")
|
22
|
+
template = File.read(template_fname)
|
23
|
+
body = ERB.new(template).result(binding)
|
24
|
+
|
25
|
+
['500', {'Content-type' => 'text/html'}, [body]]
|
26
|
+
end
|
27
|
+
|
28
|
+
def error_source_file(e)
|
29
|
+
stack_trace_top(e)[0]
|
30
|
+
end
|
31
|
+
|
32
|
+
def stack_trace_top(e)
|
33
|
+
e.backtrace[0].split(':')
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_formatted_source(e)
|
37
|
+
source_file_name = error_source_file(e)
|
38
|
+
source_line_num = source_line_num(e)
|
39
|
+
source_lines = extract_source(source_file_name)
|
40
|
+
format_source(source_lines, source_line_num)
|
41
|
+
end
|
42
|
+
|
43
|
+
def source_line_num(e)
|
44
|
+
stack_trace_top(e)[1].to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
def formatted_source(file, source_line_num)
|
48
|
+
source_lines = extract_source(file)
|
49
|
+
format_source(source_lines, source_line_num)
|
50
|
+
end
|
51
|
+
|
52
|
+
def extract_source(file)
|
53
|
+
source_file = File.open(file, 'r')
|
54
|
+
source_file.readlines
|
55
|
+
end
|
56
|
+
|
57
|
+
def format_source(source_lines, source_line_num)
|
58
|
+
start = [0, source_line_num - 3].max
|
59
|
+
lines = source_lines[start..(start + 5)]
|
60
|
+
Hash[*(start+1..(lines.count + start)).zip(lines).flatten]
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class StaticViewer
|
5
|
+
FILE_TYPE = {
|
6
|
+
'jpg' => 'image/jpeg',
|
7
|
+
'zip' => 'application/zip',
|
8
|
+
'png' => 'image/png',
|
9
|
+
'txt' => 'text/plain'
|
10
|
+
}
|
11
|
+
|
12
|
+
def initialize(app)
|
13
|
+
@app = app
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
if is_public?(env)
|
18
|
+
req = Rack::Request.new(env)
|
19
|
+
res = Rack::Response.new
|
20
|
+
|
21
|
+
file_path = "#{req.path}"
|
22
|
+
build_response(res, file_path)
|
23
|
+
else
|
24
|
+
@app.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def build_response(res, file_path)
|
30
|
+
type = find_content_type(file_path)
|
31
|
+
|
32
|
+
if type && Pathname(file_path).exist?
|
33
|
+
res.write(File.open(file_path, 'r') { |f| (f.read) })
|
34
|
+
res['Content-Type'] = find_content_type(file_path)
|
35
|
+
res.finish
|
36
|
+
else
|
37
|
+
type ? res.write("Could not find file.") : res.write("Unsupported file type")
|
38
|
+
res.status = 404
|
39
|
+
res['Content-Type'] = 'text/html'
|
40
|
+
res.finish
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def is_public?(env)
|
45
|
+
!!(Regexp.new("/public/*") =~ env["PATH_INFO"])
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_content_type(path)
|
49
|
+
type = path.match(/\.(\w*)/)[1]
|
50
|
+
FILE_TYPE[type]
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
<head>
|
2
|
+
<style>
|
3
|
+
.header {
|
4
|
+
font-family: sans-serif;
|
5
|
+
background: #d00;
|
6
|
+
padding: 10px;
|
7
|
+
color: #fff;
|
8
|
+
}
|
9
|
+
|
10
|
+
.source-view {
|
11
|
+
border: 10px solid lightgray;
|
12
|
+
padding: 5px;
|
13
|
+
width: 800px;
|
14
|
+
}
|
15
|
+
|
16
|
+
.line {
|
17
|
+
white-space: pre-wrap;
|
18
|
+
font-family: sans-serif;
|
19
|
+
padding: 2px 0;
|
20
|
+
}
|
21
|
+
|
22
|
+
.line-num {
|
23
|
+
background: lightgray;
|
24
|
+
padding: 2px;
|
25
|
+
border-top: 1px solid #aaa;
|
26
|
+
}
|
27
|
+
|
28
|
+
.line.error {
|
29
|
+
background: #d00;
|
30
|
+
color: #fff;
|
31
|
+
}
|
32
|
+
</style>
|
33
|
+
</head>
|
34
|
+
|
35
|
+
<body>
|
36
|
+
<h2 class='header'><%=e.class%>: <%=e.message%></h2>
|
37
|
+
|
38
|
+
<h4>Extracted source (around line <b><%=source_line_num(e)%></b>):</h4>
|
39
|
+
|
40
|
+
<div class='source-view'>
|
41
|
+
<table cellpadding="0" cellspacing="0">
|
42
|
+
<% extract_formatted_source(e).each do |line_num, line| %>
|
43
|
+
<tr>
|
44
|
+
<td>
|
45
|
+
<pre class='line-num'><%=line_num %></pre>
|
46
|
+
</td>
|
47
|
+
<td>
|
48
|
+
<pre class='line
|
49
|
+
<%= 'error' if line_num == source_line_num(e)%>'><%= line %></pre>
|
50
|
+
</td>
|
51
|
+
</tr>
|
52
|
+
<% end %>
|
53
|
+
</table>
|
54
|
+
</div>
|
55
|
+
<h5><%= File.expand_path(error_source_file(e)) %></h5>
|
56
|
+
|
57
|
+
<h3>Stack trace</h3>
|
58
|
+
<% e.backtrace.each do |stack_line| %>
|
59
|
+
<%= stack_line %>
|
60
|
+
<br>
|
61
|
+
<% end %>
|
62
|
+
</body>
|
data/lib/tgauge.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module TGauge
|
2
|
+
end
|
3
|
+
|
4
|
+
require_relative './version.rb'
|
5
|
+
|
6
|
+
require_relative './app/models/trecord_base'
|
7
|
+
require_relative './app/controllers/tcontroller_base'
|
8
|
+
|
9
|
+
Dir.glob('./app/models/*.rb') { |file| require file }
|
10
|
+
Dir.glob('./app/controllers/*.rb') { |file| require file }
|
11
|
+
|
12
|
+
require './db/seeds'
|
13
|
+
|
14
|
+
require_relative './db/db_connection'
|
15
|
+
require_relative './db/router'
|
16
|
+
require_relative './db/server'
|
data/lib/version.rb
ADDED