casual-api 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/casual-api +7 -0
- data/lib/casual.rb +94 -0
- data/lib/config.ru +34 -0
- data/lib/dsl.rb +185 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1f562aeb27eaed4448ca125d26b257e9c0cf51c6
|
4
|
+
data.tar.gz: 08a14d0ba57999138e843de5e5e567c33ffa50b0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 194ac34b57ddec0727cfe72c415cb66266b589c0477e9c8784869e9a534a089c302c825b143cc718d669f312e95995f6e46c6673fea540fd2388acf9d591a55a
|
7
|
+
data.tar.gz: eb1f02762637aeef75ed68d48786d35a36a097d8b846ddb401e17cae5334e0abe6bae542c9faa62ac88b476cb7b2061978298147cc6063f4767b066bb92c8e7f
|
data/bin/casual-api
ADDED
data/lib/casual.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require_relative './dsl.rb'
|
3
|
+
|
4
|
+
class ChildProcess
|
5
|
+
def initialize(action, parameters, session)
|
6
|
+
@file_descriptors = []
|
7
|
+
@action = action
|
8
|
+
@params = parameters
|
9
|
+
@session = session
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
obj = Action.fire @action, @params, @session
|
14
|
+
{
|
15
|
+
session: obj.session,
|
16
|
+
status_code: obj.status_code,
|
17
|
+
content_type: obj.content_type,
|
18
|
+
body: obj.body,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
# RackApp
|
25
|
+
class Casual
|
26
|
+
def initialize(prefix, file, mode)
|
27
|
+
@prefix = prefix
|
28
|
+
@file = file
|
29
|
+
@mode = mode
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
@env = env
|
34
|
+
@req = Rack::Request.new(env)
|
35
|
+
fire_action
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def fire_action
|
40
|
+
actions = parse_actions
|
41
|
+
action = detect_action actions
|
42
|
+
if action
|
43
|
+
cp = ChildProcess.new(action, parameters, session)
|
44
|
+
result = cp.execute
|
45
|
+
@req.session['casual.session'] = result[:session]
|
46
|
+
res = Rack::Response.new(){|r|
|
47
|
+
r.status = result[:status_code]
|
48
|
+
r.write result[:body]
|
49
|
+
r['Content-Type'] = map_content_type(result[:content_type])
|
50
|
+
}
|
51
|
+
res.finish
|
52
|
+
else
|
53
|
+
[404, {}, []]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def map_content_type(c)
|
58
|
+
{
|
59
|
+
txt: "text/plain;charset=utf-8",
|
60
|
+
html: "text/html;charset=utf-8",
|
61
|
+
}[c.to_sym] || c
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse_actions
|
65
|
+
if @mode == "development" || !@actions
|
66
|
+
@actions = DSL.parse @file
|
67
|
+
else
|
68
|
+
@actions
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def detect_action actions
|
73
|
+
path = request_path
|
74
|
+
_method = request_method
|
75
|
+
actions.detect{|act|
|
76
|
+
act[:method] == _method && make_path(act) == path
|
77
|
+
}
|
78
|
+
end
|
79
|
+
def make_path action
|
80
|
+
@prefix + (action[:domain] + [action[:name]]).compact.map{|p|p.to_s}.join('/')
|
81
|
+
end
|
82
|
+
def parameters
|
83
|
+
@req.params.map{|k,v| {k.to_sym => v} }.inject({}, :merge)
|
84
|
+
end
|
85
|
+
def session
|
86
|
+
@req.session['casual.session'] || {}
|
87
|
+
end
|
88
|
+
def request_method
|
89
|
+
@req.request_method.downcase.to_sym
|
90
|
+
end
|
91
|
+
def request_path
|
92
|
+
@req.path
|
93
|
+
end
|
94
|
+
end
|
data/lib/config.ru
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
require_relative './casual.rb'
|
5
|
+
|
6
|
+
config = open("config.yaml"){|f|
|
7
|
+
YAML.load f.read
|
8
|
+
}
|
9
|
+
|
10
|
+
use Rack::Session::Pool,
|
11
|
+
:key => config["session"]["key"],
|
12
|
+
:domain => config["session"]["domain"],
|
13
|
+
:path => config["session"]["path"],
|
14
|
+
:expire_after => config["session"]["expire"],
|
15
|
+
:secret => config["session"]["secret"]
|
16
|
+
|
17
|
+
(config["routes"] || {}).each{|k,v|
|
18
|
+
map(k)do
|
19
|
+
run Rack::Cascade.new [
|
20
|
+
Casual.new(k, v, config["mode"])
|
21
|
+
]
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
(config["static_web"] || {}).each{|k,v|
|
26
|
+
use Rack::Static, urls: [""],
|
27
|
+
root: v["directory"],
|
28
|
+
index: v["index"]
|
29
|
+
map(k)do
|
30
|
+
run Rack::Cascade.new [
|
31
|
+
Rack::Directory.new(v["directory"]),
|
32
|
+
]
|
33
|
+
end
|
34
|
+
}
|
data/lib/dsl.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module BodyWriter
|
5
|
+
def self.extended obj
|
6
|
+
obj.instance_variable_set(:@body, "")
|
7
|
+
end
|
8
|
+
def write str
|
9
|
+
@body << str.to_s
|
10
|
+
end
|
11
|
+
def print str
|
12
|
+
@body << str.to_s
|
13
|
+
end
|
14
|
+
def puts str
|
15
|
+
@body << str.to_s << "\n"
|
16
|
+
end
|
17
|
+
def printf *p
|
18
|
+
@body << (sprintf *p)
|
19
|
+
end
|
20
|
+
def command cmd
|
21
|
+
write `#{cmd.to_s}`
|
22
|
+
end
|
23
|
+
def body
|
24
|
+
@body
|
25
|
+
end
|
26
|
+
def body=(value)
|
27
|
+
@body = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Action
|
32
|
+
def self.extended obj
|
33
|
+
obj.instance_variable_set(:@session, {})
|
34
|
+
obj.instance_variable_set(:@content_type, nil)
|
35
|
+
obj.instance_variable_set(:@status_code, 200)
|
36
|
+
obj.instance_variable_set(:@tempfiles, [])
|
37
|
+
end
|
38
|
+
|
39
|
+
def file? file
|
40
|
+
file.is_a? Hash
|
41
|
+
end
|
42
|
+
|
43
|
+
def tempfile file
|
44
|
+
if file.is_a? Hash
|
45
|
+
file[:tempfile].path
|
46
|
+
else
|
47
|
+
f = Tempfile.open(SecureRandom.hex(4))
|
48
|
+
f.write file
|
49
|
+
f.flush
|
50
|
+
@tempfiles.push f
|
51
|
+
f.path
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def session_clear
|
56
|
+
session.clear
|
57
|
+
session = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def session; @session; end
|
61
|
+
def session=(value); @session = value; end
|
62
|
+
def content_type; @content_type; end
|
63
|
+
def tempfiles; @tempfiles; end
|
64
|
+
def status_code; @status_code; end
|
65
|
+
def content_type=(type); @content_type = type; end
|
66
|
+
def status_code=(code); @status_code = code; end
|
67
|
+
|
68
|
+
|
69
|
+
def self.fire(action, params, session)
|
70
|
+
obj = Object
|
71
|
+
obj.extend Action
|
72
|
+
obj.extend BodyWriter
|
73
|
+
obj.content_type = action[:content_type] || :txt
|
74
|
+
obj.session = session
|
75
|
+
ps = action[:action].parameters.map{|_, name|
|
76
|
+
{name => params[name]}
|
77
|
+
}.inject({}, :merge)
|
78
|
+
result = execute(obj, ps, action[:action])
|
79
|
+
unless result[:executed]
|
80
|
+
obj.status_code = 400
|
81
|
+
obj.body = "Not given enough parameter"
|
82
|
+
obj.content_type = :txt
|
83
|
+
end
|
84
|
+
obj.tempfiles.each{|f| f.close }
|
85
|
+
obj
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def self.execute(obj, ps, block)
|
90
|
+
vs = block.parameters.map{|_,n|ps[n]}.inject([[],true]){|(acc,b),v|
|
91
|
+
(b && v) ? [acc+[v],b] : [acc,false]
|
92
|
+
}[0]
|
93
|
+
if block.arity <= vs.length
|
94
|
+
result = obj.instance_exec(*vs, &block)
|
95
|
+
{ executed: true, result: result }
|
96
|
+
else
|
97
|
+
{ executed: false }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
module Annotation
|
104
|
+
def file *files
|
105
|
+
@context_stack.last[:files] ||= []
|
106
|
+
@context_stack.last[:files].push(*files)
|
107
|
+
end
|
108
|
+
def description(param1, param2=nil)
|
109
|
+
@context_stack.last[:description] ||= {}
|
110
|
+
if param2
|
111
|
+
@context_stack.last[:description][:params] ||= {}
|
112
|
+
@context_stack.last[:description][:params][param1] = param2
|
113
|
+
else
|
114
|
+
@context_stack.last[:description][:path] = param1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
def clear_annotaion
|
118
|
+
@context_stack.last.delete(:files)
|
119
|
+
@context_stack.last.delete(:description)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
module ExtExpression
|
124
|
+
def content_type type
|
125
|
+
@context_stack.last[:content_type] = type
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
module SyntaxSugar
|
130
|
+
def get(path, &block); path(:get, path, caller, &block); end
|
131
|
+
def post(path, &block); path(:post, path, caller, &block); end
|
132
|
+
|
133
|
+
def command(path, cmd)
|
134
|
+
get path do
|
135
|
+
write `#{cmd}`
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
module DSL
|
142
|
+
def self.extended obj
|
143
|
+
# add stacks and a context to an extended object.
|
144
|
+
obj.instance_variable_set(:@action_stack, [])
|
145
|
+
obj.instance_variable_set(:@domain_stack, [])
|
146
|
+
obj.instance_variable_set(:@context_stack, [{}])
|
147
|
+
obj.extend Action
|
148
|
+
obj.extend Annotation
|
149
|
+
obj.extend ExtExpression
|
150
|
+
obj.extend SyntaxSugar
|
151
|
+
end
|
152
|
+
def action_stack
|
153
|
+
@action_stack
|
154
|
+
end
|
155
|
+
|
156
|
+
def domain(*path , &block)
|
157
|
+
# annotations don't effect over domain
|
158
|
+
clear_annotaion
|
159
|
+
@domain_stack.push *path
|
160
|
+
@context_stack.push(@context_stack.last.clone)
|
161
|
+
block.call
|
162
|
+
@context_stack.pop
|
163
|
+
@domain_stack.pop path.length
|
164
|
+
# annotations don't effect over domain
|
165
|
+
clear_annotaion
|
166
|
+
end
|
167
|
+
|
168
|
+
def path(method, name, c=nil, &block)
|
169
|
+
meta = @context_stack.last.clone
|
170
|
+
meta[:method] = method
|
171
|
+
meta[:name] = name
|
172
|
+
meta[:domain] = @domain_stack.clone
|
173
|
+
meta[:action] = block
|
174
|
+
clear_annotaion # annotations effect only a path.
|
175
|
+
@action_stack.push meta
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.parse(file)
|
179
|
+
obj = Object.new
|
180
|
+
obj.extend DSL
|
181
|
+
obj.instance_eval(open(file){|f|f.read})
|
182
|
+
obj.action_stack
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: casual-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Takeshi Kojima
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thin
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.6.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.6'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.6.2
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rack
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.5'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 1.5.2
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.5'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.5.2
|
53
|
+
description: A simple web server
|
54
|
+
email: tatkeshi@gmail.com
|
55
|
+
executables:
|
56
|
+
- casual-api
|
57
|
+
extensions: []
|
58
|
+
extra_rdoc_files: []
|
59
|
+
files:
|
60
|
+
- bin/casual-api
|
61
|
+
- lib/casual.rb
|
62
|
+
- lib/config.ru
|
63
|
+
- lib/dsl.rb
|
64
|
+
homepage: https://github.com/tatkeshi/casual-api
|
65
|
+
licenses:
|
66
|
+
- MIT
|
67
|
+
metadata: {}
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options: []
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 2.2.2
|
85
|
+
signing_key:
|
86
|
+
specification_version: 4
|
87
|
+
summary: casual-api
|
88
|
+
test_files: []
|