utopia 0.12.6 → 1.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 +4 -4
- data/.travis.yml +6 -2
- data/Gemfile +6 -0
- data/README.md +48 -14
- data/Rakefile +5 -0
- data/bin/utopia +132 -15
- data/lib/utopia.rb +13 -10
- data/lib/utopia/content.rb +140 -0
- data/lib/utopia/content/link.rb +124 -0
- data/lib/utopia/content/links.rb +228 -0
- data/lib/utopia/content/node.rb +387 -0
- data/lib/utopia/content/processor.rb +128 -0
- data/lib/utopia/content/tag.rb +102 -0
- data/lib/utopia/controller.rb +137 -0
- data/lib/utopia/controller/action.rb +112 -0
- data/lib/utopia/controller/base.rb +174 -0
- data/lib/utopia/{middleware/controller → controller}/variables.rb +36 -38
- data/lib/utopia/exception_handler.rb +79 -0
- data/lib/utopia/extensions/array.rb +2 -2
- data/lib/utopia/localization.rb +143 -0
- data/lib/utopia/mail_exceptions.rb +136 -0
- data/lib/utopia/middleware.rb +7 -22
- data/lib/utopia/path.rb +150 -60
- data/lib/utopia/redirector.rb +152 -0
- data/lib/utopia/{extensions/hash.rb → session.rb} +4 -6
- data/lib/utopia/session/encrypted_cookie.rb +46 -48
- data/lib/utopia/{middleware/directory_index.rb → session/lazy_hash.rb} +44 -27
- data/lib/utopia/static.rb +255 -0
- data/lib/utopia/tags/deferred.rb +12 -8
- data/lib/utopia/tags/environment.rb +18 -6
- data/lib/utopia/tags/node.rb +12 -8
- data/lib/utopia/tags/override.rb +12 -12
- data/lib/utopia/version.rb +1 -1
- data/setup/.bowerrc +3 -0
- data/{lib/utopia/setup → setup}/Gemfile +1 -1
- data/setup/Rakefile +4 -0
- data/{lib/utopia/setup → setup}/cache/head/readme.txt +0 -0
- data/{lib/utopia/setup → setup}/cache/meta/readme.txt +0 -0
- data/setup/config.ru +64 -0
- data/{lib/utopia/setup → setup}/lib/readme.txt +0 -0
- data/{lib/utopia/setup → setup}/pages/_heading.xnode +0 -0
- data/{lib/utopia/setup → setup}/pages/_page.xnode +1 -1
- data/{lib/utopia/setup → setup}/pages/_static/icon.png +0 -0
- data/setup/pages/_static/site.css +70 -0
- data/{lib/utopia/setup → setup}/pages/errors/exception.xnode +0 -0
- data/{lib/utopia/setup → setup}/pages/errors/file-not-found.xnode +0 -0
- data/{lib/utopia/setup → setup}/pages/links.yaml +0 -0
- data/setup/pages/welcome/index.xnode +17 -0
- data/{lib/utopia/setup → setup}/public/readme.txt +0 -0
- data/spec/utopia/content/link_spec.rb +108 -0
- data/spec/utopia/content/links/foo/index.xnode +0 -0
- data/spec/utopia/content/links/foo/links.yaml +2 -0
- data/spec/utopia/content/links/foo/test.de.xnode +0 -0
- data/spec/utopia/content/links/foo/test.en.xnode +0 -0
- data/spec/utopia/content/links/links.yaml +9 -0
- data/spec/utopia/content/links/welcome.xnode +0 -0
- data/spec/utopia/content/localized/five/index.en.xnode +0 -0
- data/spec/utopia/content/localized/four/index.en.xnode +0 -0
- data/spec/utopia/content/localized/four/index.zh.xnode +0 -0
- data/spec/utopia/content/localized/four/links.yaml +4 -0
- data/spec/utopia/content/localized/links.yaml +16 -0
- data/spec/utopia/content/localized/one.xnode +0 -0
- data/spec/utopia/content/localized/three/index.xnode +0 -0
- data/spec/utopia/content/localized/two.en.xnode +0 -0
- data/spec/utopia/content/localized/two.zh.xnode +0 -0
- data/spec/utopia/content/node/ordered/first.xnode +0 -0
- data/spec/utopia/content/node/ordered/index.xnode +0 -0
- data/spec/utopia/content/node/ordered/links.yaml +4 -0
- data/spec/utopia/content/node/ordered/second.xnode +0 -0
- data/spec/utopia/content/node/related/foo.en.xnode +0 -0
- data/spec/utopia/content/node/related/foo.ja.xnode +0 -0
- data/spec/utopia/content/node/related/links.yaml +4 -0
- data/spec/utopia/content/node_spec.rb +63 -0
- data/spec/utopia/{middleware/content_spec.rb → content/processor_spec.rb} +34 -23
- data/spec/utopia/content_spec.rb +87 -0
- data/spec/utopia/content_spec.ru +10 -0
- data/spec/utopia/{middleware/controller_spec.rb → controller_spec.rb} +61 -16
- data/spec/utopia/controller_spec.ru +4 -0
- data/spec/utopia/extensions_spec.rb +6 -17
- data/spec/utopia/localization_spec.rb +60 -0
- data/spec/utopia/localization_spec.ru +11 -0
- data/{lib/utopia/tags.rb → spec/utopia/middleware_spec.rb} +8 -14
- data/spec/utopia/{middleware/content_root → pages}/_heading.xnode +0 -0
- data/spec/utopia/pages/content/_show-value.xnode +1 -0
- data/spec/utopia/pages/content/test-partial.xnode +1 -0
- data/spec/utopia/pages/controller/controller.rb +28 -0
- data/spec/utopia/pages/controller/index.xnode +1 -0
- data/spec/utopia/pages/controller/nested/controller.rb +4 -0
- data/spec/utopia/{middleware/content_root → pages}/index.xnode +0 -0
- data/spec/utopia/pages/localized.de.txt +1 -0
- data/spec/utopia/pages/localized.en.txt +1 -0
- data/spec/utopia/pages/localized.jp.txt +1 -0
- data/spec/utopia/pages/node/index.xnode +1 -0
- data/spec/utopia/pages/test.txt +1 -0
- data/spec/utopia/path_spec.rb +109 -0
- data/spec/utopia/rack_spec.rb +2 -0
- data/spec/utopia/session_spec.rb +82 -0
- data/spec/utopia/session_spec.ru +20 -0
- data/spec/utopia/spec_helper.rb +16 -0
- data/{lib/utopia/extensions/string.rb → spec/utopia/static_spec.rb} +24 -15
- data/spec/utopia/static_spec.ru +4 -0
- data/utopia.gemspec +3 -3
- metadata +138 -54
- data/lib/utopia/extensions/regexp.rb +0 -33
- data/lib/utopia/link.rb +0 -288
- data/lib/utopia/middleware/all.rb +0 -33
- data/lib/utopia/middleware/content.rb +0 -157
- data/lib/utopia/middleware/content/node.rb +0 -386
- data/lib/utopia/middleware/content/processor.rb +0 -123
- data/lib/utopia/middleware/controller.rb +0 -130
- data/lib/utopia/middleware/controller/action.rb +0 -121
- data/lib/utopia/middleware/controller/base.rb +0 -184
- data/lib/utopia/middleware/exception_handler.rb +0 -80
- data/lib/utopia/middleware/localization.rb +0 -147
- data/lib/utopia/middleware/localization/name.rb +0 -69
- data/lib/utopia/middleware/mail_exceptions.rb +0 -138
- data/lib/utopia/middleware/redirector.rb +0 -146
- data/lib/utopia/middleware/requester.rb +0 -126
- data/lib/utopia/middleware/static.rb +0 -295
- data/lib/utopia/setup.rb +0 -60
- data/lib/utopia/setup/config.ru +0 -47
- data/lib/utopia/setup/pages/_static/background.png +0 -0
- data/lib/utopia/setup/pages/_static/site.css +0 -48
- data/lib/utopia/setup/pages/welcome/index.xnode +0 -7
- data/lib/utopia/tag.rb +0 -105
- data/lib/utopia/tags/all.rb +0 -34
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
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.
|
|
20
|
-
|
|
21
|
-
require 'utopia/tag'
|
|
22
|
-
require 'trenni/parser'
|
|
23
|
-
require 'trenni/strings'
|
|
24
|
-
|
|
25
|
-
module Utopia
|
|
26
|
-
module Middleware
|
|
27
|
-
class Content
|
|
28
|
-
class Processor
|
|
29
|
-
def self.parse_xml(xml_data, delegate)
|
|
30
|
-
processor = self.new(delegate)
|
|
31
|
-
|
|
32
|
-
processor.parse(xml_data)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
class UnbalancedTagError < StandardError
|
|
36
|
-
def initialize(scanner, start_position, current_tag, closing_tag)
|
|
37
|
-
@scanner = scanner
|
|
38
|
-
@start_position = start_position
|
|
39
|
-
@current_tag = current_tag
|
|
40
|
-
@closing_tag = closing_tag
|
|
41
|
-
|
|
42
|
-
@starting_line = Trenni::Parser.line_at_offset(@scanner.string, @start_position)
|
|
43
|
-
@ending_line = Trenni::Parser.line_at_offset(@scanner.string, @scanner.pos)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
attr :scanner
|
|
47
|
-
attr :start_position
|
|
48
|
-
attr :current_tag
|
|
49
|
-
attr :closing_tag
|
|
50
|
-
|
|
51
|
-
def to_s
|
|
52
|
-
"Unbalanced Tag Error. " \
|
|
53
|
-
"Line #{@starting_line[:line_number]}: #{@current_tag} has been closed by #{@closing_tag} on line #{@ending_line[:line_number]}!"
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def initialize(delegate)
|
|
58
|
-
@delegate = delegate
|
|
59
|
-
@stack = []
|
|
60
|
-
|
|
61
|
-
@parser = Trenni::Parser.new(self)
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def parse(input)
|
|
65
|
-
@parser.parse(input)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def begin_parse(scanner)
|
|
69
|
-
@scanner = scanner
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def text(text)
|
|
73
|
-
@delegate.cdata(text)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def cdata(text)
|
|
77
|
-
@delegate.cdata(Trenni::Strings::to_html(text))
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def comment(text)
|
|
81
|
-
@delegate.cdata("<!#{text}>")
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def begin_tag(tag_name, begin_tag_type)
|
|
85
|
-
if begin_tag_type == :opened
|
|
86
|
-
@stack << [Tag.new(tag_name, {}), @scanner.pos]
|
|
87
|
-
else
|
|
88
|
-
current_tag, current_position = @stack.pop
|
|
89
|
-
|
|
90
|
-
if (tag_name != current_tag.name)
|
|
91
|
-
raise UnbalancedTagError.new(@scanner, current_position, current_tag.name, tag_name)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
@delegate.tag_end(current_tag)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
def finish_tag(begin_tag_type, end_tag_type)
|
|
99
|
-
if begin_tag_type == :opened # <...
|
|
100
|
-
if end_tag_type == :closed # <.../>
|
|
101
|
-
cur, pos = @stack.pop
|
|
102
|
-
cur.closed = true
|
|
103
|
-
|
|
104
|
-
@delegate.tag_complete(cur)
|
|
105
|
-
elsif end_tag_type == :opened # <...>
|
|
106
|
-
cur, pos = @stack.last
|
|
107
|
-
|
|
108
|
-
@delegate.tag_begin(cur)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def attribute(name, value)
|
|
114
|
-
@stack.last[0].attributes[name] = value
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def instruction(content)
|
|
118
|
-
cdata("<?#{content}?>")
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
end
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
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.
|
|
20
|
-
|
|
21
|
-
require_relative 'controller/variables'
|
|
22
|
-
require_relative 'controller/action'
|
|
23
|
-
require_relative 'controller/base'
|
|
24
|
-
|
|
25
|
-
class Rack::Request
|
|
26
|
-
def controller(&block)
|
|
27
|
-
if block_given?
|
|
28
|
-
env["utopia.controller"].instance_eval(&block)
|
|
29
|
-
else
|
|
30
|
-
env["utopia.controller"]
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
module Utopia
|
|
36
|
-
module Middleware
|
|
37
|
-
class Controller
|
|
38
|
-
CONTROLLER_RB = "controller.rb".freeze
|
|
39
|
-
|
|
40
|
-
def initialize(app, options = {})
|
|
41
|
-
@app = app
|
|
42
|
-
@root = options[:root] || Utopia::Middleware::default_root
|
|
43
|
-
|
|
44
|
-
@controllers = {}
|
|
45
|
-
@cache_controllers = (UTOPIA_ENV == :production)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
attr :app
|
|
49
|
-
|
|
50
|
-
def lookup_controller(path)
|
|
51
|
-
if @cache_controllers
|
|
52
|
-
return @controllers.fetch(path.to_s) do |key|
|
|
53
|
-
@controllers[key] = load_controller_file(path)
|
|
54
|
-
end
|
|
55
|
-
else
|
|
56
|
-
return load_controller_file(path)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def load_controller_file(path)
|
|
61
|
-
uri_path = path
|
|
62
|
-
base_path = File.join(@root, uri_path.components)
|
|
63
|
-
|
|
64
|
-
controller_path = File.join(base_path, CONTROLLER_RB)
|
|
65
|
-
# puts "load_controller_file(#{path.inspect}) => #{controller_path}"
|
|
66
|
-
|
|
67
|
-
if File.exist?(controller_path)
|
|
68
|
-
klass = Class.new(Base)
|
|
69
|
-
klass.const_set(:BASE_PATH, base_path)
|
|
70
|
-
klass.const_set(:URI_PATH, uri_path)
|
|
71
|
-
klass.const_set(:CONTROLLER, self)
|
|
72
|
-
|
|
73
|
-
$LOAD_PATH.unshift(base_path)
|
|
74
|
-
|
|
75
|
-
klass.class_eval(File.read(controller_path), controller_path)
|
|
76
|
-
|
|
77
|
-
$LOAD_PATH.delete(base_path)
|
|
78
|
-
|
|
79
|
-
return klass.new
|
|
80
|
-
else
|
|
81
|
-
return nil
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
def invoke_controllers(variables, request, done = Set.new)
|
|
86
|
-
path = Path.create(request.path_info)
|
|
87
|
-
controller_path = Path.new
|
|
88
|
-
|
|
89
|
-
path.descend do |controller_path|
|
|
90
|
-
# puts "Invoke controller: #{controller_path}"
|
|
91
|
-
if controller = lookup_controller(controller_path)
|
|
92
|
-
# We only want to invoke controllers which have not already been invoked:
|
|
93
|
-
unless done.include? controller
|
|
94
|
-
# If we get throw :rewrite, location, the URL has been rewritten and we need to request again:
|
|
95
|
-
location = catch(:rewrite) do
|
|
96
|
-
# Invoke the controller and if it returns a result, send it back out:
|
|
97
|
-
if result = controller.process!(request, path)
|
|
98
|
-
return result
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
if location
|
|
103
|
-
request.env['PATH_INFO'] = location.to_s
|
|
104
|
-
|
|
105
|
-
return invoke_controllers(variables, request, done)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
done << controller
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# No controller gave a useful result:
|
|
114
|
-
return nil
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def call(env)
|
|
118
|
-
variables = (env["utopia.controller"] ||= Variables.new)
|
|
119
|
-
|
|
120
|
-
request = Rack::Request.new(env)
|
|
121
|
-
|
|
122
|
-
if result = invoke_controllers(variables, request)
|
|
123
|
-
return result
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
return @app.call(env)
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
# Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
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.
|
|
20
|
-
|
|
21
|
-
module Utopia
|
|
22
|
-
module Middleware
|
|
23
|
-
class Controller
|
|
24
|
-
class Action < Hash
|
|
25
|
-
attr_accessor :callback
|
|
26
|
-
attr_accessor :options
|
|
27
|
-
|
|
28
|
-
def callback?
|
|
29
|
-
@callback != nil
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def indirect?
|
|
33
|
-
@options[:indirect]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def eql? other
|
|
37
|
-
super and self.callback.eql? other.callback and self.options.eql? other.options
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def hash
|
|
41
|
-
[super, callback, options].hash
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def == other
|
|
45
|
-
super and (self.callback == other.callback) and (self.options == other.options)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
protected
|
|
49
|
-
|
|
50
|
-
def append(path, index, actions = [])
|
|
51
|
-
# ** is greedy, it always matches if possible:
|
|
52
|
-
if match_all = self[:**]
|
|
53
|
-
# Match all remaining input:
|
|
54
|
-
actions << match_all if match_all.callback?
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
if index < path.size
|
|
58
|
-
name = path[index].to_sym
|
|
59
|
-
|
|
60
|
-
if match_name = self[name]
|
|
61
|
-
# Match the exact name:
|
|
62
|
-
match_name.append(path, index+1, actions)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
if match_one = self[:*]
|
|
66
|
-
# Match one input:
|
|
67
|
-
match_one.append(path, index+1, actions)
|
|
68
|
-
end
|
|
69
|
-
else
|
|
70
|
-
# Got to end, matched completely:
|
|
71
|
-
actions << self if self.callback?
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
public
|
|
76
|
-
|
|
77
|
-
# relative_path = 2014/mr-potato
|
|
78
|
-
# actions => {:** => A}
|
|
79
|
-
def select(relative_path)
|
|
80
|
-
actions = []
|
|
81
|
-
|
|
82
|
-
append(relative_path.reverse, 0, actions)
|
|
83
|
-
|
|
84
|
-
# puts "select(#{relative_path}, #{self.inspect}) => #{actions.inspect}"
|
|
85
|
-
|
|
86
|
-
return actions
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def define(path, options = {}, &callback)
|
|
90
|
-
current = self
|
|
91
|
-
|
|
92
|
-
path.reverse.each do |name|
|
|
93
|
-
current = (current[name.to_sym] ||= Action.new)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
current.options = options
|
|
97
|
-
current.callback = callback
|
|
98
|
-
|
|
99
|
-
return current
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def invoke!(controller, *arguments)
|
|
103
|
-
if @options[:unbound]
|
|
104
|
-
# This is the old code path for explicit methods.
|
|
105
|
-
controller.instance_exec(controller, *arguments, &@callback)
|
|
106
|
-
else
|
|
107
|
-
controller.instance_exec(*arguments, &@callback)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def inspect
|
|
112
|
-
if callback?
|
|
113
|
-
"<action " + super + ":#{callback.source_location}(#{options})>"
|
|
114
|
-
else
|
|
115
|
-
"<action " + super + ">"
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
end
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
# Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
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.
|
|
20
|
-
|
|
21
|
-
require 'utopia/path'
|
|
22
|
-
require 'utopia/http'
|
|
23
|
-
|
|
24
|
-
module Utopia
|
|
25
|
-
module Middleware
|
|
26
|
-
class Controller
|
|
27
|
-
class Base
|
|
28
|
-
def self.base_path
|
|
29
|
-
self.const_get(:BASE_PATH)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.uri_path
|
|
33
|
-
self.const_get(:URI_PATH)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def self.controller
|
|
37
|
-
self.const_get(:CONTROLLER)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
class << self
|
|
41
|
-
def require_local(path)
|
|
42
|
-
require File.join(base_path, path)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def direct?(path)
|
|
46
|
-
path.dirname == uri_path
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def actions
|
|
50
|
-
@actions ||= Action.new
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def on(path, options = {}, &block)
|
|
54
|
-
if Symbol === path
|
|
55
|
-
path = ['**', path]
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
actions.define(Path[path].components, options, &block)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def lookup(path)
|
|
62
|
-
possible_actions = actions.select((path - uri_path).components)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def method_added(name)
|
|
66
|
-
if name.match(/^on_(.*)$/)
|
|
67
|
-
warn "Method #{name} using legacy definition mechanism."
|
|
68
|
-
on($1.split("_").join('/'), unbound: true, &name)
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Given a path, look up all matched actions.
|
|
74
|
-
def lookup(path)
|
|
75
|
-
self.class.lookup(path)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Given a request, call associated actions if at least one exists.
|
|
79
|
-
def passthrough(request, path)
|
|
80
|
-
actions = lookup(path)
|
|
81
|
-
|
|
82
|
-
if actions.size > 0
|
|
83
|
-
variables = request.controller
|
|
84
|
-
controller_clone = self.clone
|
|
85
|
-
|
|
86
|
-
variables << controller_clone
|
|
87
|
-
|
|
88
|
-
response = catch(:response) do
|
|
89
|
-
# By default give nothing - i.e. keep on processing:
|
|
90
|
-
actions.each do |action|
|
|
91
|
-
action.invoke!(controller_clone, request, path)
|
|
92
|
-
end and nil
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
if response
|
|
96
|
-
return controller_clone.respond_with(*response)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
return nil
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Copy the instance variables from the previous controller to the next controller (usually only a few). This allows controllers to share effectively the same instance variables while still being separate classes/instances.
|
|
104
|
-
def copy_instance_variables(from)
|
|
105
|
-
from.instance_variables.each do |name|
|
|
106
|
-
instance_variable_set(name, from.instance_variable_get(name))
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def call(env)
|
|
111
|
-
self.class.controller.app.call(env)
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
def respond!(*args)
|
|
115
|
-
throw :response, args
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def ignore!
|
|
119
|
-
throw :response, nil
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
def redirect! (target, status = 302)
|
|
123
|
-
respond! :redirect => target.to_str, :status => status
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
def rewrite! location
|
|
127
|
-
throw :rewrite, location.to_str
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
def fail!(error = :bad_request)
|
|
131
|
-
respond! error
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
def success!(*args)
|
|
135
|
-
respond! :success, *args
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def respond_with(*args)
|
|
139
|
-
return args[0] if args[0] == nil || Array === args[0]
|
|
140
|
-
|
|
141
|
-
status = 200
|
|
142
|
-
options = nil
|
|
143
|
-
|
|
144
|
-
if Numeric === args[0] || Symbol === args[0]
|
|
145
|
-
status = args[0]
|
|
146
|
-
options = args[1] || {}
|
|
147
|
-
else
|
|
148
|
-
options = args[0]
|
|
149
|
-
status = options[:status] || status
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
status = Utopia::HTTP::STATUS_CODES[status] || status
|
|
153
|
-
headers = options[:headers] || {}
|
|
154
|
-
|
|
155
|
-
if options[:type]
|
|
156
|
-
headers['Content-Type'] ||= options[:type]
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
if options[:redirect]
|
|
160
|
-
headers["Location"] = options[:redirect]
|
|
161
|
-
status = 302 if status < 300 || status >= 400
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
body = []
|
|
165
|
-
if options[:body]
|
|
166
|
-
body = options[:body]
|
|
167
|
-
elsif options[:content]
|
|
168
|
-
body = [options[:content]]
|
|
169
|
-
elsif status >= 300
|
|
170
|
-
body = [Utopia::HTTP::STATUS_DESCRIPTIONS[status] || "Status #{status}"]
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
return [status, headers, body]
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# Return nil if this controller didn't do anything. Request will keep on processing. Return a valid rack response if the controller can do so.
|
|
177
|
-
def process!(request, path)
|
|
178
|
-
# puts "process! #{request} #{path}"
|
|
179
|
-
passthrough(request, path)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
end
|
|
184
|
-
end
|