utopia 0.12.5 → 0.12.6

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 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