rhail 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/LICENSE +21 -0
- data/README.md +77 -0
- data/bin/rhail +78 -0
- data/generator_folders/.DS_Store +0 -0
- data/generator_folders/json/.DS_Store +0 -0
- data/generator_folders/json/.ruby-version +1 -0
- data/generator_folders/json/Gemfile +6 -0
- data/generator_folders/json/Gemfile.lock +17 -0
- data/generator_folders/json/config.ru +61 -0
- data/generator_folders/json/db/database.sqlite3 +0 -0
- data/generator_folders/json/db/seed.rb +31 -0
- data/generator_folders/json/public/.DS_Store +0 -0
- data/generator_folders/json/public/img/.DS_Store +0 -0
- data/generator_folders/json/public/img/favicon.png +0 -0
- data/generator_folders/json/settings.yml +1 -0
- data/generator_folders/plain/.DS_Store +0 -0
- data/generator_folders/plain/.ruby-version +1 -0
- data/generator_folders/plain/Gemfile +6 -0
- data/generator_folders/plain/Gemfile.lock +17 -0
- data/generator_folders/plain/config.ru +59 -0
- data/generator_folders/plain/db/database.sqlite3 +0 -0
- data/generator_folders/plain/db/seed.rb +31 -0
- data/generator_folders/plain/public/.DS_Store +0 -0
- data/generator_folders/plain/public/css/styles.css +123 -0
- data/generator_folders/plain/public/html/example_one.html +35 -0
- data/generator_folders/plain/public/html/example_three.html +75 -0
- data/generator_folders/plain/public/html/example_two.html +50 -0
- data/generator_folders/plain/public/html/foot.html +22 -0
- data/generator_folders/plain/public/html/head.html +51 -0
- data/generator_folders/plain/public/html/home.html +29 -0
- data/generator_folders/plain/public/img/favicon.png +0 -0
- data/generator_folders/plain/public/img/jumbotron.jpg +0 -0
- data/generator_folders/spa/.DS_Store +0 -0
- data/generator_folders/spa/.ruby-version +1 -0
- data/generator_folders/spa/Gemfile +6 -0
- data/generator_folders/spa/Gemfile.lock +17 -0
- data/generator_folders/spa/config.ru +49 -0
- data/generator_folders/spa/db/database.sqlite3 +0 -0
- data/generator_folders/spa/db/seed.rb +31 -0
- data/generator_folders/spa/public/.DS_Store +0 -0
- data/generator_folders/spa/public/css/styles.css +123 -0
- data/generator_folders/spa/public/html/index.html +186 -0
- data/generator_folders/spa/public/img/favicon.png +0 -0
- data/generator_folders/spa/public/img/jumbotron.jpg +0 -0
- data/generator_folders/spa/public/js/another-component.js +72 -0
- data/generator_folders/spa/public/js/app.js +16 -0
- data/lib/rhail.rb +13 -0
- data/lib/rhail/generator.rb +46 -0
- data/lib/rhail/helper.rb +117 -0
- data/lib/rhail/main.rb +8 -0
- data/rhail.gemspec +46 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 056abf1008fba20421c9ba46138ed29b6e5bf034
|
4
|
+
data.tar.gz: 05716cad7a09486017c33edc8b8a818bbb6150f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 64da155c38b307723f7233a7801a65b78d48041208b74b3a71f422d2fbd2a452cfd1a19afd2cfaabc9fd8e4b76dcdbc1942d62de8edd08eeb38a359f795858c4
|
7
|
+
data.tar.gz: 9a6d19cffba546c95747eb6613c7b48fe4b51786581e91f68ed97606f1fd9e1f10f75e07663544012bf7720604d0f5f5f0ffcc09e5fe08b064c50ce5ea1df89a
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2016 Maksim Sundukov
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Ruby Hail
|
2
|
+
|
3
|
+
## About
|
4
|
+
|
5
|
+
Ruby Hail is Rack-based nano framework. It's the fastest-by-design Rack-based framework. It requires less learning (besides Rack, you suppose to know it by this time) than any other framework. It works great for simple dynamic web-sites, single-page web-apps and microservices.
|
6
|
+
|
7
|
+
|
8
|
+
## Why
|
9
|
+
|
10
|
+
I was looking for balance between simplicity and development speed. If you go bellow Rack it's a lot of extra work for simple things. If you put a lot of abstractions on top of Rack then you loose track of what's going on under the hood and with high probability slowing things down.
|
11
|
+
|
12
|
+
I believe in right tool for the right job. If you look for fully-featured framework you might need Rails, something simpler - Sinatra, static site - Jekyll, blog - Wordpress. When you need simple web app or API that can do just couple of things really fast and easy - go Ruby Hail.
|
13
|
+
|
14
|
+
It was born when I was looking for web frameworks for Go and people advocated to not to use any except for the very basic ones. Otherwise you can't get the performance you're aiming for. So I took this concept to my favorite language - Ruby. I wanted to use as bare metal approach much as possible and to implement only the most necessary things so everyone can solve one task per app with high efficiency.
|
15
|
+
|
16
|
+
## Use
|
17
|
+
|
18
|
+
```
|
19
|
+
gem install rhail
|
20
|
+
rhail generate <plain|json|spa> path/to/folder # it will create directory with app structure
|
21
|
+
cd path/to/folder
|
22
|
+
# Install ruby via rvm if needed
|
23
|
+
gem install bundler
|
24
|
+
bundle install
|
25
|
+
bundle exec rackup # Go to http://127.0.0.1:9292 or wherever output tells you.
|
26
|
+
```
|
27
|
+
|
28
|
+
To run it in production treat it as any other Rack-based (Rails or Sinatra) app. For example Passenger should be ponted to `<the folder>/public`.
|
29
|
+
|
30
|
+
Enjoy!
|
31
|
+
|
32
|
+
## Generators
|
33
|
+
|
34
|
+
### Plain
|
35
|
+
|
36
|
+
```
|
37
|
+
rhail generate plain path/to/folder
|
38
|
+
```
|
39
|
+
|
40
|
+
Traditional server-side-based web-app, with example of (relatively secured) form, database and very simple html templating. You can chain htmls and utilize ruby string interpolation.
|
41
|
+
|
42
|
+
### JSON
|
43
|
+
|
44
|
+
```
|
45
|
+
rhail generate json path/to/folder
|
46
|
+
```
|
47
|
+
|
48
|
+
JSON Web-API with authentication.
|
49
|
+
|
50
|
+
#### Play with it
|
51
|
+
|
52
|
+
```
|
53
|
+
curl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' http://127.0.0.1:9292/
|
54
|
+
curl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' http://127.0.0.1:9292/example_one
|
55
|
+
curl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' -H "Content-Type: application/json" -X POST -d '{"city":"asd","state":"asd","zipcode":"123","name":"asd"}' http://127.0.0.1:9292/example_two
|
56
|
+
```
|
57
|
+
|
58
|
+
### SPA
|
59
|
+
|
60
|
+
```
|
61
|
+
rhail generate spa path/to/folder
|
62
|
+
```
|
63
|
+
|
64
|
+
Single-page app based on Ruby Hail with VueJS. VueJS has a lot of similarities to AngularJS 1, minus performance issues.
|
65
|
+
|
66
|
+
## Custom code
|
67
|
+
|
68
|
+
Once you play with automatically generated code you will want to extend it with your real functionality.
|
69
|
+
|
70
|
+
```
|
71
|
+
RhailHelper.render_json(response_structure: {}, headers: {}
|
72
|
+
RhailHelper.render_html files: %w(head body foot), local_vars: {}, headers: {}
|
73
|
+
```
|
74
|
+
|
75
|
+
## Benchmarks
|
76
|
+
|
77
|
+
[In another repo](https://github.com/E-Xor/rhail-benchmark "Ruby Hail Benchmark")
|
data/bin/rhail
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rhail'
|
4
|
+
|
5
|
+
##
|
6
|
+
# Exception in the bin/rhail for the case when user specifies wrong arguments for the rhail command.
|
7
|
+
#
|
8
|
+
class ArgError < StandardError; end
|
9
|
+
|
10
|
+
begin
|
11
|
+
case ARGV[0]
|
12
|
+
when 'g', 'gen', 'generate'
|
13
|
+
type = ARGV[1]
|
14
|
+
raise ArgError unless type
|
15
|
+
|
16
|
+
puts
|
17
|
+
gen = Rhail::Generator.new(type: type, folder: ARGV[2])
|
18
|
+
puts 'Creating file structure ...'
|
19
|
+
gen.create_folder_with_app
|
20
|
+
|
21
|
+
install_dependencies = false # Later get from parameter
|
22
|
+
if type == 'json'
|
23
|
+
further_instructions = %Q( Test it\n\tcurl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' http://127.0.0.1:9292/\n)
|
24
|
+
else
|
25
|
+
further_instructions = " Go to http://127.0.0.1:9292 or wherever output tells you."
|
26
|
+
end
|
27
|
+
|
28
|
+
if install_dependencies
|
29
|
+
puts 'Installing dependencies ...'
|
30
|
+
gen.install_dependencies
|
31
|
+
done_message = %Q(
|
32
|
+
|
33
|
+
\e[32mDone!
|
34
|
+
|
35
|
+
What to do next?
|
36
|
+
|
37
|
+
$ cd #{gen.folder}/#{gen.type}
|
38
|
+
$ bundle exec rackup
|
39
|
+
|
40
|
+
#{further_instructions}
|
41
|
+
|
42
|
+
Enjoy!
|
43
|
+
|
44
|
+
\e[0m
|
45
|
+
|
46
|
+
)
|
47
|
+
else
|
48
|
+
done_message = %Q(
|
49
|
+
|
50
|
+
\e[32mDone!
|
51
|
+
|
52
|
+
What to do next?
|
53
|
+
|
54
|
+
$ cd #{gen.folder}/#{gen.type}
|
55
|
+
$ gem install bundler
|
56
|
+
$ bundle install
|
57
|
+
$ bundle exec rackup
|
58
|
+
|
59
|
+
#{further_instructions}
|
60
|
+
|
61
|
+
Enjoy!
|
62
|
+
|
63
|
+
\e[0m
|
64
|
+
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
puts done_message
|
69
|
+
|
70
|
+
else
|
71
|
+
raise ArgError
|
72
|
+
end
|
73
|
+
rescue ArgError => e
|
74
|
+
puts Rhail::USAGE
|
75
|
+
rescue => e
|
76
|
+
puts "\e[31mError happened: #{e.message}\e[0m"
|
77
|
+
raise e
|
78
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.3.0@rhail_json
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rhail/helper'
|
2
|
+
require 'yaml'
|
3
|
+
require 'sequel'
|
4
|
+
|
5
|
+
use Rack::Static, :urls => ['/img'], :root => 'public' # http://www.rubydoc.info/github/rack/rack/Rack/Static
|
6
|
+
|
7
|
+
SETTINGS = YAML.load_file(File.expand_path('../settings.yml',__FILE__))
|
8
|
+
TOKEN_VALUE = SETTINGS['token']
|
9
|
+
DB = Sequel.sqlite('db/database.sqlite3')
|
10
|
+
|
11
|
+
class WebApi
|
12
|
+
|
13
|
+
def self.call(env)
|
14
|
+
req = Rack::Request.new(env)
|
15
|
+
|
16
|
+
case
|
17
|
+
when req.get? && req.path_info == '/'
|
18
|
+
|
19
|
+
h = {
|
20
|
+
one: '584c5915f896067cb0927bd6',
|
21
|
+
two: 0,
|
22
|
+
three: 'f5ef49a3-2b82-46f5-aa8f-1180f7dbef98',
|
23
|
+
four: true,
|
24
|
+
five: '$2,343.28',
|
25
|
+
six: 33,
|
26
|
+
seven: 'blue',
|
27
|
+
eight: 'dot'
|
28
|
+
}
|
29
|
+
Rhail::Helper.token_auth(env['HTTP_AUTHORIZATION'], TOKEN_VALUE) do |t|
|
30
|
+
Rhail::Helper.render_json(response_structure: h)
|
31
|
+
end
|
32
|
+
|
33
|
+
when req.get? && req.path_info == '/example_one'
|
34
|
+
Rhail::Helper.token_auth(env['HTTP_AUTHORIZATION'], TOKEN_VALUE) do |t|
|
35
|
+
people_with_addresses = DB[:people].left_join(:addresses, id: :address_id).select(:name, :city, :state, :zipcode).all
|
36
|
+
Rhail::Helper.render_json(response_structure: people_with_addresses)
|
37
|
+
end
|
38
|
+
|
39
|
+
when req.post? && req.path_info == '/example_two'
|
40
|
+
Rhail::Helper.token_auth(env['HTTP_AUTHORIZATION'], TOKEN_VALUE) do |t|
|
41
|
+
params = Rhail::Helper.params_from_json_request(req)
|
42
|
+
|
43
|
+
address_id = DB[:addresses].insert(city: params['city'], state: params['state'], zipcode: params['zipcode'])
|
44
|
+
DB[:people].insert(name: params['name'], address_id: address_id)
|
45
|
+
|
46
|
+
people_with_addresses = DB[:people].left_join(:addresses, id: :address_id).select(:name, :city, :state, :zipcode).all
|
47
|
+
Rhail::Helper.render_json(response_structure: people_with_addresses)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
rescue => e
|
52
|
+
Rhail::Helper.render_json(response_structure: {status: '500', code: 'Internal Server Error', detail: e.message}, status: 500)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
run WebApi
|
58
|
+
|
59
|
+
# curl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' http://127.0.0.1:9292/
|
60
|
+
# curl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' http://127.0.0.1:9292/example_one
|
61
|
+
# curl -i -H 'Authorization: Token token="afbadb4ff8485c0adcba486b4ca90cc4"' -H "Content-Type: application/json" -X POST -d '{"city":"asd","state":"asd","zipcode":"123","name":"asd"}' http://127.0.0.1:9292/example_two
|
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
DB = Sequel.sqlite('db/database.sqlite3')
|
4
|
+
|
5
|
+
DB.run "DROP TABLE IF EXISTS addresses"
|
6
|
+
DB.run "DROP TABLE IF EXISTS people"
|
7
|
+
|
8
|
+
DB.create_table :addresses do
|
9
|
+
primary_key :id
|
10
|
+
String :street_1, :size => 255
|
11
|
+
String :street_2, :size => 255
|
12
|
+
String :city
|
13
|
+
String :state, :size => 2
|
14
|
+
String :zipcode, :size => 5
|
15
|
+
end
|
16
|
+
|
17
|
+
DB.run "INSERT INTO addresses(city, state, zipcode) VALUES('Atlanta', 'GA', '30305')"
|
18
|
+
DB.run "INSERT INTO addresses(city, state, zipcode) VALUES('Houston', 'TX', '77001')"
|
19
|
+
DB.run "INSERT INTO addresses(city, state, zipcode) VALUES('Fargo', 'ND', '58102')"
|
20
|
+
|
21
|
+
|
22
|
+
DB.create_table :people do
|
23
|
+
primary_key :id
|
24
|
+
String :name, :size => 255
|
25
|
+
Integer :address_id
|
26
|
+
end
|
27
|
+
|
28
|
+
DB.run "INSERT INTO people(name, address_id) VALUES('George', 1)"
|
29
|
+
DB.run "INSERT INTO people(name, address_id) VALUES('Thomas', 2)"
|
30
|
+
DB.run "INSERT INTO people(name, address_id) VALUES('Douglas', 3)"
|
31
|
+
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
token: 'afbadb4ff8485c0adcba486b4ca90cc4'
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
ruby-2.3.0@rhail_plain
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rhail/helper'
|
2
|
+
require 'sequel'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
use Rack::Static, :urls => ['/img', '/css'], :root => 'public' # http://www.rubydoc.info/github/rack/rack/Rack/Static
|
6
|
+
use Rack::Session::Pool
|
7
|
+
|
8
|
+
DB = Sequel.sqlite('db/database.sqlite3')
|
9
|
+
|
10
|
+
class RhailPlain
|
11
|
+
def self.call(env)
|
12
|
+
|
13
|
+
req = Rack::Request.new(env)
|
14
|
+
|
15
|
+
case
|
16
|
+
when req.get? && req.path_info == '/'
|
17
|
+
|
18
|
+
Rhail::Helper.render_html files: %w(head home foot), local_vars: {page: 'home'}
|
19
|
+
|
20
|
+
when req.get? && req.path_info == '/example_one'
|
21
|
+
|
22
|
+
Rhail::Helper.render_html files: %w(head example_one foot), local_vars: {page: 'example_one'}
|
23
|
+
|
24
|
+
when req.get? && req.path_info == '/example_two'
|
25
|
+
|
26
|
+
Rhail::Helper.render_html files: %w(head example_two foot), local_vars: {page: 'example_two'}
|
27
|
+
|
28
|
+
when req.post? && req.path_info == '/example_two' # post
|
29
|
+
|
30
|
+
Rhail::Helper.render_html files: %w(head example_two foot), local_vars: {page: 'example_two', results: req.params}
|
31
|
+
|
32
|
+
when req.get? && req.path_info == '/example_three'
|
33
|
+
|
34
|
+
req.session['csrf.token'] = SecureRandom.urlsafe_base64(32)
|
35
|
+
|
36
|
+
people_with_addresses = DB[:people].left_join(:addresses, id: :address_id).select(:name, :city, :state, :zipcode).all
|
37
|
+
|
38
|
+
Rhail::Helper.render_html files: %w(head example_three foot), local_vars: {page: 'example_three', results: people_with_addresses, csrf_token: env['rack.session']['csrf.token']}
|
39
|
+
|
40
|
+
when req.post? && req.path_info == '/example_three' # post
|
41
|
+
|
42
|
+
params = req.params
|
43
|
+
|
44
|
+
if params['csrf_token'] == req.session['csrf.token']
|
45
|
+
|
46
|
+
address_id = DB[:addresses].insert(city: params['city'], state: params['state'], zipcode: params['zipcode'])
|
47
|
+
DB[:people].insert(name: params['name'], address_id: address_id)
|
48
|
+
|
49
|
+
else
|
50
|
+
puts "CSRF check failed."
|
51
|
+
end
|
52
|
+
|
53
|
+
[302, {'Location' => '/example_three', 'Content-Type' => 'text/html'}, ['302 found']]
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
run RhailPlain
|
Binary file
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
DB = Sequel.sqlite('db/database.sqlite3')
|
4
|
+
|
5
|
+
DB.run "DROP TABLE IF EXISTS addresses"
|
6
|
+
DB.run "DROP TABLE IF EXISTS people"
|
7
|
+
|
8
|
+
DB.create_table :addresses do
|
9
|
+
primary_key :id
|
10
|
+
String :street_1, :size => 255
|
11
|
+
String :street_2, :size => 255
|
12
|
+
String :city
|
13
|
+
String :state, :size => 2
|
14
|
+
String :zipcode, :size => 5
|
15
|
+
end
|
16
|
+
|
17
|
+
DB.run "INSERT INTO addresses(city, state, zipcode) VALUES('Atlanta', 'GA', '30305')"
|
18
|
+
DB.run "INSERT INTO addresses(city, state, zipcode) VALUES('Houston', 'TX', '77001')"
|
19
|
+
DB.run "INSERT INTO addresses(city, state, zipcode) VALUES('Fargo', 'ND', '58102')"
|
20
|
+
|
21
|
+
|
22
|
+
DB.create_table :people do
|
23
|
+
primary_key :id
|
24
|
+
String :name, :size => 255
|
25
|
+
Integer :address_id
|
26
|
+
end
|
27
|
+
|
28
|
+
DB.run "INSERT INTO people(name, address_id) VALUES('George', 1)"
|
29
|
+
DB.run "INSERT INTO people(name, address_id) VALUES('Thomas', 2)"
|
30
|
+
DB.run "INSERT INTO people(name, address_id) VALUES('Douglas', 3)"
|
31
|
+
|