utopia 0.12.5 → 0.12.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ea079ab122d29c56750a675d963b90ef916f5eac
4
- data.tar.gz: c9b2cc7c6224f2d298c519af4a07d5f78fee8633
3
+ metadata.gz: 296a41faf645d12aa55672af9445a4f94f939da1
4
+ data.tar.gz: 53019aaae569b4191d3361d3d2a1094a9e52bcfe
5
5
  SHA512:
6
- metadata.gz: fb8391a52441f77e987fae432167b8b4c826e9c2856ac0e3facc96cc9e4b18ceae665df604fbeafe079542da2cffa6242125f3c6f6035b46bdceb4842d3e9598
7
- data.tar.gz: 12752a420f331461d35b47292a88f58f0134a5664ebfa76dea992e47f4e4824557074e417f07c030cc2fe1c3c2502b3bdbd63c39db36042dde9b49942a2780f1
6
+ metadata.gz: 34aaf114f9917265dbe27b65379bb77cb66fda5310b27384d2edcae8dc21fc01d7d814d6ea79f98fca46be81cbf2c1b9dc0c1da9f40b6af20dffa7cfb5dd2557
7
+ data.tar.gz: 09e47aa4fd61d9d015c7aa836c734dda5f20570efd02bacdd745756b2e02c6fc4e32e413bd47dfd1d140d740adf340dd4f0682b08ad63b579352af2798b0fae7
@@ -19,23 +19,11 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  class Array
22
- def find_index(&block)
23
- each_with_index do |item, index|
24
- if yield(item)
25
- return index
26
- end
27
- end
28
-
29
- return nil
30
- end
31
-
32
22
  def split_at(&block)
33
- index = find_index(&block)
34
-
35
- if index
36
- return [self[0...index], self[index], self[index+1..-1]]
23
+ if middle = index(&block)
24
+ [self[0...middle], self[middle], self[middle+1..-1]]
25
+ else
26
+ [[], nil, []]
37
27
  end
38
-
39
- return [[], nil, []]
40
28
  end
41
29
  end
@@ -23,61 +23,26 @@
23
23
 
24
24
  require 'date'
25
25
 
26
- class Date
27
- alias_method :old_compare, :<=>
28
-
29
- def <=>(other)
30
- if other.class == Date
31
- old_compare(other)
32
- else
33
- if Time === other
34
- other = other.to_datetime
35
- end
36
-
37
- if DateTime === other
38
- result = old_compare(other.to_date)
39
- if result == 0 && other.day_fraction > 0
40
- -1
41
- else
42
- result
43
- end
44
- end
45
- end
46
- end
47
- end
48
-
49
26
  class Time
50
27
  alias_method :old_compare, :<=>
51
28
 
52
29
  def <=>(other)
53
- if other.class == Date
54
- (other <=> self) * -1
55
- elsif Time === other
56
- old_compare(other)
30
+ if Date === other or DateTime === other
31
+ self.to_datetime <=> other
57
32
  else
58
- if DateTime === other
59
- other = other.to_time
60
- end
61
-
62
33
  old_compare(other)
63
34
  end
64
35
  end
65
36
  end
66
37
 
67
- class DateTime
38
+ class Date
68
39
  alias_method :old_compare, :<=>
69
40
 
70
41
  def <=>(other)
71
- if other.class == Date
72
- (other <=> self) * -1
73
- elsif DateTime === other
74
- old_compare(other)
42
+ if Time === other
43
+ self.to_datetime <=> other.to_datetime
75
44
  else
76
- if Time === other
77
- other = other.to_datetime
78
- end
79
-
80
45
  old_compare(other)
81
46
  end
82
47
  end
83
- end
48
+ end
@@ -21,8 +21,7 @@
21
21
  class Hash
22
22
  def symbolize_keys
23
23
  inject({}) do |options, (key, value)|
24
- options[(key.to_sym rescue key) || key] = value
25
- options
24
+ options[(key.to_sym rescue key) || key] = value; options
26
25
  end
27
26
  end
28
27
  end
@@ -22,23 +22,18 @@ require 'rack'
22
22
 
