gin 1.1.2 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +22 -1
- data/Manifest.txt +7 -0
- data/TODO.rdoc +45 -0
- data/bin/gin +105 -35
- data/lib/gin.rb +7 -1
- data/lib/gin/app.rb +328 -59
- data/lib/gin/asset_manifest.rb +178 -0
- data/lib/gin/asset_pipeline.rb +235 -0
- data/lib/gin/cache.rb +36 -0
- data/lib/gin/config.rb +3 -1
- data/lib/gin/constants.rb +6 -1
- data/lib/gin/controller.rb +180 -17
- data/lib/gin/core_ext/float.rb +10 -0
- data/lib/gin/core_ext/time.rb +41 -0
- data/lib/gin/filterable.rb +5 -5
- data/lib/gin/mountable.rb +100 -0
- data/lib/gin/request.rb +4 -12
- data/lib/gin/response.rb +4 -2
- data/lib/gin/router.rb +110 -37
- data/lib/gin/strict_hash.rb +33 -0
- data/lib/gin/test.rb +8 -4
- data/lib/gin/worker.rb +49 -0
- data/test/mock_app.rb +7 -7
- data/test/test_app.rb +266 -17
- data/test/test_cache.rb +73 -5
- data/test/test_config.rb +4 -4
- data/test/test_controller.rb +158 -32
- data/test/test_filterable.rb +16 -1
- data/test/test_gin.rb +7 -6
- data/test/test_request.rb +6 -1
- data/test/test_response.rb +1 -1
- data/test/test_router.rb +156 -34
- data/test/test_test.rb +51 -45
- metadata +42 -14
- checksums.yaml +0 -7
data/lib/gin/config.rb
CHANGED
@@ -41,6 +41,8 @@ class Gin::Config
|
|
41
41
|
|
42
42
|
attr_accessor :dir, :logger, :ttl, :environment
|
43
43
|
|
44
|
+
SYNTAX_ERROR = defined?(Psych) ? Psych::SyntaxError : ArgumentError
|
45
|
+
|
44
46
|
##
|
45
47
|
# Create a new config instance for the given environment name.
|
46
48
|
# The environment dictates which part of the config files gets exposed.
|
@@ -120,7 +122,7 @@ class Gin::Config
|
|
120
122
|
@data[name] = c
|
121
123
|
end
|
122
124
|
|
123
|
-
rescue
|
125
|
+
rescue SYNTAX_ERROR
|
124
126
|
@logger.write "[ERROR] Could not parse config `#{filepath}' as YAML"
|
125
127
|
return nil
|
126
128
|
end
|
data/lib/gin/constants.rb
CHANGED
@@ -7,6 +7,8 @@ module Gin::Constants
|
|
7
7
|
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
8
8
|
REMOTE_USER = 'REMOTE_USER'.freeze
|
9
9
|
HTTP_VERSION = 'HTTP_VERSION'.freeze
|
10
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
11
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
10
12
|
REQ_METHOD = 'REQUEST_METHOD'.freeze
|
11
13
|
PATH_INFO = 'PATH_INFO'.freeze
|
12
14
|
QUERY_STRING = 'QUERY_STRING'.freeze
|
@@ -14,6 +16,7 @@ module Gin::Constants
|
|
14
16
|
IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
|
15
17
|
IF_MOD_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
|
16
18
|
IF_UNMOD_SINCE = 'HTTP_IF_UNMODIFIED_SINCE'.freeze
|
19
|
+
RACK_INPUT = 'rack.input'.freeze
|
17
20
|
|
18
21
|
ASYNC_CALLBACK = 'async.callback'.freeze
|
19
22
|
|
@@ -27,13 +30,15 @@ module Gin::Constants
|
|
27
30
|
CACHE_CTRL = 'Cache-Control'.freeze
|
28
31
|
EXPIRES = 'Expires'.freeze
|
29
32
|
PRAGMA = 'Pragma'.freeze
|
33
|
+
HOST_NAME = 'Host'.freeze
|
30
34
|
|
31
35
|
# Gin env constants
|
36
|
+
GIN_APP = 'gin.app'.freeze
|
32
37
|
GIN_STACK = 'gin.stack'.freeze
|
33
38
|
GIN_ROUTE = 'gin.http_route'.freeze
|
34
39
|
GIN_PATH_PARAMS = 'gin.path_query_hash'.freeze
|
40
|
+
GIN_TARGET = 'gin.target'.freeze
|
35
41
|
GIN_CTRL = 'gin.controller'.freeze
|
36
|
-
GIN_ACTION = 'gin.action'.freeze
|
37
42
|
GIN_STATIC = 'gin.static'.freeze
|
38
43
|
GIN_RELOADED = 'gin.reloaded'.freeze
|
39
44
|
GIN_ERRORS = 'gin.errors'.freeze
|
data/lib/gin/controller.rb
CHANGED
@@ -49,6 +49,7 @@
|
|
49
49
|
|
50
50
|
class Gin::Controller
|
51
51
|
extend GinClass
|
52
|
+
extend Gin::Mountable
|
52
53
|
include Gin::Constants
|
53
54
|
include Gin::Filterable
|
54
55
|
include Gin::Errorable
|
@@ -84,7 +85,7 @@ class Gin::Controller
|
|
84
85
|
# Array of action names for this controller.
|
85
86
|
|
86
87
|
def self.actions
|
87
|
-
instance_methods(false)
|
88
|
+
instance_methods(false).map{|a| a.to_sym }
|
88
89
|
end
|
89
90
|
|
90
91
|
|
@@ -93,6 +94,9 @@ class Gin::Controller
|
|
93
94
|
# Underscores the class name and removes mentions of 'controller'.
|
94
95
|
# MyApp::FooController.controller_name
|
95
96
|
# #=> "my_app/foo"
|
97
|
+
#
|
98
|
+
# Note that when route names get autogenerated, the namespacing is dropped.
|
99
|
+
# If two routes have the same name, the last one defined wins.
|
96
100
|
|
97
101
|
def self.controller_name new_name=nil
|
98
102
|
@ctrl_name = new_name if new_name
|
@@ -100,6 +104,32 @@ class Gin::Controller
|
|
100
104
|
end
|
101
105
|
|
102
106
|
|
107
|
+
DEFAULT_ACTION_MAP = {
|
108
|
+
:index => %w{GET /},
|
109
|
+
:show => %w{GET /:id},
|
110
|
+
:new => %w{GET /new},
|
111
|
+
:create => %w{POST /},
|
112
|
+
:edit => %w{GET /:id/edit},
|
113
|
+
:update => %w{PUT /:id},
|
114
|
+
:destroy => %w{DELETE /:id}
|
115
|
+
} # :nodoc:
|
116
|
+
|
117
|
+
|
118
|
+
def self.default_route_for action #:nodoc:
|
119
|
+
DEFAULT_ACTION_MAP[action] || ['GET', "/#{action}"]
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def self.route_name_for action #:nodoc:
|
124
|
+
"#{action}_#{controller_name.sub(%r{^.*/},'')}".to_sym
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
def self.display_name action=nil #:nodoc:
|
129
|
+
[self, action].compact.join("#")
|
130
|
+
end
|
131
|
+
|
132
|
+
|
103
133
|
##
|
104
134
|
# Set or get the default content type for this Gin::Controller.
|
105
135
|
# Default value is "text/html". This attribute is inherited.
|
@@ -123,6 +153,18 @@ class Gin::Controller
|
|
123
153
|
end
|
124
154
|
|
125
155
|
|
156
|
+
##
|
157
|
+
# Call the Controller with an Rack env hash. Requires the hash to have
|
158
|
+
# the keys 'gin.target' with the action name as the second item of the array,
|
159
|
+
# and 'gin.app'.
|
160
|
+
|
161
|
+
def self.call env
|
162
|
+
inst = new(env[GIN_APP], env)
|
163
|
+
env[GIN_CTRL] = inst
|
164
|
+
inst.call_action(env[GIN_TARGET][1])
|
165
|
+
end
|
166
|
+
|
167
|
+
|
126
168
|
##
|
127
169
|
# Get or set a layout for a given controller.
|
128
170
|
# Value can be a symbol or filepath.
|
@@ -164,7 +206,11 @@ class Gin::Controller
|
|
164
206
|
end
|
165
207
|
|
166
208
|
|
167
|
-
|
209
|
+
##
|
210
|
+
# Calls the given or preset action and returns a Rack response Array.
|
211
|
+
|
212
|
+
def call_action action=nil
|
213
|
+
action ||= @action
|
168
214
|
invoke{ dispatch action }
|
169
215
|
invoke{ handle_status(@response.status) }
|
170
216
|
content_type self.class.content_type unless @response[CNT_TYPE]
|
@@ -301,8 +347,6 @@ class Gin::Controller
|
|
301
347
|
end
|
302
348
|
|
303
349
|
|
304
|
-
|
305
|
-
|
306
350
|
##
|
307
351
|
# Set multiple response headers with Hash.
|
308
352
|
|
@@ -404,7 +448,8 @@ class Gin::Controller
|
|
404
448
|
|
405
449
|
def path_to *args
|
406
450
|
return "#{args[0]}#{"?" << Gin.build_query(args[1]) if args[1]}" if String === args[0]
|
407
|
-
args.unshift(self.class) if Symbol === args[0] &&
|
451
|
+
args.unshift(self.class) if Symbol === args[0] &&
|
452
|
+
self.class.actions.include?(args[0])
|
408
453
|
@app.router.path_to(*args)
|
409
454
|
end
|
410
455
|
|
@@ -471,6 +516,95 @@ class Gin::Controller
|
|
471
516
|
end
|
472
517
|
|
473
518
|
|
519
|
+
##
|
520
|
+
# Halt execution of the current controller action and create a new request to
|
521
|
+
# another action and/or controller, or path.
|
522
|
+
#
|
523
|
+
# Raises Gin::RouterError if the controller and action don't have a route.
|
524
|
+
# Returns a 404 response if an unrecognized path is given.
|
525
|
+
#
|
526
|
+
# The rewrite method acts just as if a request had been sent from the client,
|
527
|
+
# and will re-run any of the in-app middleware. If the app is itself running
|
528
|
+
# as middleware, you may use rewrite to pass a request down to the next item
|
529
|
+
# in the stack by specifying a path not supported by the app.
|
530
|
+
#
|
531
|
+
# Supports the same arguments at the Gin::Controller#url_to method.
|
532
|
+
#
|
533
|
+
# rewrite MyController, :action
|
534
|
+
# #=> Calls app with route for MyController#action
|
535
|
+
#
|
536
|
+
# rewrite MyController, :show, :id => 123
|
537
|
+
# #=> Calls app with route for MyController#action with the given params
|
538
|
+
#
|
539
|
+
# rewrite :show_foo
|
540
|
+
# #=> Calls app with route for the current controller's :show_foo action,
|
541
|
+
# #=> or if missing the controller and action for the :show_foo named route.
|
542
|
+
#
|
543
|
+
# # Rewrite and execute the request with the given headers.
|
544
|
+
# rewrite :show_foo, 'HTTP_X_CUSTOM_HEADER' => 'foo'
|
545
|
+
# rewrite :show_foo, params, 'HTTP_X_CUSTOM_HEADER' => 'foo'
|
546
|
+
#
|
547
|
+
# # Rewrite to an arbitrary path.
|
548
|
+
# rewrite '/path/to/something/else', {}, 'REQUEST_METHOD' => 'POST'
|
549
|
+
#
|
550
|
+
# Note that params are not forwarded with the rewrite call. The app
|
551
|
+
# considers this to be a completely different request, which means all params
|
552
|
+
# required must be passed explicitely.
|
553
|
+
#
|
554
|
+
# Streamed and IO request content is also ignored unless it is explicitely
|
555
|
+
# assigned to the 'rack.input' (as a part of the headers argument).
|
556
|
+
|
557
|
+
def rewrite *args
|
558
|
+
args.unshift(self.class) if Symbol === args[0] &&
|
559
|
+
self.class.actions.include?(args[0])
|
560
|
+
halt(*@app.rewrite!(@env, *args))
|
561
|
+
end
|
562
|
+
|
563
|
+
|
564
|
+
##
|
565
|
+
# Unlike Gin::Controller#rewrite, the reroute method forwards the current
|
566
|
+
# request and params to the provided controller and/or action, or named route.
|
567
|
+
# Halts further execution in the current action.
|
568
|
+
# Raises RouterError if a given named route isn't found in the app's routes.
|
569
|
+
#
|
570
|
+
# reroute MyController, :action
|
571
|
+
# #=> Executes MyController#action
|
572
|
+
#
|
573
|
+
# reroute MyController, :show, :id => 123
|
574
|
+
# #=> Executes MyController#action with the given params merged to
|
575
|
+
# #=> the current params.
|
576
|
+
#
|
577
|
+
# reroute :show_foo
|
578
|
+
# #=> Executes the current controller's :show_foo action, or if missing
|
579
|
+
# #=> the controller and action for the :show_foo named route.
|
580
|
+
#
|
581
|
+
# # Reroute with the given headers.
|
582
|
+
# reroute :show_foo, {}, 'HTTP_X_CUSTOM_HEADER' => 'foo'
|
583
|
+
|
584
|
+
def reroute *args
|
585
|
+
args.unshift(self.class) if Symbol === args[0] &&
|
586
|
+
self.class.actions.include?(args[0])
|
587
|
+
nheaders = args.pop if Hash === args.last && Hash === args[-2] && args[-2] != args[-1]
|
588
|
+
nparams = args.pop if Hash === args.last
|
589
|
+
|
590
|
+
if Class === args[0]
|
591
|
+
ctrl_klass, naction = args[0..1]
|
592
|
+
else
|
593
|
+
route = @app.router.route_to(*args)
|
594
|
+
ctrl_klass, naction = route.target
|
595
|
+
end
|
596
|
+
|
597
|
+
nenv = @env.merge(nheaders || {})
|
598
|
+
nenv[GIN_PATH_PARAMS] = {}
|
599
|
+
ctrl = ctrl_klass.new(@app, nenv)
|
600
|
+
|
601
|
+
ctrl.params.merge!(params)
|
602
|
+
ctrl.params.merge!(nparams) if nparams
|
603
|
+
|
604
|
+
halt(*ctrl.call_action(naction))
|
605
|
+
end
|
606
|
+
|
607
|
+
|
474
608
|
##
|
475
609
|
# Assigns a file to the response body and halts the execution of the action.
|
476
610
|
# Produces a 404 response if no file is found.
|
@@ -510,9 +644,15 @@ class Gin::Controller
|
|
510
644
|
def last_modified time
|
511
645
|
return unless time
|
512
646
|
|
513
|
-
time =
|
514
|
-
|
515
|
-
|
647
|
+
time = if Integer === time
|
648
|
+
Time.at(time)
|
649
|
+
elsif time.respond_to?(:to_time)
|
650
|
+
time.to_time
|
651
|
+
elsif !time.is_a?(Time)
|
652
|
+
Time.parse time.to_s
|
653
|
+
else
|
654
|
+
time
|
655
|
+
end
|
516
656
|
|
517
657
|
@response[LAST_MOD] = time.httpdate
|
518
658
|
return if @env[IF_NONE_MATCH]
|
@@ -596,7 +736,7 @@ class Gin::Controller
|
|
596
736
|
|
597
737
|
def expire_cache_control
|
598
738
|
@response[PRAGMA] = 'no-cache'
|
599
|
-
expires EPOCH, :no_cache, :no_store, :must_revalidate, max_age
|
739
|
+
expires EPOCH, :no_cache, :no_store, :must_revalidate, :max_age => 0
|
600
740
|
end
|
601
741
|
|
602
742
|
|
@@ -604,9 +744,28 @@ class Gin::Controller
|
|
604
744
|
# Returns the url to an asset, including predefined asset cdn hosts if set.
|
605
745
|
|
606
746
|
def asset_url name
|
607
|
-
|
608
|
-
|
609
|
-
|
747
|
+
host = @app.asset_host_for(name)
|
748
|
+
return asset_path(name) if !host
|
749
|
+
File.join(host, name)
|
750
|
+
end
|
751
|
+
|
752
|
+
|
753
|
+
##
|
754
|
+
# Returns the HTTP path to the local asset.
|
755
|
+
|
756
|
+
def asset_path name
|
757
|
+
fdpath = @app.asset(name)
|
758
|
+
|
759
|
+
if fdpath && fdpath.start_with?(@app.assets_dir)
|
760
|
+
if fdpath.start_with?(@app.public_dir)
|
761
|
+
fdpath[@app.public_dir.length..-1]
|
762
|
+
else
|
763
|
+
fdpath[@app.assets_dir.length..-1]
|
764
|
+
end
|
765
|
+
else
|
766
|
+
path = File.join('', name)
|
767
|
+
[path, *@app.asset_version(name)].compact.join("?")
|
768
|
+
end
|
610
769
|
end
|
611
770
|
|
612
771
|
|
@@ -643,7 +802,7 @@ class Gin::Controller
|
|
643
802
|
# #=> "<root_dir>/other/foo"
|
644
803
|
|
645
804
|
def template_path template, is_layout=false
|
646
|
-
dir = if template[0] == ?/
|
805
|
+
dir = if template.to_s[0] == ?/
|
647
806
|
@app.root_dir
|
648
807
|
elsif is_layout
|
649
808
|
@app.layouts_dir
|
@@ -765,10 +924,11 @@ class Gin::Controller
|
|
765
924
|
|
766
925
|
def html_error_page err, code=nil
|
767
926
|
if @app.development?
|
768
|
-
|
927
|
+
backtrace = err.backtrace || ['No backtrace :(']
|
928
|
+
fulltrace = backtrace.join("\n")
|
769
929
|
fulltrace = "<pre>#{h(fulltrace)}</pre>"
|
770
930
|
|
771
|
-
apptrace = Gin.app_trace(
|
931
|
+
apptrace = Gin.app_trace(backtrace).join("\n")
|
772
932
|
apptrace = "<pre>#{h(apptrace)}</pre>" unless apptrace.empty?
|
773
933
|
|
774
934
|
DEV_ERROR_HTML %
|
@@ -807,9 +967,12 @@ class Gin::Controller
|
|
807
967
|
# Get action arguments from the params.
|
808
968
|
# Raises Gin::BadRequest if a required argument has no matching param.
|
809
969
|
|
810
|
-
def action_arguments action
|
970
|
+
def action_arguments action
|
971
|
+
raise Gin::NotFound,
|
972
|
+
"No action exists for: #{env[REQ_METHOD]} #{env[PATH_INFO]}" unless action
|
973
|
+
|
811
974
|
raise Gin::NotFound, "No action #{self.class}##{action}" unless
|
812
|
-
self.class.actions.include?
|
975
|
+
self.class.actions.include?(action.to_sym)
|
813
976
|
|
814
977
|
args = []
|
815
978
|
temp = []
|
@@ -0,0 +1,41 @@
|
|
1
|
+
unless Time.now.respond_to?(:httpdate)
|
2
|
+
|
3
|
+
class Time
|
4
|
+
def httpdate(date)
|
5
|
+
if /\A\s*
|
6
|
+
(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\x20
|
7
|
+
(\d{2})\x20
|
8
|
+
(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20
|
9
|
+
(\d{4})\x20
|
10
|
+
(\d{2}):(\d{2}):(\d{2})\x20
|
11
|
+
GMT
|
12
|
+
\s*\z/x =~ date
|
13
|
+
self.rfc2822(date)
|
14
|
+
elsif /\A\s*
|
15
|
+
(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday),\x20
|
16
|
+
(\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d)\x20
|
17
|
+
(\d\d):(\d\d):(\d\d)\x20
|
18
|
+
GMT
|
19
|
+
\s*\z/x =~ date
|
20
|
+
year = $3.to_i
|
21
|
+
if year < 50
|
22
|
+
year += 2000
|
23
|
+
else
|
24
|
+
year += 1900
|
25
|
+
end
|
26
|
+
self.utc(year, $2, $1.to_i, $4.to_i, $5.to_i, $6.to_i)
|
27
|
+
elsif /\A\s*
|
28
|
+
(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\x20
|
29
|
+
(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\x20
|
30
|
+
(\d\d|\x20\d)\x20
|
31
|
+
(\d\d):(\d\d):(\d\d)\x20
|
32
|
+
(\d{4})
|
33
|
+
\s*\z/x =~ date
|
34
|
+
self.utc($6.to_i, MonthValue[$1.upcase], $2.to_i,
|
35
|
+
$3.to_i, $4.to_i, $5.to_i)
|
36
|
+
else
|
37
|
+
raise ArgumentError.new("not RFC 2616 compliant date: #{date.inspect}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/gin/filterable.rb
CHANGED
@@ -74,14 +74,14 @@ module Gin::Filterable
|
|
74
74
|
|
75
75
|
elsif opts[:except]
|
76
76
|
except = Array(opts[:except])
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
except.include?(action)
|
77
|
+
except.each do |action|
|
78
|
+
filter_hsh[action] ||= filter_hsh[nil].dup
|
79
|
+
end
|
81
80
|
|
81
|
+
filter_hsh.keys.each do |action|
|
82
|
+
next if except.include?(action)
|
82
83
|
yield filter_hsh, action, names
|
83
84
|
end
|
84
|
-
yield filter_hsh, nil, names
|
85
85
|
|
86
86
|
else
|
87
87
|
filter_hsh.keys.each do |action|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
##
|
2
|
+
# The Gin::Mountable module provides an interface to mount any type of object
|
3
|
+
# to a Gin::App route.
|
4
|
+
#
|
5
|
+
# The Gin::Mountable module is only necessary if features such automatic route
|
6
|
+
# naming and defaults are needed. Gin will otherwise happily mount any object
|
7
|
+
# that responds to the `call' method onto it's routing tree.
|
8
|
+
|
9
|
+
module Gin::Mountable
|
10
|
+
|
11
|
+
def verify_mount! #:nodoc:
|
12
|
+
controller_name.to_str
|
13
|
+
|
14
|
+
test_action = actions[0]
|
15
|
+
|
16
|
+
test_route = default_route_for(test_action)
|
17
|
+
test_route[0].to_str
|
18
|
+
test_route[1].to_str
|
19
|
+
|
20
|
+
route_name_for(test_action).to_proc
|
21
|
+
|
22
|
+
display_name.to_str
|
23
|
+
display_name(test_action).to_str
|
24
|
+
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
##
|
30
|
+
# The actions available on this Mountable object.
|
31
|
+
# Actions can be of any object type.
|
32
|
+
#
|
33
|
+
# Must return an Array of actions. Must be overloaded.
|
34
|
+
# UserController.actions
|
35
|
+
# #=> [:show, :delete, :list, :update]
|
36
|
+
|
37
|
+
def actions
|
38
|
+
raise NoMethodError,
|
39
|
+
"The `#{__method__}' method must be defined on #{self} and return an\
|
40
|
+
Array of available actions for the Router to map to."
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
##
|
45
|
+
# The String representing the controller.
|
46
|
+
#
|
47
|
+
# Must return a String. Must be overloaded.
|
48
|
+
# UserController.controller_name
|
49
|
+
# #=> 'user'
|
50
|
+
|
51
|
+
def controller_name
|
52
|
+
raise NoMethodError,
|
53
|
+
"The `#{__method__}' method must be defined on #{self} and return a\
|
54
|
+
String representing the controller name."
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
##
|
59
|
+
# Should return a 2 item Array with the HTTP verb and request path (local to
|
60
|
+
# the controller) to use for a given action.
|
61
|
+
#
|
62
|
+
# Must return a 2 item Array of Strings. Must be overloaded.
|
63
|
+
# UserController.default_route_for :show
|
64
|
+
# #=> ['GET', '/:id']
|
65
|
+
|
66
|
+
def default_route_for action
|
67
|
+
raise NoMethodError,
|
68
|
+
"The `#{__method__}' method must be defined on #{self} and return a\
|
69
|
+
2 item Array with the HTTP verb and local path: ['GET', '/users/:id']"
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
##
|
74
|
+
# Creates a route name used to identify a given route. Used by helper methods.
|
75
|
+
#
|
76
|
+
# Must return a Symbol, or nil. Must be overloaded.
|
77
|
+
# UserController.route_name_for :show
|
78
|
+
# #=> :show_user
|
79
|
+
|
80
|
+
def route_name_for action
|
81
|
+
raise NoMethodError,
|
82
|
+
"The `#{__method__}' method must be defined on #{self} and return a\
|
83
|
+
Symbol representing the route name: :show_user"
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
##
|
88
|
+
# Creates a display name for the Controller (and optional action).
|
89
|
+
# Used for logging and error messages.
|
90
|
+
#
|
91
|
+
# Must return a String. Must be overloaded.
|
92
|
+
# UserController.display_name :show
|
93
|
+
# #=> "UserController#show"
|
94
|
+
|
95
|
+
def display_name action=nil
|
96
|
+
raise NoMethodError,
|
97
|
+
"The `#{__method__}' method must be defined on #{self} and return a\
|
98
|
+
String for display purposes"
|
99
|
+
end
|
100
|
+
end
|