tanuki 0.1.3
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/LICENSE +19 -0
- data/README.rdoc +19 -0
- data/app/tanuki/controller/controller.rb +3 -0
- data/app/tanuki/controller/default.thtml +5 -0
- data/app/tanuki/controller/index.thtml +1 -0
- data/app/tanuki/controller/link.thtml +7 -0
- data/app/tanuki/object/object.rb +3 -0
- data/app/tanuki/page/missing/default.thtml +2 -0
- data/app/tanuki/page/missing/missing.rb +9 -0
- data/app/user/page/index/default.thtml +121 -0
- data/app/user/page/index/index.rb +2 -0
- data/bin/tanuki +55 -0
- data/lib/tanuki/application.rb +111 -0
- data/lib/tanuki/argument/base.rb +27 -0
- data/lib/tanuki/argument/integer.rb +15 -0
- data/lib/tanuki/argument/integer_range.rb +22 -0
- data/lib/tanuki/argument/string.rb +15 -0
- data/lib/tanuki/argument.rb +41 -0
- data/lib/tanuki/configurator.rb +93 -0
- data/lib/tanuki/context.rb +36 -0
- data/lib/tanuki/controller_behavior.rb +324 -0
- data/lib/tanuki/i18n.rb +33 -0
- data/lib/tanuki/launcher.rb +20 -0
- data/lib/tanuki/loader.rb +131 -0
- data/lib/tanuki/module_extensions.rb +27 -0
- data/lib/tanuki/object_behavior.rb +32 -0
- data/lib/tanuki/template_compiler.rb +145 -0
- data/lib/tanuki/version.rb +6 -0
- data/lib/tanuki.rb +22 -0
- data/schema/tanuki/l10n/en/controller.yml +13 -0
- data/schema/tanuki/l10n/en/page.yml +31 -0
- data/schema/tanuki/l10n/ru/controller.yml +13 -0
- data/schema/tanuki/l10n/ru/page.yml +31 -0
- data/schema/tanuki/models/controller.yml +18 -0
- data/schema/tanuki/models/page.yml +48 -0
- metadata +168 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Anatoly Ressin, Dimitry Solovyov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= Tanuki
|
2
|
+
|
3
|
+
Tanuki is a web application framework inspired by the MVVM pattern with focus
|
4
|
+
on DRY code and extensibility. Tanuki tries to keep its looks close to
|
5
|
+
idiomatic Ruby, so you would feel at home.
|
6
|
+
|
7
|
+
== Quick Start
|
8
|
+
|
9
|
+
Fire up the terminal and type:
|
10
|
+
|
11
|
+
$ gem install tanuki
|
12
|
+
$ tanuki create test
|
13
|
+
$ ruby test/main.rb
|
14
|
+
|
15
|
+
View the result at: http://localhost:3000
|
16
|
+
|
17
|
+
== Resources
|
18
|
+
|
19
|
+
{Home Page}[http://bitbucket.org/dimituri/tanuki]
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= self.class %> is under construction
|
@@ -0,0 +1,121 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title>Welcome to Tanuki</title>
|
6
|
+
<style type="text/css" media="screen">
|
7
|
+
body {
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
font-family: 'Trebuchet MS', Helvetica, sans-serif;
|
11
|
+
font-size: 11pt;
|
12
|
+
background-color: #f5f4ef;
|
13
|
+
color: #000305;
|
14
|
+
}
|
15
|
+
|
16
|
+
#header {
|
17
|
+
margin: 0 auto;
|
18
|
+
width: 540px;
|
19
|
+
}
|
20
|
+
|
21
|
+
div.top {
|
22
|
+
background-color: #fff;
|
23
|
+
margin: 0 auto 20px auto;
|
24
|
+
padding: 15px 20px 0 20px;
|
25
|
+
width: 540px;
|
26
|
+
border-radius: 10px;
|
27
|
+
-moz-border-radius: 10px;
|
28
|
+
-webkit-border-radius: 10px;
|
29
|
+
}
|
30
|
+
|
31
|
+
h1 {
|
32
|
+
margin: 20px 0;
|
33
|
+
padding: 0;
|
34
|
+
font-size: 22pt;
|
35
|
+
}
|
36
|
+
|
37
|
+
h1 em {
|
38
|
+
display: block;
|
39
|
+
font-style: normal;
|
40
|
+
font-size: 12pt;
|
41
|
+
color: #777a7c;
|
42
|
+
}
|
43
|
+
|
44
|
+
h2 {
|
45
|
+
margin: 0 0 15px 0;
|
46
|
+
padding: 0;
|
47
|
+
font-size: 16pt;
|
48
|
+
}
|
49
|
+
|
50
|
+
#next ol {
|
51
|
+
margin: 0;
|
52
|
+
padding-bottom: 25px;
|
53
|
+
}
|
54
|
+
|
55
|
+
#next li {
|
56
|
+
margin: 0;
|
57
|
+
padding: 0;
|
58
|
+
font-size: 16pt;
|
59
|
+
font-weight: bold;
|
60
|
+
color: #c74451;
|
61
|
+
}
|
62
|
+
|
63
|
+
#next li span {
|
64
|
+
font-size: 11pt;
|
65
|
+
font-weight: normal;
|
66
|
+
color: #000305;
|
67
|
+
}
|
68
|
+
|
69
|
+
code {
|
70
|
+
font-family: 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', Menlo, Monaco, Consolas, 'Courier New', monospace;
|
71
|
+
font-size: 10pt;
|
72
|
+
background-color: #fffccc;
|
73
|
+
padding: 1px 3px;
|
74
|
+
}
|
75
|
+
|
76
|
+
#env div {
|
77
|
+
padding-bottom: 25px;
|
78
|
+
}
|
79
|
+
|
80
|
+
#env table {
|
81
|
+
margin: 0;
|
82
|
+
padding: 0;
|
83
|
+
border-collapse: collapse;
|
84
|
+
color: #444;
|
85
|
+
}
|
86
|
+
|
87
|
+
#env table th {
|
88
|
+
text-align: left;
|
89
|
+
}
|
90
|
+
|
91
|
+
#env table td {
|
92
|
+
padding-left: 20px;
|
93
|
+
font-size: 10pt;
|
94
|
+
}
|
95
|
+
</style>
|
96
|
+
</head>
|
97
|
+
<body>
|
98
|
+
<div id="header">
|
99
|
+
<h1>It Works! <em>You have been granted one Tanuki.</em></h1>
|
100
|
+
</div>
|
101
|
+
<div class="top" id="next">
|
102
|
+
<h2>What to do next</h2>
|
103
|
+
<div>
|
104
|
+
<p>Here's a few suggestions to get things going:</p>
|
105
|
+
<ol>
|
106
|
+
<li><span>To use a database, describe its contents in <code>schema/user</code>.</span></li>
|
107
|
+
<li><span>To generate models from your schema, type <code>rake tanuki:models</code>.</span></li>
|
108
|
+
<li><span>To add or edit controllers, navigate to <code>app/user</code>.</span></li>
|
109
|
+
</ol>
|
110
|
+
</div>
|
111
|
+
</div>
|
112
|
+
<div class="top" id="env">
|
113
|
+
<h2>Your setup</h2>
|
114
|
+
<div><table>
|
115
|
+
<tr><th>Ruby</th><td><%= RUBY_VERSION %> (<%= RUBY_RELEASE_DATE %>) [<%= RUBY_PLATFORM %>]</td></tr>
|
116
|
+
<tr><th>Rack</th><td><%= Rack.version %> (on <%= ctx.env['SERVER_SOFTWARE'] %>)</td></tr>
|
117
|
+
<tr><th>Tanuki</th><td><%= Tanuki::VERSION %></td></tr>
|
118
|
+
</table></div>
|
119
|
+
</div>
|
120
|
+
</body>
|
121
|
+
</html>
|
data/bin/tanuki
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
module Tanuki
|
3
|
+
class << self
|
4
|
+
def create(name)
|
5
|
+
require 'fileutils'
|
6
|
+
version
|
7
|
+
puts "\n creating #{name = name.downcase}"
|
8
|
+
FileUtils.mkdir name
|
9
|
+
puts " copying app/tanuki"
|
10
|
+
FileUtils.mkdir_p File.join(name, 'app', 'tanuki')
|
11
|
+
FileUtils.cp_r File.expand_path(File.join('..', '..', 'app', 'tanuki'), __FILE__), File.join(name, 'app')
|
12
|
+
puts " copying app/user"
|
13
|
+
FileUtils.mkdir_p File.join(name, 'app', 'user')
|
14
|
+
FileUtils.cp_r File.expand_path(File.join('..', '..', 'app', 'user'), __FILE__), File.join(name, 'app')
|
15
|
+
puts " creating cache"
|
16
|
+
FileUtils.mkdir(File.join(name, 'cache'))
|
17
|
+
puts " copying schema"
|
18
|
+
FileUtils.mkdir(File.join(name, 'schema'))
|
19
|
+
FileUtils.cp_r File.expand_path(File.join('..', '..', 'schema'), __FILE__), name
|
20
|
+
puts " writing main"
|
21
|
+
File.open(File.join(name, 'main.rb'), 'w') {|file| file.write "require 'tanuki'\nTanuki.development_application" }
|
22
|
+
end
|
23
|
+
|
24
|
+
def help
|
25
|
+
version
|
26
|
+
puts "\nbasic commands:\n"
|
27
|
+
{
|
28
|
+
'help' => 'show help (this text)',
|
29
|
+
'create' => 'create a new app with the given name',
|
30
|
+
'version' => 'show framework version'
|
31
|
+
}.each_pair {|k, v| puts ' %-7s %s' % [k, v] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def init
|
35
|
+
case ARGV[0]
|
36
|
+
when /\Ac(r(e(a(te?)?)?)?)?\Z/, '--create', '-c' then create(ARGV[1])
|
37
|
+
when /\Ah(e(lp?)?)?\Z/, '--help', '-h', nil then help
|
38
|
+
when /\Av(e(r(s(i(on?)?)?)?)?)?\Z/, '--version', '-v' then version
|
39
|
+
else create(ARGV[0])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def version
|
44
|
+
begin
|
45
|
+
require 'tanuki/version'
|
46
|
+
rescue LoadError
|
47
|
+
$:.unshift File.expand_path(File.join('..', '..', 'lib'), __FILE__)
|
48
|
+
require 'tanuki/version'
|
49
|
+
end
|
50
|
+
puts "Tanuki version #{VERSION}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Tanuki.init if File.basename(__FILE__) == File.basename($0)
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Tanuki
|
2
|
+
|
3
|
+
# Tanuki::Application is the starting point for all framework applications.
|
4
|
+
# It contains core application functionality like configuration, request handling and view management.
|
5
|
+
class Application
|
6
|
+
|
7
|
+
@context = Loader.context = Context.new
|
8
|
+
@rack_middleware = {}
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
# Removes a given middleware from the Rack middleware pipeline
|
13
|
+
def discard(middleware)
|
14
|
+
@rack_middleware.delete(middleware)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Runs the application with current settings
|
18
|
+
def run
|
19
|
+
rack_builder = Rack::Builder.new
|
20
|
+
@rack_middleware.each {|middleware, params| rack_builder.use(middleware, *params[0], ¶ms[1]) }
|
21
|
+
rack_builder.run(rack_app)
|
22
|
+
srv = available_server
|
23
|
+
puts "A wild Tanuki appears!", "You used #{srv.name.gsub(/.*::/, '')} at #{@context.host}:#{@context.port}."
|
24
|
+
srv.run rack_builder.to_app, :Host => @context.host, :Port => @context.port
|
25
|
+
end
|
26
|
+
|
27
|
+
# Sets an option to value in the current context.
|
28
|
+
def set(option, value)
|
29
|
+
@context.send("#{option}=".to_sym, value)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Adds a given middleware to the Rack middleware pipeline
|
33
|
+
def use(middleware, *args, &block)
|
34
|
+
@rack_middleware[middleware] = [args, block]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Adds a template visitor block. This block must return a Proc.
|
38
|
+
#
|
39
|
+
# visitor :escape do
|
40
|
+
# s = ''
|
41
|
+
# proc {|out| s << CGI.escapeHTML(out.to_s) }
|
42
|
+
# end
|
43
|
+
# visitor :printf do |format|
|
44
|
+
# s = ''
|
45
|
+
# proc {|out| s << format % out.to_s }
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# It can later be used in a template via the visitor syntax.
|
49
|
+
#
|
50
|
+
# <%_escape escaped_view %>
|
51
|
+
# <%_printf('<div>%s</div>') formatted_view %>
|
52
|
+
def visitor(sym, &block)
|
53
|
+
ObjectBehavior.instance_eval { define_method "#{sym}_visitor".to_sym, &block }
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Returns the first available server from a server list in the current context.
|
59
|
+
def available_server
|
60
|
+
@context.server.each do |server_name|
|
61
|
+
begin
|
62
|
+
return Rack::Handler.get(server_name.downcase)
|
63
|
+
rescue LoadError
|
64
|
+
rescue NameError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
raise "servers #{@context.server.join(', ')} not found"
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns an array of template outputs for controller ctrl in context ctx.
|
71
|
+
def build_body(ctrl, request_ctx)
|
72
|
+
arr = []
|
73
|
+
Launcher.new(ctrl, request_ctx).each &proc {|out| arr << out.to_s }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a Rack app block for Rack::Builder.
|
77
|
+
# This block is passed a request environment and returns and array of three elements:
|
78
|
+
# * a response status code,
|
79
|
+
# * a hash of headers,
|
80
|
+
# * an iterable body object.
|
81
|
+
# It is run on each request.
|
82
|
+
def rack_app
|
83
|
+
ctx = @context
|
84
|
+
proc do |env|
|
85
|
+
request_ctx = ctx.child
|
86
|
+
request_ctx.templates = {}
|
87
|
+
if match = env['REQUEST_PATH'].match(/^(.+)(?<!\$)\/$/)
|
88
|
+
loc = match[1]
|
89
|
+
loc << "?#{env['QUERY_STRING']}" unless env['QUERY_STRING'].empty?
|
90
|
+
[301, {'Location' => loc, 'Content-Type' => 'text/html; charset=utf-8'}, []]
|
91
|
+
else
|
92
|
+
request_ctx.env = env
|
93
|
+
result = ::Tanuki::ControllerBehavior.dispatch(request_ctx, ctx.i18n ? ::Tanuki::I18n : ctx.root_page,
|
94
|
+
Rack::Utils.unescape(env['REQUEST_PATH']).force_encoding('UTF-8'))
|
95
|
+
case result[:type]
|
96
|
+
when :redirect then
|
97
|
+
[302, {'Location' => result[:location], 'Content-Type' => 'text/html; charset=utf-8'}, []]
|
98
|
+
when :page then
|
99
|
+
[200, {'Content-Type' => 'text/html; charset=utf-8'}, build_body(result[:controller], request_ctx)]
|
100
|
+
else
|
101
|
+
[404, {'Content-Type' => 'text/html; charset=utf-8'}, build_body(result[:controller], request_ctx)]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end # end class << self
|
108
|
+
|
109
|
+
end # end Application
|
110
|
+
|
111
|
+
end # end Tanuki
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Tanuki
|
2
|
+
module Argument
|
3
|
+
|
4
|
+
# Tanuki::Argument::Base is the base class for argument types.
|
5
|
+
class Base
|
6
|
+
|
7
|
+
attr_reader :default, :value
|
8
|
+
|
9
|
+
# Initializes the argument with a default value.
|
10
|
+
def initialize(default)
|
11
|
+
@value = @default = default
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns a string representation of the argument value.
|
15
|
+
def to_s
|
16
|
+
@value.to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
# Sets the value with required rules.
|
20
|
+
def value=(obj)
|
21
|
+
@value = to_value(obj)
|
22
|
+
end
|
23
|
+
|
24
|
+
end # end Base
|
25
|
+
|
26
|
+
end # end Argument
|
27
|
+
end # end Tanuki
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Tanuki
|
2
|
+
module Argument
|
3
|
+
|
4
|
+
# Tanuki::Argument::Integer is a class for Integer arguments.
|
5
|
+
class Integer < Base
|
6
|
+
|
7
|
+
# Returns argument value from a string representation.
|
8
|
+
def to_value(obj)
|
9
|
+
begin Kernel::Integer obj rescue @default end
|
10
|
+
end
|
11
|
+
|
12
|
+
end # end Integer
|
13
|
+
|
14
|
+
end # end Argument
|
15
|
+
end # end Tanuki
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Tanuki
|
2
|
+
module Argument
|
3
|
+
|
4
|
+
# Tanuki::Argument::IntegerRange is a class for Integer arguments with a certain value range.
|
5
|
+
class IntegerRange < Integer
|
6
|
+
|
7
|
+
# Initializes the argument with a default value and allowed value range.
|
8
|
+
def initialize(range, default=nil)
|
9
|
+
super(default ? default : range.first)
|
10
|
+
@range = range
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns argument value from a string representation.
|
14
|
+
def to_value(obj)
|
15
|
+
i = super(obj)
|
16
|
+
@range.include?(i) ? i : @default
|
17
|
+
end
|
18
|
+
|
19
|
+
end # end IntegerRange
|
20
|
+
|
21
|
+
end # end Argument
|
22
|
+
end # end Tanuki
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Tanuki
|
2
|
+
module Argument
|
3
|
+
|
4
|
+
# Tanuki::Argument::String is a class for String arguments.
|
5
|
+
class String < Base
|
6
|
+
|
7
|
+
# Returns argument value from a string representation.
|
8
|
+
def to_value(obj)
|
9
|
+
obj.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
end # end String
|
13
|
+
|
14
|
+
end # end Argument
|
15
|
+
end # end Tanuki
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'tanuki/argument/base'
|
2
|
+
require 'tanuki/argument/integer'
|
3
|
+
require 'tanuki/argument/integer_range'
|
4
|
+
require 'tanuki/argument/string'
|
5
|
+
|
6
|
+
module Tanuki
|
7
|
+
|
8
|
+
# Tanuki::Argument contains basic classes and methods for controller arguments.
|
9
|
+
module Argument
|
10
|
+
|
11
|
+
@assoc = {}
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
# Remove argument association for a given type class.
|
16
|
+
def delete(klass)
|
17
|
+
@assoc.delete(klass)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Associate a given type class with an argument class.
|
21
|
+
def store(klass, arg_class)
|
22
|
+
warn "Tanuki::Argument::Base is not an ancestor of `#{arg_class}'" unless arg_class.ancestors.include? Argument::Base
|
23
|
+
@assoc[klass] = arg_class
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :[], :store
|
27
|
+
|
28
|
+
# Convert a given type object to an argument object.
|
29
|
+
def to_argument(obj, *args)
|
30
|
+
if @assoc.include?(klass = obj.class)
|
31
|
+
@assoc[klass].new(obj, *args)
|
32
|
+
else
|
33
|
+
Argument::String.new(obj.to_s)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end # end class << self
|
38
|
+
|
39
|
+
end # end Argument
|
40
|
+
|
41
|
+
end # end Tanuki
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Tanuki
|
2
|
+
|
3
|
+
# Tanuki::Configurator is a scope for evaluating a Tanuki application configuration block.
|
4
|
+
# Use Tanuki::development_application and Tanuki::production_application to create such a block.
|
5
|
+
class Configurator
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Invokes Tanuki::Argument.store.
|
12
|
+
def argument(klass, arg_class)
|
13
|
+
Argument.store(klass, arg_class)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Invokes Tanuki::Application.set.
|
17
|
+
def set(option, value)
|
18
|
+
Application.set(option, value)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Invokes Tanuki::Application.use.
|
22
|
+
def use(middleware, *args, &block)
|
23
|
+
Application.use(middleware, *args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Invokes Tanuki::Application.discard.
|
27
|
+
def discard(middleware)
|
28
|
+
Application.discard(middleware)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Invokes Tanuki::Application.visitor.
|
32
|
+
def visitor(sym, &block)
|
33
|
+
Application.visitor(sym, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
end # end class << self
|
37
|
+
|
38
|
+
end # end Configurator
|
39
|
+
|
40
|
+
# Creates default configuration for development environments.
|
41
|
+
def self.development_application(&block)
|
42
|
+
Application.set :development, true
|
43
|
+
Application.instance_eval do
|
44
|
+
use Rack::CommonLogger
|
45
|
+
use Rack::Lint
|
46
|
+
use Rack::Reloader, 0
|
47
|
+
use Rack::ShowExceptions
|
48
|
+
end
|
49
|
+
common_application(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Creates default configuration for production environments.
|
53
|
+
def self.production_application(&block)
|
54
|
+
Application.set :development, false
|
55
|
+
common_application(&block)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Creates default configuration for common environments.
|
61
|
+
def self.common_application(&block)
|
62
|
+
Application.instance_eval do
|
63
|
+
use Rack::Head
|
64
|
+
use Rack::ShowStatus
|
65
|
+
set :server, [:thin, :mongrel, :webrick]
|
66
|
+
set :host, '0.0.0.0'
|
67
|
+
set :port, 3000
|
68
|
+
set :root, File.expand_path('..', $0)
|
69
|
+
set :app_root, proc { File.join(root, 'app') }
|
70
|
+
set :cache_root, proc { File.join(root, 'cache') }
|
71
|
+
set :schema_root, proc { File.join(root, 'schema') }
|
72
|
+
set :root_page, ::User_Page_Index
|
73
|
+
set :missing_page, ::Tanuki_Page_Missing
|
74
|
+
set :i18n, false
|
75
|
+
set :language, nil
|
76
|
+
set :language_fallback, {}
|
77
|
+
set :languages, proc { language_fallback.keys }
|
78
|
+
set :best_language, proc {|lngs| language_fallback[language].each {|lng| return lng if lngs.include? lng }; nil }
|
79
|
+
set :best_translation, proc {|trn| language_fallback[language].each {|lng| return trn[lng] if trn.include? lng }; nil }
|
80
|
+
visitor :string do s = ''; proc {|out| s << out.to_s } end
|
81
|
+
end
|
82
|
+
Argument.instance_eval do
|
83
|
+
store Fixnum, Argument::Integer
|
84
|
+
store Bignum, Argument::Integer
|
85
|
+
store Range, Argument::IntegerRange
|
86
|
+
store String, Argument::String
|
87
|
+
end
|
88
|
+
Application.instance_eval { @context = @context.child }
|
89
|
+
Configurator.instance_eval(&block) if block_given?
|
90
|
+
Application.run
|
91
|
+
end
|
92
|
+
|
93
|
+
end # end Tanuki
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Tanuki
|
2
|
+
|
3
|
+
# Tanuki::Context is used to create unique environments for each request.
|
4
|
+
# Once a context variable has been set, it cannot be redefined.
|
5
|
+
# Child contexts are used to override parent context variables without changing them directly.
|
6
|
+
# Use Tanuki::Context#child to create new contexts.
|
7
|
+
class Context
|
8
|
+
|
9
|
+
# Create and return child context.
|
10
|
+
def child
|
11
|
+
Class.new(self.class).new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Kernel#method_missing hook.
|
15
|
+
def method_missing(sym, arg=nil)
|
16
|
+
match = sym.to_s.match(/^([^=]+)(=)?$/)
|
17
|
+
self.class.instance_eval do
|
18
|
+
method_sym = match[1].to_sym
|
19
|
+
unless instance_methods(false).include? method_sym
|
20
|
+
if arg.is_a? Proc
|
21
|
+
define_method(method_sym, &arg)
|
22
|
+
else
|
23
|
+
define_method(method_sym) { arg }
|
24
|
+
end
|
25
|
+
return arg
|
26
|
+
else
|
27
|
+
raise "context entry `#{match[1]}' redefined, use Context#child"
|
28
|
+
end
|
29
|
+
end if match[2]
|
30
|
+
warn "#{__FILE__}:#{__LINE__}: warning: undefined context entry `#{sym}' for #{self}"
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
end # end Context
|
35
|
+
|
36
|
+
end # end Tanuki
|