actionframework 0.0.7
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.
- data/bin/afw +49 -0
- data/lib/actionframework.rb +329 -0
- metadata +98 -0
data/bin/afw
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optitron'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'gors'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
class GorsCLI < Optitron::CLI
|
8
|
+
desc "About ActionFramework"
|
9
|
+
def about
|
10
|
+
puts "ActionFramework (previously called GORS) is a web application framework that tries to be as flexible as sinatra and at the same time have the structure of rails.Enjoy!"
|
11
|
+
end
|
12
|
+
desc "Create ActionFramework project"
|
13
|
+
def new projectname
|
14
|
+
puts "Creating project directory and structure"
|
15
|
+
Dir.mkdir("#{projectname}")
|
16
|
+
Dir.mkdir("#{projectname}/controllers")
|
17
|
+
Dir.mkdir("#{projectname}/models")
|
18
|
+
Dir.mkdir("#{projectname}/views")
|
19
|
+
Dir.mkdir("#{projectname}/config")
|
20
|
+
Dir.mkdir("#{projectname}/initializers")
|
21
|
+
FileUtils.touch("#{projectname}/views/layout.html.erb")
|
22
|
+
FileUtils.touch("#{projectname}/Gemfile")
|
23
|
+
File.write("#{projectname}/main.rb","# Example of basic configuration\ngors = Gors::Server.new\n\nngors.autoimport\ngors.start")
|
24
|
+
File.write("#{projectname}/controllers/default_controller.rb","class DefaultController < Gors::Controller
|
25
|
+
\n def index\n \"<h1>Welcome to ActionFramework</h1><i>Great Opensource Ruby Server</i><p>Gors is loading routes from routes.json but you should write your own main.rb<br/>This is just to gettings started</p>\"\n end\nend");
|
26
|
+
File.write("#{projectname}/routes.json","{\n \"get\": [{\n \"/\": \"DefaultController#index\"\n}]\n}")
|
27
|
+
File.write("#{projectname}/config/routes.rb","Gors::Server.current.routes do\n\n get \"/\" => \"DefaultController#index\"\n\nend")
|
28
|
+
File.write("#{projectname}/config/settings.rb","Gors::Server.current.settings do |s|\n\nend")
|
29
|
+
puts "Done"
|
30
|
+
puts "Test ActionFramework by running \"gors s\""
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Start gors server"
|
34
|
+
def s
|
35
|
+
Gors::Server.init
|
36
|
+
Gors::Server.current.start
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Start gors console"
|
40
|
+
def c
|
41
|
+
Gors::Server.init
|
42
|
+
puts "Starting Gors IRB"
|
43
|
+
require 'irb'
|
44
|
+
ARGV.clear
|
45
|
+
IRB.start
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
GorsCLI.dispatch
|
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'erb'
|
3
|
+
require 'tilt'
|
4
|
+
require 'json'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
$runningserver = nil
|
8
|
+
|
9
|
+
module ActionFramework
|
10
|
+
class Server
|
11
|
+
def self.init
|
12
|
+
require 'bundler'
|
13
|
+
Bundler.require(:default)
|
14
|
+
ActionFramework::Server.current = ActionFramework::Server.new
|
15
|
+
ActionFramework::Server.current.autoimport
|
16
|
+
end
|
17
|
+
def initialize
|
18
|
+
@settings = Settings.new
|
19
|
+
@logger = Logger.new(@settings)
|
20
|
+
@routesklass = Routes.new(@logger)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.current
|
24
|
+
if($runningserver.nil?)
|
25
|
+
ActionFramework::Server.init
|
26
|
+
$runningserver
|
27
|
+
else
|
28
|
+
$runningserver
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.current=(runningserver)
|
33
|
+
$runningserver = runningserver
|
34
|
+
end
|
35
|
+
|
36
|
+
def run
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def call env
|
41
|
+
if(@settings.errorhandler != nil)
|
42
|
+
@errorhandler = Object.const_get(@settings.errorhandler).new
|
43
|
+
else
|
44
|
+
@errorhandler = ActionFramework::DefaultErrorHandler.new
|
45
|
+
end
|
46
|
+
|
47
|
+
routesinfo = @routesklass.routes(env["REQUEST_PATH"])
|
48
|
+
controller = routesinfo[0]
|
49
|
+
@logger.log controller.inspect
|
50
|
+
matcheddata = routesinfo[1]
|
51
|
+
|
52
|
+
if(controller == nil)
|
53
|
+
if(@settings.server != "thin")
|
54
|
+
response = ["<h1>404 Not Found</h1>"]
|
55
|
+
else
|
56
|
+
response = "<h1>404 Not Found</h1>"
|
57
|
+
end
|
58
|
+
return @errorhandler.call "404"
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
if(controller.include? "ActionFramework::")
|
63
|
+
ctrl = controller.split("#")
|
64
|
+
params = ctrl[1].split(":")
|
65
|
+
|
66
|
+
req = Rack::Request.new(env)
|
67
|
+
|
68
|
+
data = Model.new(req).call(params[1])
|
69
|
+
return ["200",{"Content-type" => "application/json"},[data]]
|
70
|
+
end
|
71
|
+
|
72
|
+
# Call the Controller
|
73
|
+
request = Request.new
|
74
|
+
request.request.params = Rack::Utils.parse_query(env["QUERY_STRING"])
|
75
|
+
if(matcheddata != nil)
|
76
|
+
request.request.params.merge! (matcheddata)
|
77
|
+
end
|
78
|
+
request.request.params.default = ""
|
79
|
+
|
80
|
+
infoctrl = controller.split("#")
|
81
|
+
ctrl = Object.const_get(infoctrl[0]).new(request)
|
82
|
+
|
83
|
+
response = ctrl.send(infoctrl[1])
|
84
|
+
if(@settings.server != "thin")
|
85
|
+
response = [response]
|
86
|
+
end
|
87
|
+
[ctrl.info.response.status_code,ctrl.info.response.headers,response]
|
88
|
+
end
|
89
|
+
|
90
|
+
def start
|
91
|
+
if(@settings.errorhandler != nil)
|
92
|
+
@errorhandler = Object.const_get(@settings.errorhandler).new
|
93
|
+
else
|
94
|
+
@errorhandler = ActionFramework::DefaultErrorHandler.new
|
95
|
+
end
|
96
|
+
|
97
|
+
if(@settings.daemon)
|
98
|
+
puts "Sending ActionFramework to background"
|
99
|
+
system("kill `cat running.pid`")
|
100
|
+
Process.daemon true
|
101
|
+
File.write("running.pid",Process.pid)
|
102
|
+
end
|
103
|
+
@logger.log @routesklass.inspect
|
104
|
+
Rack::Server.new({:app => self,:server => @settings.server, :Port => @settings.port}).start
|
105
|
+
end
|
106
|
+
|
107
|
+
def routes &block
|
108
|
+
@routesklass.instance_eval &block
|
109
|
+
end
|
110
|
+
|
111
|
+
def settings
|
112
|
+
yield(@settings)
|
113
|
+
end
|
114
|
+
|
115
|
+
def autoimport
|
116
|
+
Dir.glob("controllers/*").each do |file|
|
117
|
+
require './'+file
|
118
|
+
end
|
119
|
+
|
120
|
+
Dir.glob("models/*").each do |file|
|
121
|
+
require './'+file
|
122
|
+
end
|
123
|
+
|
124
|
+
require './config/routes'
|
125
|
+
require './config/settings'
|
126
|
+
|
127
|
+
Dir.glob("initializers/*").each do |file|
|
128
|
+
require './'+file
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
class Routes
|
135
|
+
NAME_PATTERN = /:(\S+)/
|
136
|
+
|
137
|
+
attr_accessor :routes
|
138
|
+
attr_accessor :models
|
139
|
+
attr_accessor :posts
|
140
|
+
|
141
|
+
def initialize logger
|
142
|
+
@routes = {}
|
143
|
+
@models = {}
|
144
|
+
@logger = logger
|
145
|
+
@routespost = {}
|
146
|
+
end
|
147
|
+
|
148
|
+
def get hash
|
149
|
+
@routes[pattern_for(hash.keys.first.to_s)] = hash[hash.keys.first.to_s]
|
150
|
+
@logger.log "Adding route GET "+hash.keys.first.to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
def json
|
154
|
+
# TODO zorg evoor dat routes ook van routes.json gehaald kunnen worden (structuur zie generator in de CLI)
|
155
|
+
end
|
156
|
+
|
157
|
+
def post hash
|
158
|
+
@routespost[hash.keys.first.to_s] = hash[hash.keys.first.to_s]
|
159
|
+
@logger.log "Adding route POST "+hash.keys.first.to_s
|
160
|
+
end
|
161
|
+
|
162
|
+
def post
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
def model hash
|
167
|
+
@routes["/api/"+hash.keys.first.to_s] = "ActionFramework::Model#call:"+hash[hash.keys.first.to_s];
|
168
|
+
puts "Adding model with path "+hash.keys.first.to_s
|
169
|
+
end
|
170
|
+
|
171
|
+
def routes(path)
|
172
|
+
hash = {}
|
173
|
+
controller = nil
|
174
|
+
@routes.each do |route,controller|
|
175
|
+
if(matched = route.match path)
|
176
|
+
matched.names.each do |name|
|
177
|
+
hash[name] = matched[name]
|
178
|
+
end
|
179
|
+
|
180
|
+
return [controller,hash]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
# Logic from github.com/alisnic/nyny
|
185
|
+
def pattern_for signature
|
186
|
+
if(signature.class == Regexp)
|
187
|
+
return signature
|
188
|
+
end
|
189
|
+
build_regex(signature.start_with?('/') ? signature : "/#{signature}")
|
190
|
+
end
|
191
|
+
|
192
|
+
def build_regex signature
|
193
|
+
return %r(^#{signature}$) unless signature.include?(':')
|
194
|
+
|
195
|
+
groups = signature.split('/').map do |part|
|
196
|
+
next part if part.empty?
|
197
|
+
next part unless part.start_with? ':'
|
198
|
+
name = NAME_PATTERN.match(part)[1]
|
199
|
+
%Q{(?<#{name}>\\S+)}
|
200
|
+
end.select {|s| !s.empty? }.join('\/')
|
201
|
+
|
202
|
+
%r(^\/#{groups}$)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
class Controller
|
206
|
+
attr_accessor :info
|
207
|
+
|
208
|
+
def initialize(context)
|
209
|
+
@info = context
|
210
|
+
if(self.respond_to? "before")
|
211
|
+
self.before
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def erb template
|
216
|
+
renderer = Tilt::ERBTemplate.new("views/layout.html.erb")
|
217
|
+
output = renderer.render(self){ Tilt::ERBTemplate.new("views/"+template.to_s+".html.erb").render(self) }
|
218
|
+
return output
|
219
|
+
end
|
220
|
+
|
221
|
+
def params
|
222
|
+
@info.request.params
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
class Request
|
227
|
+
attr_accessor :response
|
228
|
+
attr_accessor :request
|
229
|
+
|
230
|
+
def initialize
|
231
|
+
@response = OpenStruct.new ({:headers => {}, :status_code => "200"})
|
232
|
+
@request = OpenStruct.new ({:ip => "",:user_agent => "",:headers => {},:params => {}})
|
233
|
+
end
|
234
|
+
|
235
|
+
def info
|
236
|
+
return @info
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
class Model
|
241
|
+
def initialize req
|
242
|
+
@req = req
|
243
|
+
end
|
244
|
+
|
245
|
+
def call modelname
|
246
|
+
case @req.request_method
|
247
|
+
when "GET"
|
248
|
+
if(Object.const_get(modelname.capitalize).respond_to? "append")
|
249
|
+
model = Object.const_get(modelname.capitalize).all.send(Object.const_get(modelname.capitalize).append)
|
250
|
+
else
|
251
|
+
model = Object.const_get(modelname.capitalize).all
|
252
|
+
end
|
253
|
+
model.to_json
|
254
|
+
when "POST"
|
255
|
+
if(Object.const_get(modelname.capitalize).respond_to? "append")
|
256
|
+
model = Object.const_get(modelname.capitalize).create(JSON.parse(@req.body.string))
|
257
|
+
else
|
258
|
+
model = Object.const_get(modelname.capitalize).create(JSON.parse(@req.body.string))
|
259
|
+
end
|
260
|
+
model.to_json
|
261
|
+
else
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
class Settings
|
269
|
+
attr_accessor :port
|
270
|
+
attr_accessor :server
|
271
|
+
attr_accessor :verbose
|
272
|
+
attr_accessor :daemon
|
273
|
+
attr_accessor :errorhandler
|
274
|
+
|
275
|
+
def initialize
|
276
|
+
@port = 8080
|
277
|
+
@server = "puma"
|
278
|
+
@verbose = true
|
279
|
+
@daemon = false
|
280
|
+
@errorhandler = nil
|
281
|
+
end
|
282
|
+
end
|
283
|
+
class Logger
|
284
|
+
|
285
|
+
def initialize(settings)
|
286
|
+
@settings = settings
|
287
|
+
end
|
288
|
+
|
289
|
+
def log msg
|
290
|
+
if(@settings.daemon)
|
291
|
+
return
|
292
|
+
end
|
293
|
+
if(@settings.verbose)
|
294
|
+
puts msg
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|
299
|
+
|
300
|
+
class ErrorHelper
|
301
|
+
def call(errortype)
|
302
|
+
if(self.respond_to? "error_"+errortype)
|
303
|
+
[errortype, {},[self.send("error_"+errortype)]]
|
304
|
+
else
|
305
|
+
ActionFramework::DefaultErrorHandler.new.send("error_"+errortype)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
class DefaultErrorHandler < ActionFramework::ErrorHelper
|
311
|
+
def error_404
|
312
|
+
"<h1>404 Not Found</h1>"
|
313
|
+
end
|
314
|
+
def error_500
|
315
|
+
"<h1>500 Internal server error</h1>"
|
316
|
+
end
|
317
|
+
def error_403
|
318
|
+
"<h1>403 Forbidden</h1>"
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
end
|
323
|
+
|
324
|
+
at_exit do
|
325
|
+
puts "Exiting..."
|
326
|
+
if(File.exists? "running.pid")
|
327
|
+
File.delete("running.pid")
|
328
|
+
end
|
329
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: actionframework
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.7
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bram Vandenbogaerde
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: tilt
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rack
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: A web framework built on top of Rack, it has the simplicity of sinatra
|
63
|
+
and the structure of rails
|
64
|
+
email: bram.vandenbogaerde@gmail.com
|
65
|
+
executables:
|
66
|
+
- afw
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- lib/actionframework.rb
|
71
|
+
- bin/afw
|
72
|
+
homepage: http://rubygems.org/gems/actionframework
|
73
|
+
licenses:
|
74
|
+
- MIT
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 1.8.23
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: A web framework built on top of Rack
|
97
|
+
test_files: []
|
98
|
+
has_rdoc:
|