apex 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +146 -3
- data/lib/apex/request.rb +26 -2
- data/lib/apex/server.rb +36 -9
- data/lib/apex/version.rb +1 -1
- data/spec/unit/request_get_spec.rb +89 -0
- metadata +5 -46
- data/spec/main_spec.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 67fa319a7be6930bdb57ed1225ec9a85ac34e58c
|
4
|
+
data.tar.gz: c2145e383269e800c13a603ad9b4169684cf5a45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 556f7b5edc3cadf511de37be894a3f575d2e9a63efa9294b54e0f8989d7c8c372682890110779e63e252b7c06f04bc6cf9a44335de6a9ab9717a8f22b89de236
|
7
|
+
data.tar.gz: 494d533192637b923e3e62f58c735c1b64214402467c38cc208cbde1325888c691af1be6ed42e5bb0265a250773d8abdc149eae28eb12187e6f5976c1b4ab247
|
data/README.md
CHANGED
@@ -4,14 +4,14 @@ Apex is a RubyMotion web framework for OS X. It uses
|
|
4
4
|
GCDWebServer under the hood and provides a Sinatra-like
|
5
5
|
router and DSL.
|
6
6
|
|
7
|
-
Apex is currently experimental and in development.
|
8
|
-
|
7
|
+
Apex is currently experimental and in development. Let me know
|
8
|
+
what you think [on Twitter](http://twitter.com/jamonholmgren).
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
12
12
|
```ruby
|
13
13
|
# In Gemfile:
|
14
|
-
gem 'apex'
|
14
|
+
gem 'apex', :git => 'https://github.com/clearsightstudio/apex.git
|
15
15
|
|
16
16
|
# In Terminal:
|
17
17
|
bundle install
|
@@ -20,6 +20,8 @@ rake pod:install
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
+
### Using your AppDelegate
|
24
|
+
|
23
25
|
```ruby
|
24
26
|
class AppDelegate < Apex::Server
|
25
27
|
port 8080 # defaults to 8080
|
@@ -50,6 +52,147 @@ class AppDelegate < Apex::Server
|
|
50
52
|
end
|
51
53
|
```
|
52
54
|
|
55
|
+
#### Alternate Railsy Syntax
|
56
|
+
|
57
|
+
This is under consideration.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
class AppDelegate < Apex::Server
|
61
|
+
def routes
|
62
|
+
get "/", controller: HomeController, action: :home
|
63
|
+
get "/about", controller: AboutController, action: :about
|
64
|
+
get "/about/me", controller: AboutController, action: :me
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class HomeController < Apex::Controller
|
69
|
+
def home
|
70
|
+
render :home, layout: DefaultLayout
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class AboutController < Apex::Controller
|
75
|
+
layout AboutLayout
|
76
|
+
|
77
|
+
def about
|
78
|
+
render :about
|
79
|
+
end
|
80
|
+
|
81
|
+
def me
|
82
|
+
render :me, name: "Jamon"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def AboutView < Apex::View
|
87
|
+
def about
|
88
|
+
"<h1>About</h1>"
|
89
|
+
end
|
90
|
+
|
91
|
+
def me(args={})
|
92
|
+
"<h1>Me #{args[:name]}</h1>"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def AboutLayout < Apex::Layout
|
97
|
+
def render
|
98
|
+
"<html>" +
|
99
|
+
"<head>" +
|
100
|
+
"<title>#{title}</title>" +
|
101
|
+
"</head>" +
|
102
|
+
"<body>#{content}</body>" +
|
103
|
+
"</html>"
|
104
|
+
end
|
105
|
+
|
106
|
+
# Potentially, a Ruby DSL for HTML:
|
107
|
+
def render
|
108
|
+
html do
|
109
|
+
head do
|
110
|
+
title "My title"
|
111
|
+
end
|
112
|
+
body do
|
113
|
+
content_for :body
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
### Standalone class
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
|
124
|
+
class WebServer < Apex::Server
|
125
|
+
port 8080 # defaults to 8080
|
126
|
+
|
127
|
+
layout do
|
128
|
+
"<html>" +
|
129
|
+
"<head><title>Apex</title></head>" +
|
130
|
+
"<body>" +
|
131
|
+
content +
|
132
|
+
"</body>" +
|
133
|
+
"</html>"
|
134
|
+
end
|
135
|
+
|
136
|
+
get "/" do |r|
|
137
|
+
"<h1>Apex is running. Response: #{r}</h1>" +
|
138
|
+
"<p><a href='/about'>About Apex</a></p>"
|
139
|
+
end
|
140
|
+
|
141
|
+
get "/about" do |r|
|
142
|
+
"<h1>About Apex</h1>" +
|
143
|
+
"<p><a href='/'>Home</a></p>"
|
144
|
+
end
|
145
|
+
|
146
|
+
post "/some_post" do |request|
|
147
|
+
request.headers["User-Agent"]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
152
|
+
Then in your AppDelegate (or wherever you'd like to start your server):
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
@server = WebServer.new
|
156
|
+
@server.start_server
|
157
|
+
```
|
158
|
+
|
159
|
+
## JSON
|
160
|
+
|
161
|
+
You can also return json. You can specify the response_type, like so:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
get "/current_user", response_type: :json do |request|
|
165
|
+
{
|
166
|
+
name: 'Todd',
|
167
|
+
age: 21
|
168
|
+
}
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
Or it will auto-detect the repsonse type if you return a hash instead of a string:
|
173
|
+
```ruby
|
174
|
+
get "/current_user" do |request|
|
175
|
+
{
|
176
|
+
name: 'Todd',
|
177
|
+
age: 21
|
178
|
+
}
|
179
|
+
end
|
180
|
+
```
|
181
|
+
|
182
|
+
## Benchmarking
|
183
|
+
|
184
|
+
Somewhat useless (but still fun) benchmarking against a minimal Node.js/Express app
|
185
|
+
shows Apex serving requests about 1.4x as fast as Node.
|
186
|
+
|
187
|
+
```sh-session
|
188
|
+
# Node.js / express.js app found in ./benchmarks/node/app.js
|
189
|
+
$ ab -r -n 10000 -c 6 -r http://192.168.1.246:8081/benchmark | grep "Requests per second"
|
190
|
+
Requests per second: 2789.32 [#/sec] (mean)
|
191
|
+
# Apex server found in ./app/app_delegate.rb
|
192
|
+
$ ab -r -n 10000 -c 6 -r http://192.168.1.246:8080/benchmark | grep "Requests per second"
|
193
|
+
Requests per second: 3862.26 [#/sec] (mean)
|
194
|
+
```
|
195
|
+
|
53
196
|
## Contributing
|
54
197
|
|
55
198
|
1. Fork it
|
data/lib/apex/request.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
module Apex
|
2
2
|
class Request
|
3
|
+
HEADER_PARAM = /\s*[\w.]+=(?:[\w.]+|"(?:[^"\\]|\\.)*")?\s*/
|
4
|
+
HEADER_VALUE_WITH_PARAMS = /(?:(?:\w+|\*)\/(?:\w+(?:\.|\-|\+)?|\*)*)\s*(?:;#{HEADER_PARAM})*/
|
5
|
+
|
3
6
|
attr_accessor :raw
|
4
7
|
|
5
8
|
def initialize(raw)
|
@@ -26,6 +29,7 @@ module Apex
|
|
26
29
|
def query
|
27
30
|
raw.query
|
28
31
|
end
|
32
|
+
alias_method :params, :query
|
29
33
|
|
30
34
|
def path
|
31
35
|
raw.path
|
@@ -35,8 +39,28 @@ module Apex
|
|
35
39
|
raw.URL
|
36
40
|
end
|
37
41
|
|
38
|
-
def
|
39
|
-
|
42
|
+
def method
|
43
|
+
raw.method
|
44
|
+
end
|
45
|
+
|
46
|
+
def get?
|
47
|
+
method == "GET"
|
48
|
+
end
|
49
|
+
|
50
|
+
def post?
|
51
|
+
method == "POST"
|
52
|
+
end
|
53
|
+
|
54
|
+
def put?
|
55
|
+
method == "PUT"
|
56
|
+
end
|
57
|
+
|
58
|
+
def patch?
|
59
|
+
method == "PATCH"
|
60
|
+
end
|
61
|
+
|
62
|
+
def delete?
|
63
|
+
method == "DELETE"
|
40
64
|
end
|
41
65
|
|
42
66
|
end
|
data/lib/apex/server.rb
CHANGED
@@ -3,6 +3,11 @@ module Apex
|
|
3
3
|
include DelegateInterface
|
4
4
|
|
5
5
|
def on_launch
|
6
|
+
start_server
|
7
|
+
end
|
8
|
+
|
9
|
+
def start_server
|
10
|
+
return true if RUBYMOTION_ENV == "test"
|
6
11
|
add_static_handler
|
7
12
|
add_app_handlers
|
8
13
|
start
|
@@ -27,14 +32,36 @@ module Apex
|
|
27
32
|
processBlock: -> (raw_request) {
|
28
33
|
layout = false
|
29
34
|
request = Request.new(raw_request)
|
30
|
-
if
|
35
|
+
if (routes_verb = self.routes[verb]) &&
|
36
|
+
(request_path = routes_verb[request.path]) &&
|
37
|
+
(response_block = request_path[:handler])
|
38
|
+
|
31
39
|
request_args = [request].first(response_block.arity)
|
32
40
|
response = response_block.call(*request_args)
|
33
|
-
layout =
|
41
|
+
layout = request_path[:layout]
|
42
|
+
|
43
|
+
unless response_type = request_path[:response_type]
|
44
|
+
case response # Auto detect
|
45
|
+
when Hash
|
46
|
+
response_type = :json
|
47
|
+
else
|
48
|
+
response_type = :html
|
49
|
+
# TODO, do the the rest of the types
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
case response_type
|
54
|
+
when :html
|
55
|
+
GCDWebServerDataResponse.responseWithHTML(apply_layout(response, layout))
|
56
|
+
when :json
|
57
|
+
GCDWebServerDataResponse.responseWithJSONObject(apply_layout(response, layout))
|
58
|
+
# TODO, do the the rest of the types
|
59
|
+
end
|
60
|
+
|
34
61
|
else
|
35
62
|
response = "<h1>404 not found</h1>"
|
63
|
+
GCDWebServerDataResponse.responseWithHTML(apply_layout(response, layout))
|
36
64
|
end
|
37
|
-
GCDWebServerDataResponse.responseWithHTML apply_layout(response, layout)
|
38
65
|
}
|
39
66
|
)
|
40
67
|
end
|
@@ -50,29 +77,29 @@ module Apex
|
|
50
77
|
end
|
51
78
|
|
52
79
|
def start
|
53
|
-
server.
|
80
|
+
server.startWithPort self.class.port, bonjourName: nil
|
54
81
|
end
|
55
82
|
|
56
83
|
# Class methods *************************
|
57
84
|
|
58
85
|
def self.get(path, args={}, &block)
|
59
|
-
routes[:get][path] = { handler: block, layout: args[:layout] }
|
86
|
+
routes[:get][path] = { handler: block, layout: args[:layout], response_type: args[:response_type] }
|
60
87
|
end
|
61
88
|
|
62
89
|
def self.post(path, args={}, &block)
|
63
|
-
routes[:post][path] = { handler: block, layout: args[:layout] }
|
90
|
+
routes[:post][path] = { handler: block, layout: args[:layout], response_type: args[:response_type] }
|
64
91
|
end
|
65
92
|
|
66
93
|
def self.put(path, args={}, &block)
|
67
|
-
routes[:put][path] = { handler: block, layout: args[:layout] }
|
94
|
+
routes[:put][path] = { handler: block, layout: args[:layout], response_type: args[:response_type] }
|
68
95
|
end
|
69
96
|
|
70
97
|
def self.patch(path, args={}, &block)
|
71
|
-
routes[:patch][path] = { handler: block, layout: args[:layout] }
|
98
|
+
routes[:patch][path] = { handler: block, layout: args[:layout], response_type: args[:response_type] }
|
72
99
|
end
|
73
100
|
|
74
101
|
def self.delete(path, args={}, &block)
|
75
|
-
routes[:delete][path] = { handler: block, layout: args[:layout] }
|
102
|
+
routes[:delete][path] = { handler: block, layout: args[:layout], response_type: args[:response_type] }
|
76
103
|
end
|
77
104
|
|
78
105
|
def self.routes
|
data/lib/apex/version.rb
CHANGED
@@ -0,0 +1,89 @@
|
|
1
|
+
describe "Apex::Request GET request" do
|
2
|
+
|
3
|
+
def headers
|
4
|
+
{
|
5
|
+
"Accept" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
6
|
+
"Accept-Encoding" => "gzip,deflate,sdch",
|
7
|
+
"Cookie" => "user_id=USER_ID; __atuvc=0%7C17",
|
8
|
+
"Host" => "localhost:8080",
|
9
|
+
"User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
|
10
|
+
"Accept-Language" => "en-US,en;q=0.8,sk;q=0.6,es;q=0.4",
|
11
|
+
"Cache-Control" => "max-age=0",
|
12
|
+
"Connection" => "keep-alive"
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def query
|
17
|
+
{ "test" => "tested" }
|
18
|
+
end
|
19
|
+
|
20
|
+
def raw_request
|
21
|
+
@raw_request ||= GCDWebServerRequest.alloc.initWithMethod("GET", url: "http://localhost:8080", headers: headers, path:"/benchmark", query: query)
|
22
|
+
end
|
23
|
+
|
24
|
+
def request
|
25
|
+
@request ||= Apex::Request.new(raw_request)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "#raw" do
|
29
|
+
request.raw.should == raw_request
|
30
|
+
end
|
31
|
+
|
32
|
+
it "#body?" do
|
33
|
+
request.body?.should.be.false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "#content_type" do
|
37
|
+
request.content_type.should.be.nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "#headers" do
|
41
|
+
request.headers.should == headers
|
42
|
+
end
|
43
|
+
|
44
|
+
it "#content_length" do
|
45
|
+
request.content_length.should == 9223372036854775807
|
46
|
+
end
|
47
|
+
|
48
|
+
it "#query" do
|
49
|
+
request.query.should == query
|
50
|
+
end
|
51
|
+
|
52
|
+
it "#path" do
|
53
|
+
request.path.should == "/benchmark"
|
54
|
+
end
|
55
|
+
|
56
|
+
it "#url" do
|
57
|
+
request.url.should == "http://localhost:8080"
|
58
|
+
end
|
59
|
+
|
60
|
+
it "#params" do
|
61
|
+
request.query.should == query
|
62
|
+
request.params.should == query
|
63
|
+
end
|
64
|
+
|
65
|
+
it "#method" do
|
66
|
+
request.method.should == "GET"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "#get?" do
|
70
|
+
request.get?.should.be.true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "#post?" do
|
74
|
+
request.post?.should.be.false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "#put?" do
|
78
|
+
request.put?.should.be.false
|
79
|
+
end
|
80
|
+
|
81
|
+
it "#patch?" do
|
82
|
+
request.patch?.should.be.false
|
83
|
+
end
|
84
|
+
|
85
|
+
it "#delete?" do
|
86
|
+
request.delete?.should.be.false
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamon Holmgren
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: motion-cocoapods
|
@@ -24,48 +24,6 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.5.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: webstub
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '1.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '1.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: motion-stump
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.3'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.3'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: motion-redgreen
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0.1'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0.1'
|
69
27
|
- !ruby/object:Gem::Dependency
|
70
28
|
name: rake
|
71
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -95,7 +53,7 @@ files:
|
|
95
53
|
- lib/apex/response.rb
|
96
54
|
- lib/apex/server.rb
|
97
55
|
- lib/apex/version.rb
|
98
|
-
- spec/
|
56
|
+
- spec/unit/request_get_spec.rb
|
99
57
|
homepage: https://github.com/clearsightstudio/apex
|
100
58
|
licenses:
|
101
59
|
- MIT
|
@@ -122,4 +80,5 @@ specification_version: 4
|
|
122
80
|
summary: Apex is a RubyMotion web framework for OS X. It uses GCDWebServer under the
|
123
81
|
hood and provides a Sinatra-like router and DSL.
|
124
82
|
test_files:
|
125
|
-
- spec/
|
83
|
+
- spec/unit/request_get_spec.rb
|
84
|
+
has_rdoc:
|