kwipper 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +2 -0
- data/app/controllers/comments_controller.rb +32 -0
- data/app/controllers/post_favorites_controller.rb +17 -0
- data/app/controllers/posts_controller.rb +41 -0
- data/app/controllers/sessions_controller.rb +32 -0
- data/app/controllers/users_controller.rb +70 -0
- data/app/models/comment.rb +20 -0
- data/app/models/post.rb +31 -0
- data/app/models/post_favorite.rb +6 -0
- data/app/models/session.rb +12 -0
- data/app/models/user.rb +21 -0
- data/app/views/edit_user.erb +26 -0
- data/app/views/fave_button.erb +8 -0
- data/app/views/home.erb +126 -0
- data/app/views/layout.erb +110 -0
- data/app/views/login_user.erb +24 -0
- data/app/views/new_comment.erb +18 -0
- data/app/views/new_post.erb +11 -0
- data/app/views/new_user.erb +26 -0
- data/app/views/not_found.erb +11 -0
- data/app/views/pagination.erb +29 -0
- data/app/views/posts.erb +7 -0
- data/app/views/posts_list.erb +44 -0
- data/app/views/reply_button.erb +4 -0
- data/app/views/server_error.erb +17 -0
- data/app/views/show_post.erb +37 -0
- data/app/views/show_user.erb +11 -0
- data/app/views/users.erb +38 -0
- data/db/.DS_Store +0 -0
- data/kwipper.gemspec +27 -0
- data/lib/kwipper.rb +46 -0
- data/lib/kwipper/application.rb +87 -0
- data/lib/kwipper/controller.rb +36 -0
- data/lib/kwipper/controller_helpers.rb +22 -0
- data/lib/kwipper/errors.rb +5 -0
- data/lib/kwipper/http_parser.rb +51 -0
- data/lib/kwipper/http_server.rb +61 -0
- data/lib/kwipper/inflect.rb +19 -0
- data/lib/kwipper/model.rb +174 -0
- data/lib/kwipper/paginator.rb +71 -0
- data/lib/kwipper/renders_views.rb +18 -0
- data/lib/kwipper/request.rb +35 -0
- data/lib/kwipper/request_headers.rb +36 -0
- data/lib/kwipper/response.rb +85 -0
- data/lib/kwipper/version.rb +3 -0
- data/public/css/bootstrap-lumen.min.css +7 -0
- data/public/css/styles.css +48 -0
- data/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/public/fonts/glyphicons-halflings-regular.svg +229 -0
- data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/public/js/bootstrap.min.js +7 -0
- metadata +173 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
|
8
|
+
<title>Kwipper</title>
|
9
|
+
|
10
|
+
<link rel="stylesheet" href="/css/bootstrap-lumen.min.css">
|
11
|
+
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
12
|
+
<link rel="stylesheet" href="/css/styles.css">
|
13
|
+
|
14
|
+
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
15
|
+
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
16
|
+
<!--[if lt IE 9]>
|
17
|
+
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
18
|
+
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
19
|
+
<![endif]-->
|
20
|
+
</head>
|
21
|
+
<body role="document">
|
22
|
+
|
23
|
+
<nav class="navbar navbar-default">
|
24
|
+
<div class="container">
|
25
|
+
<div class="navbar-header">
|
26
|
+
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
27
|
+
<span class="sr-only">Toggle navigation</span>
|
28
|
+
<span class="icon-bar"></span>
|
29
|
+
<span class="icon-bar"></span>
|
30
|
+
<span class="icon-bar"></span>
|
31
|
+
</button>
|
32
|
+
<a class="navbar-brand" href="/">Kwipper</a>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div id="navbar" class="collapse navbar-collapse">
|
36
|
+
<ul class="nav navbar-nav">
|
37
|
+
<li class="<%= 'active' if action == :posts %>">
|
38
|
+
<a href="/kwips">Kwips</a>
|
39
|
+
</li>
|
40
|
+
<% if current_user %>
|
41
|
+
<li class="<%= 'active' if action == :users %>">
|
42
|
+
<a href="/users">Users</a>
|
43
|
+
</li>
|
44
|
+
<% end %>
|
45
|
+
</ul>
|
46
|
+
|
47
|
+
<ul class="nav navbar-nav navbar-right">
|
48
|
+
<% if current_user %>
|
49
|
+
<li class="dropdown">
|
50
|
+
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
|
51
|
+
<%= current_user.username %> <span class="caret"></span>
|
52
|
+
</a>
|
53
|
+
<ul class="dropdown-menu" role="menu">
|
54
|
+
<li>
|
55
|
+
<a href="/users/edit?id=<%= current_user.id %>">Profile</a>
|
56
|
+
</li>
|
57
|
+
<li>
|
58
|
+
<a href="/logout">
|
59
|
+
<span class="fa fa-sign-out"> </span>
|
60
|
+
Logout
|
61
|
+
</a>
|
62
|
+
</li>
|
63
|
+
</ul>
|
64
|
+
</li>
|
65
|
+
<% else %>
|
66
|
+
<li>
|
67
|
+
<a href="/login">
|
68
|
+
<span class="fa fa-sign-in"> </span>
|
69
|
+
Login
|
70
|
+
</a>
|
71
|
+
</li>
|
72
|
+
<% end %>
|
73
|
+
</ul>
|
74
|
+
|
75
|
+
<% if current_user %>
|
76
|
+
<ul class="nav navbar-right navbar-nav nav-pills">
|
77
|
+
<li role="presentation">
|
78
|
+
<a href="/kwips/new">
|
79
|
+
<span class="fa fa-plus"> </span>
|
80
|
+
New Kwip
|
81
|
+
</a>
|
82
|
+
</li>
|
83
|
+
</ul>
|
84
|
+
<% end %>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
</nav>
|
88
|
+
|
89
|
+
<div class="container">
|
90
|
+
<div class="row">
|
91
|
+
<div class="col-sm-2"></div>
|
92
|
+
|
93
|
+
<div class="col-sm-8">
|
94
|
+
<%= @view %>
|
95
|
+
</div>
|
96
|
+
|
97
|
+
<div class="col-sm-2"></div>
|
98
|
+
</div>
|
99
|
+
</div>
|
100
|
+
|
101
|
+
<footer class="footer">
|
102
|
+
<div class="container">
|
103
|
+
<p class="text-muted">© 2015 Diego Salazar</p>
|
104
|
+
</div>
|
105
|
+
</footer>
|
106
|
+
|
107
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
108
|
+
<script src="/js/bootstrap.min.js"></script>
|
109
|
+
</body>
|
110
|
+
</html>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<h1>
|
2
|
+
<span class="fa fa-sign-in"></span>
|
3
|
+
Login
|
4
|
+
</h1>
|
5
|
+
|
6
|
+
<div class="row">
|
7
|
+
<div class="col-sm-12">
|
8
|
+
<form action="/sessions/create" method="post">
|
9
|
+
<div class="form-group">
|
10
|
+
<label>Username</label>
|
11
|
+
<input type="text" name="username" class="form-control">
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div class="form-group">
|
15
|
+
<label>Password</label>
|
16
|
+
<input type="text" name="password" class="form-control">
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="form-group">
|
20
|
+
<input type="submit" value="Login" class="btn btn-primary">
|
21
|
+
</div>
|
22
|
+
</form>
|
23
|
+
</div>
|
24
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<h1><%= @post.username %></h1>
|
2
|
+
<p>@ <%= @post.created_at %></p>
|
3
|
+
|
4
|
+
<div class="well">
|
5
|
+
<%= @post.content %>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<h2>New Comment</h2>
|
9
|
+
|
10
|
+
<form action="/kwips/comments/create?id=<%= @post.id %>" method="post">
|
11
|
+
<div class="form-group">
|
12
|
+
<textarea name="content" class="form-control" rows="5"></textarea>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="form-group">
|
16
|
+
<input type="submit" value="Submit" class="btn btn-xs btn-success">
|
17
|
+
</div>
|
18
|
+
</form>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<h1>New Kwip</h1>
|
2
|
+
|
3
|
+
<form action="/kwips/create" method="post">
|
4
|
+
<div class="form-group">
|
5
|
+
<textarea name="content" class="form-control" rows="5"></textarea>
|
6
|
+
</div>
|
7
|
+
|
8
|
+
<div class="form-group">
|
9
|
+
<input type="submit" value="Create" class="btn btn-primary">
|
10
|
+
</div>
|
11
|
+
</form>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
<h1>New User</h1>
|
2
|
+
|
3
|
+
<div class="row">
|
4
|
+
<div class="col-sm-12">
|
5
|
+
<form action="/users/create" method="post">
|
6
|
+
<div class="form-group">
|
7
|
+
<label>Username</label>
|
8
|
+
<input type="text" name="username" class="form-control">
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="form-group">
|
12
|
+
<label>Email</label>
|
13
|
+
<input type="text" name="email" class="form-control">
|
14
|
+
</div>
|
15
|
+
|
16
|
+
<div class="form-group">
|
17
|
+
<label>Password</label>
|
18
|
+
<input type="text" name="hashed_password" class="form-control">
|
19
|
+
</div>
|
20
|
+
|
21
|
+
<div class="form-group">
|
22
|
+
<input type="submit" value="Create" class="btn btn-primary">
|
23
|
+
</div>
|
24
|
+
</form>
|
25
|
+
</div>
|
26
|
+
</div>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<nav>
|
2
|
+
<ul class="pagination pull-left">
|
3
|
+
<li class="<%= 'disabled' if @paginator.on_first_page? %>">
|
4
|
+
<a href="<%= @paginator.prev_page_path %>" aria-label="Previous">
|
5
|
+
<span aria-hidden="true">«</span>
|
6
|
+
</a>
|
7
|
+
</li>
|
8
|
+
|
9
|
+
<% @paginator.pages.each do |page| %>
|
10
|
+
<li class="<%= 'active' if page.current? %>">
|
11
|
+
<a href="<%= page.path %>">
|
12
|
+
<%= page.num %>
|
13
|
+
</a>
|
14
|
+
</li>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<li class="<%= 'disabled' if @paginator.on_last_page? %>">
|
18
|
+
<a href="<%= @paginator.next_page_path %>" aria-label="Next">
|
19
|
+
<span aria-hidden="true">»</span>
|
20
|
+
</a>
|
21
|
+
</li>
|
22
|
+
</ul>
|
23
|
+
|
24
|
+
<p class="pagination-stats pull-left">
|
25
|
+
Showing <%= @paginator.from %>
|
26
|
+
to <%= @paginator.to %>
|
27
|
+
of <%= @paginator.count %>
|
28
|
+
</p>
|
29
|
+
</nav>
|
data/app/views/posts.erb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
<ul class="list-unstyled">
|
2
|
+
<% @posts.each do |post| %>
|
3
|
+
<li>
|
4
|
+
<p>
|
5
|
+
<a href="/users/show?id=<%= post.user_id %>">
|
6
|
+
<strong><%= post.username %></strong>
|
7
|
+
</a>
|
8
|
+
@ <%= post.created_at %>
|
9
|
+
</p>
|
10
|
+
|
11
|
+
<div class="well">
|
12
|
+
<%= post.content %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="row">
|
16
|
+
<div class="col-sm-12">
|
17
|
+
<div class="btn-group pull-left">
|
18
|
+
<span class="badge">
|
19
|
+
<%= Inflect.plural post.faves_count, 'Fave' %>
|
20
|
+
</span>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="btn-group pull-right">
|
24
|
+
<%= render :fave_button, post: post if current_user %>
|
25
|
+
|
26
|
+
<a href="/kwips/show?id=<%= post.id %>" class="btn btn-xs btn-info">
|
27
|
+
<span class="fa fa-comments-o"> </span>
|
28
|
+
Comments
|
29
|
+
<span class="badge">
|
30
|
+
<%= post.comments_count %>
|
31
|
+
</span>
|
32
|
+
</a>
|
33
|
+
|
34
|
+
<%= render :reply_button, post: post if current_user %>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<hr>
|
40
|
+
</li>
|
41
|
+
<% end %>
|
42
|
+
|
43
|
+
<%= render :pagination if @posts.any? %>
|
44
|
+
</ul>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<h1><%= response.info %></h1>
|
2
|
+
|
3
|
+
<p>
|
4
|
+
<code>
|
5
|
+
<%= @error.class %> <%= CGI.escapeHTML @error.message %>
|
6
|
+
</code>
|
7
|
+
</p>
|
8
|
+
|
9
|
+
<pre>
|
10
|
+
<%= @error.backtrace.map { |line| CGI.escapeHTML line }.join "\n" %>
|
11
|
+
</pre>
|
12
|
+
|
13
|
+
<p>Params:</p>
|
14
|
+
|
15
|
+
<pre>
|
16
|
+
<%= params.pretty_inspect %>
|
17
|
+
</pre>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<h1><%= @post.username %></h1>
|
2
|
+
<p>
|
3
|
+
<span class="fa fa-clock-o"> </span>
|
4
|
+
<%= @post.created_at %>
|
5
|
+
</p>
|
6
|
+
|
7
|
+
<div class="well">
|
8
|
+
<%= @post.content %>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="btn-group pull-right">
|
12
|
+
<% if current_user %>
|
13
|
+
<%= render :fave_button, post: @post %>
|
14
|
+
<%= render :reply_button, post: @post %>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<% if @comments.empty? %>
|
19
|
+
<h2>No Comments</h2>
|
20
|
+
<% else %>
|
21
|
+
<h2>Comments</h2>
|
22
|
+
|
23
|
+
<ul class="list-unstyled">
|
24
|
+
<% @comments.each do |comment| %>
|
25
|
+
<li>
|
26
|
+
<p>
|
27
|
+
<span class="fa fa-comments"> </span>
|
28
|
+
<%= comment.username %> @ <%= comment.created_at %>
|
29
|
+
</p>
|
30
|
+
|
31
|
+
<div class="well well-xs">
|
32
|
+
<%= comment.content %>
|
33
|
+
</div>
|
34
|
+
</li>
|
35
|
+
<% end %>
|
36
|
+
</ul>
|
37
|
+
<% end %>
|
data/app/views/users.erb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
<h1>Users</h1>
|
2
|
+
|
3
|
+
<table class="table table-striped table-hover">
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th>ID</th>
|
7
|
+
<th>Username</th>
|
8
|
+
<th>Email</th>
|
9
|
+
<th>
|
10
|
+
<a href="/users/new" class="btn btn-xs btn-primary pull-right">
|
11
|
+
<span class="fa fa-user-plus"> </span>
|
12
|
+
New User
|
13
|
+
</a>
|
14
|
+
</th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
|
18
|
+
<tbody>
|
19
|
+
<% @users.each do |user| %>
|
20
|
+
<tr>
|
21
|
+
<td><%= user.id %></td>
|
22
|
+
<td><%= user.username %></td>
|
23
|
+
<td><%= user.email %></td>
|
24
|
+
<td>
|
25
|
+
<div class="text-right">
|
26
|
+
<a href="/users/edit?id=<%= user.id %>" class="btn btn-xs btn-primary">
|
27
|
+
Edit
|
28
|
+
</a>
|
29
|
+
<form action="/users/destroy" method="post" class="pull-right">
|
30
|
+
<input type="hidden" name="id" value="<%= user.id %>">
|
31
|
+
<input type="submit" value="Destroy" class="btn btn-xs btn-danger">
|
32
|
+
</form>
|
33
|
+
</div>
|
34
|
+
</td>
|
35
|
+
</tr>
|
36
|
+
<% end %>
|
37
|
+
</tbody>
|
38
|
+
</table>
|
data/db/.DS_Store
ADDED
Binary file
|
data/kwipper.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'kwipper/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "kwipper"
|
8
|
+
spec.version = Kwipper::VERSION
|
9
|
+
spec.authors = ["Diego Salazar"]
|
10
|
+
spec.email = ["diego@greyrobot.com"]
|
11
|
+
spec.summary = %q{Kwipper is the reference example app for the Kwipper Programming Challenge}
|
12
|
+
spec.description = %q{Kwipper is the reference example app for the Kwipper Programming Challenge to build an app and server from scratch.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency 'sqlite3'
|
22
|
+
spec.add_dependency 'colorize'
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
end
|
data/lib/kwipper.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
$START_TIME = Time.now.to_f
|
2
|
+
require "socket"
|
3
|
+
require "securerandom"
|
4
|
+
require "sqlite3"
|
5
|
+
require "erb"
|
6
|
+
require "uri"
|
7
|
+
require "logger"
|
8
|
+
require "mime-types"
|
9
|
+
require "rack/utils"
|
10
|
+
require "colorize"
|
11
|
+
require "pry"
|
12
|
+
|
13
|
+
module Kwipper
|
14
|
+
ROOT = Dir.pwd
|
15
|
+
|
16
|
+
def self.log_startup_time
|
17
|
+
log.debug "Started in #{sprintf '%.2f', Time.now.to_f - $START_TIME}s"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def log
|
22
|
+
@logger ||= Logger.new(STDOUT).tap do |logger|
|
23
|
+
logger.datetime_format = '%Y-%m-%d %H:%M:%S '
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require "kwipper/version"
|
28
|
+
require "kwipper/errors"
|
29
|
+
# Server
|
30
|
+
require "kwipper/http_parser"
|
31
|
+
require "kwipper/http_server"
|
32
|
+
require "kwipper/request"
|
33
|
+
require "kwipper/request_headers"
|
34
|
+
require "kwipper/response"
|
35
|
+
# Helpers
|
36
|
+
require "kwipper/inflect"
|
37
|
+
require "kwipper/renders_views"
|
38
|
+
require "kwipper/controller_helpers"
|
39
|
+
# Micro framework
|
40
|
+
require "kwipper/application"
|
41
|
+
require "kwipper/controller"
|
42
|
+
require "kwipper/model"
|
43
|
+
# Extensions
|
44
|
+
require "kwipper/paginator"
|
45
|
+
|
46
|
+
Kwipper::HttpServer.run if __FILE__ == $0
|