23
23
  class Rack::Request
24
24
  def url_with_path(path = "")
25
- url = scheme + "://"
26
- url << host
27
-
28
- if scheme == "https" && port != 443 || scheme == "http" && port != 80
29
- url << ":#{port}"
30
- end
31
-
32
- url << path
25
+ base_url << path
33
26
  end
34
27
  end
35
28
 
36
29
  class Rack::Response
30
+ # Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
37
31
  def do_not_cache!
38
32
  self["Cache-Control"] = "no-cache, must-revalidate"
39
33
  self["Expires"] = Time.now.httpdate
40
34
  end
41
35
 
36
+ # Specify that the content should be cached.
42
37
  def cache!(duration = 3600)
43
38
  unless (self["Cache-Control"] || "").match(/no-cache/)
44
39
  self["Cache-Control"] = "public, max-age=#{duration}"
@@ -46,6 +41,7 @@ class Rack::Response
46
41
  end
47
42
  end
48
43
 
44
+ # Specify the content type of the response data.
49
45
  def content_type!(value)
50
46
  self["Content-Type"] = value.to_s
51
47
  end
@@ -18,23 +18,22 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- class String
22
- HTML_ESCAPE = {"&" => "&amp;", "<" => "&lt;", ">" => "&gt;", "\"" => "&quot;"}
23
- HTML_ESCAPE_PATTERN = Regexp.new("[" + Regexp.quote(HTML_ESCAPE.keys.join) + "]")
21
+ require 'trenni/strings'
24
22
 
23
+ class String
25
24
  def to_html
26
- gsub(HTML_ESCAPE_PATTERN){|c| HTML_ESCAPE[c]}
25
+ Trenni::Strings::to_html(self)
27
26
  end
28
27
 
29
28
  def to_quoted_string
30
- '"' + self.gsub('"', '\\"').gsub(/\r/, "\\r").gsub(/\n/, "\\n") + '"'
29
+ Trenni::Strings::to_quoted_string(self)
31
30
  end
32
31
 
33
32
  def to_title
34
- self.gsub(/(^|[ \-_])(.)/){" " + $2.upcase}.strip
33
+ Trenni::Strings::to_title(self)
35
34
  end
36
35
 
37
36
  def to_snake
38
- self.gsub("::", "").gsub(/([A-Z]+)/){"_" + $1.downcase}.sub(/^_+/, "")
37
+ Trenni::Strings::to_snake(self)
39
38
  end
40
39
  end
@@ -20,6 +20,8 @@
20
20
 
21
21
  require 'pathname'
22
22
 
23
+ warn "require 'utopia/middleware/all' is deprecated. Require 'utopia' instead."
24
+
23
25
  Pathname.new(__FILE__).dirname.entries.each do |path|
24
26
  next unless /\.rb$/ === path.to_s
25
27
 
@@ -0,0 +1,121 @@
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
@@ -0,0 +1,184 @@
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
@@ -0,0 +1,72 @@
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 Variables
25
+ def initialize
26
+ @controllers = []
27
+ end
28
+
29
+ def << controller
30
+ top = @controllers.last
31
+
32
+ @controllers << controller
33
+
34
+ # This ensures that most variables will be at the top and controllers can naturally interactive with instance variables.
35
+ controller.copy_instance_variables(top) if top
36
+ end
37
+
38
+ def fetch(key)
39
+ @controllers.reverse_each do |controller|
40
+ if controller.instance_variables.include?(key)
41
+ return controller.instance_variable_get(key)
42
+ end
43
+ end
44
+
45
+ if block_given?
46
+ yield key
47
+ else
48
+ raise KeyError.new(key)
49
+ end
50
+ end
51
+
52
+ def to_hash
53
+ attributes = {}
54
+
55
+ @controllers.each do |controller|
56
+ controller.instance_variables.each do |name|
57
+ key = name[1..-1]
58
+
59
+ attributes[key] = controller.instance_variable_get(name)
60
+ end
61
+ end
62
+
63
+ return attributes
64
+ end
65
+
66
+ def [] key
67
+ fetch("@#{key}".to_sym) { nil }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end