pancake 0.1.29 → 0.2.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.
- data/README.textile +0 -4
- data/Rakefile +1 -32
- data/TODO.textile +19 -0
- data/bin/pancake-gen +1 -0
- data/lib/pancake.rb +26 -39
- data/lib/pancake/bootloaders.rb +2 -2
- data/lib/pancake/configuration.rb +1 -1
- data/lib/pancake/core_ext/class.rb +3 -3
- data/lib/pancake/errors.rb +6 -8
- data/lib/pancake/generators.rb +1 -0
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/development.rb.tt +0 -3
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/production.rb.tt +0 -3
- data/lib/pancake/generators/templates/short/%stack_name%/lib/%stack_name%/config/environments/staging.rb.tt +0 -3
- data/lib/pancake/hooks/inheritable_inner_classes.rb +9 -10
- data/lib/pancake/hooks/on_inherit.rb +7 -7
- data/lib/pancake/logger.rb +1 -14
- data/lib/pancake/master.rb +7 -45
- data/lib/pancake/middleware.rb +11 -64
- data/lib/pancake/mixins/publish.rb +20 -20
- data/lib/pancake/mixins/render.rb +59 -69
- data/lib/pancake/mixins/render/template.rb +1 -1
- data/lib/pancake/mixins/render/view_context.rb +143 -7
- data/lib/pancake/mixins/request_helper.rb +38 -1
- data/lib/pancake/mixins/stack_helper.rb +2 -2
- data/lib/pancake/paths.rb +1 -1
- data/lib/pancake/router.rb +10 -2
- data/lib/pancake/stack/bootloader.rb +12 -1
- data/lib/pancake/stack/router.rb +2 -1
- data/lib/pancake/stack/stack.rb +60 -2
- data/lib/pancake/stacks/short/controller.rb +58 -3
- data/lib/pancake/stacks/short/default/views/base.html.haml +1 -1
- data/lib/pancake/stacks/short/default/views/error.html.haml +12 -0
- data/lib/pancake/stacks/short/stack.rb +1 -0
- data/spec/pancake/fixtures/render_templates/alternate.foo_env.html.haml +1 -0
- data/spec/pancake/fixtures/render_templates/alternate.html.haml +1 -0
- data/spec/pancake/fixtures/render_templates/view_context/capture_erb.erb +1 -1
- data/spec/pancake/fixtures/render_templates/view_context/capture_haml.haml +1 -1
- data/spec/pancake/fixtures/render_templates/view_context/concat_erb.erb +1 -1
- data/spec/pancake/fixtures/render_templates/view_context/concat_haml.haml +2 -1
- data/spec/pancake/middleware_spec.rb +4 -26
- data/spec/pancake/mixins/render/view_context_spec.rb +15 -23
- data/spec/pancake/mixins/render_spec.rb +54 -0
- data/spec/pancake/pancake_spec.rb +0 -22
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +1 -0
- metadata +193 -108
- data/TODO +0 -7
- data/bin/jeweler +0 -19
- data/lib/pancake/mixins/render/render.rb +0 -197
data/lib/pancake/middleware.rb
CHANGED
@@ -47,21 +47,15 @@ module Pancake
|
|
47
47
|
|
48
48
|
# @param [Array<Symbol>] labels An array of labels specifying the stack labels to use to build the middlware list
|
49
49
|
#
|
50
|
-
# @example
|
51
|
-
# MyApp.middlewares(:production) # provides all middlewares matching the :production label, or the implicit :any label
|
52
|
-
# MyApp.middlewares(:development, :demo) # provides all middlewares matching the :development or :demo or implicit :any label
|
53
|
-
#
|
54
50
|
# @return [Array<StackMiddleware>]
|
55
51
|
# An array of middleware specifications in the order they should be used to wrap the application
|
56
52
|
#
|
57
53
|
# @see Pancake::Middleware::StackMiddleware
|
58
|
-
# @see Pancake.stack_labels for a decription of stack_labels
|
59
54
|
# @api public
|
60
55
|
# @since 0.1.0
|
61
56
|
# @author Daniel Neighman
|
62
|
-
def middlewares
|
63
|
-
|
64
|
-
self::StackMiddleware.middlewares(*labels)
|
57
|
+
def middlewares
|
58
|
+
self::StackMiddleware.middlewares
|
65
59
|
end
|
66
60
|
|
67
61
|
# Useful for adding additional information into your middleware stack definition
|
@@ -70,8 +64,6 @@ module Pancake
|
|
70
64
|
# The name of a given middleware. Each piece of middleware has a name in the stack.
|
71
65
|
# By naming middleware we can refer to it later, swap it out for a different class or even just remove it from the stack.
|
72
66
|
# @param [Hash] opts An options hash
|
73
|
-
# @option opts [Array<Symbol>] :labels ([:any])
|
74
|
-
# An array of symbols, or a straight symbol that defines what stacks this middleware sould be active in
|
75
67
|
# @option opts [Object] :before
|
76
68
|
# Sets this middlware to be run after the middleware named. Name is either the name given to the
|
77
69
|
# middleware stack, or the Middleware class itself.
|
@@ -101,15 +93,6 @@ module Pancake
|
|
101
93
|
# This middleware will be named :foo and will be run after the middleware named :bar
|
102
94
|
# If :bar is not run, :foo will not be run either
|
103
95
|
#
|
104
|
-
# @example Declaring a named middleware with some labels
|
105
|
-
# MyClass.stack(:foo, :lables => [:demo, :production, :staging]).use(MyMiddleware)
|
106
|
-
#
|
107
|
-
# This middleware will only be run when pancake is set with the :demo, :production or :staging labels
|
108
|
-
#
|
109
|
-
# @example A full example
|
110
|
-
# MyClass.stack(:foo, :labels => [:staging, :development], :after => :session).use(MyMiddleware)
|
111
|
-
#
|
112
|
-
#
|
113
96
|
# @see Pancake::Middleware#use
|
114
97
|
# @api public
|
115
98
|
# @since 0.1.0
|
@@ -165,7 +148,7 @@ module Pancake
|
|
165
148
|
# the StackMiddleware class is inherited to an inner class of the same name on the child.
|
166
149
|
class StackMiddleware
|
167
150
|
# @api private
|
168
|
-
|
151
|
+
extlib_inheritable_reader :_central_mwares, :_mwares, :_before, :_after
|
169
152
|
@_central_mwares, @_before, @_after, @_mwares = [], {}, {}, {}
|
170
153
|
|
171
154
|
# @api private
|
@@ -186,17 +169,7 @@ module Pancake
|
|
186
169
|
_after.clear
|
187
170
|
end
|
188
171
|
|
189
|
-
# Get the middleware list for this StackMiddleware
|
190
|
-
#
|
191
|
-
# @param [Symbol] labels The label or list of labels to construct a stack from.
|
192
|
-
#
|
193
|
-
# @example Specified labels
|
194
|
-
# MyClass::StackMiddleware.middlewares(:production, :demo)
|
195
|
-
#
|
196
|
-
# @example No Labels Specified
|
197
|
-
# MyClass::StackMiddleware.middlewares
|
198
|
-
#
|
199
|
-
# This will include all defined middlewares in the given stack
|
172
|
+
# Get the middleware list for this StackMiddleware
|
200
173
|
#
|
201
174
|
# @return [Array<StackMiddleware>]
|
202
175
|
# An array of the middleware definitions to use in the order that they should be applied
|
@@ -207,39 +180,29 @@ module Pancake
|
|
207
180
|
# @api public
|
208
181
|
# @since 0.1.0
|
209
182
|
# @author Daniel Neighman
|
210
|
-
def middlewares
|
183
|
+
def middlewares
|
211
184
|
_central_mwares.map do |name|
|
212
|
-
map_middleware(name
|
185
|
+
map_middleware(name)
|
213
186
|
end.flatten
|
214
187
|
end
|
215
188
|
|
216
189
|
# Map the middleware for a given <name>ed middleware. Applies the before and after groups of middlewares
|
217
190
|
#
|
218
191
|
# @param [Object] name The name of the middleware to map the before and after groups to
|
219
|
-
# @param [Symbol] labels A label or list of labels to use to construct the middleware stack
|
220
|
-
#
|
221
|
-
# @example
|
222
|
-
# MyClass::StackMiddleware.map_middleware(:foo, :production, :demo)
|
223
|
-
#
|
224
|
-
# Constructs the middleware list based on the middleware named :foo, including all :before, and :after groups
|
225
|
-
#
|
226
192
|
# @return [Array<StackMiddleware>]
|
227
193
|
# Provides an array of StackMiddleware instances in the array [<before :foo>, <:foo>, <after :foo>]
|
228
194
|
#
|
229
195
|
# @api private
|
230
196
|
# @since 0.1.0
|
231
197
|
# @author Daniel Neighman
|
232
|
-
def map_middleware(name
|
198
|
+
def map_middleware(name)
|
233
199
|
result = []
|
234
200
|
_before[name] ||= []
|
235
201
|
_after[name] ||= []
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
result.flatten
|
241
|
-
end
|
242
|
-
result
|
202
|
+
result << _before[name].map{|n| map_middleware(n)}
|
203
|
+
result << _mwares[name]
|
204
|
+
result << _after[name].map{|n| map_middleware(n)}
|
205
|
+
result.flatten
|
243
206
|
end
|
244
207
|
|
245
208
|
# Provides access to a named middleware
|
@@ -268,8 +231,6 @@ module Pancake
|
|
268
231
|
# @param [Object] name a name for this middleware definition. Usually a symbol, but could be the class.
|
269
232
|
# @param [Object] stack the stack owner of this middleware.
|
270
233
|
# @param [Hash] options an options hash. Provide labels for this middleware.
|
271
|
-
# @option options [Array] :labels ([:any])
|
272
|
-
# The labels that are associated with this middleware
|
273
234
|
# @option options [Object] :before A middleware name to add this middleware before
|
274
235
|
# @option options [Object] :after A middleware name to add this middleware after
|
275
236
|
#
|
@@ -278,7 +239,6 @@ module Pancake
|
|
278
239
|
# @author Daniel Neighman
|
279
240
|
def initialize(name, stack, options = {})
|
280
241
|
@name, @stack, @options = name, stack, options
|
281
|
-
@options[:labels] ||= [:any]
|
282
242
|
end
|
283
243
|
|
284
244
|
# Delete this middleware from the current stack
|
@@ -322,19 +282,6 @@ module Pancake
|
|
322
282
|
self
|
323
283
|
end
|
324
284
|
|
325
|
-
# Checks if this middleware definition should be included from the labels given
|
326
|
-
# @param [Symbol] labels The label or list of labels to check if this middleware should be included
|
327
|
-
#
|
328
|
-
# @return [Boolean] true if this middlware should be included
|
329
|
-
#
|
330
|
-
# @api private
|
331
|
-
# @since 0.1.0
|
332
|
-
# @author Daniel Neighman
|
333
|
-
def use_for_labels?(*labels)
|
334
|
-
return true if labels.empty? || options[:labels].nil? || options[:labels].include?(:any)
|
335
|
-
!(options[:labels] & labels).empty?
|
336
|
-
end
|
337
|
-
|
338
285
|
# @api private
|
339
286
|
def dup
|
340
287
|
result = super
|
@@ -2,17 +2,17 @@ require File.join(File.dirname(__FILE__), "publish", "action_options")
|
|
2
2
|
module Pancake
|
3
3
|
module Mixins
|
4
4
|
module Publish
|
5
|
-
|
5
|
+
|
6
6
|
def self.extended(base)
|
7
7
|
base.class_eval do
|
8
|
-
|
8
|
+
extlib_inheritable_accessor :actions, :formats
|
9
9
|
self.actions = {}
|
10
10
|
self.formats = [:html]
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
# Accepts a list of symbols representing the formats each action in the
|
15
|
-
# controller will return. By default the list consists only of :html
|
15
|
+
# controller will return. By default the list consists only of :html
|
16
16
|
#
|
17
17
|
# provides :html, :xml
|
18
18
|
#
|
@@ -20,9 +20,9 @@ module Pancake
|
|
20
20
|
def provides(*args)
|
21
21
|
self.formats = args
|
22
22
|
end
|
23
|
-
|
24
|
-
# Causes the next method added immediately after it’s call to be defined
|
25
|
-
# as an action. It also generates an instance of ActionOptions, which
|
23
|
+
|
24
|
+
# Causes the next method added immediately after it’s call to be defined
|
25
|
+
# as an action. It also generates an instance of ActionOptions, which
|
26
26
|
# encapsulates all the parameters the action accepts/expects and also
|
27
27
|
# the formats that it can potentially return.
|
28
28
|
#
|
@@ -31,13 +31,13 @@ module Pancake
|
|
31
31
|
# ...
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
# The example above publishes the action "show" and configures the
|
34
|
+
# The example above publishes the action "show" and configures the
|
35
35
|
# following options for it:
|
36
36
|
#
|
37
37
|
# - The parameter 'id' will be coerced into an Integer
|
38
38
|
# - It also must not be blank, it is required.
|
39
39
|
#
|
40
|
-
# The publish declaration can also create much more sophisticated
|
40
|
+
# The publish declaration can also create much more sophisticated
|
41
41
|
# constraints. You can declare a parameter as optional, give it a default
|
42
42
|
# value if it is missing.
|
43
43
|
#
|
@@ -52,9 +52,9 @@ module Pancake
|
|
52
52
|
# #publish can also handle the specification of formats for an action. It
|
53
53
|
# accepts an additional two options, :provides and :only_provides.
|
54
54
|
#
|
55
|
-
# To the list of globally declared formats, :provides adds additional
|
55
|
+
# To the list of globally declared formats, :provides adds additional
|
56
56
|
# formats to an action.
|
57
|
-
#
|
57
|
+
#
|
58
58
|
# publish :provides => :xml
|
59
59
|
#
|
60
60
|
# The :only_provides option overrides any globally declared formats for an
|
@@ -66,9 +66,9 @@ module Pancake
|
|
66
66
|
def publish(opts = {})
|
67
67
|
@pending_publication_opts = opts
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Used when declaring configuration for parameters in the publish
|
71
|
-
# declaration. It returns an array of the type, default value an
|
71
|
+
# declaration. It returns an array of the type, default value an
|
72
72
|
# additional options.
|
73
73
|
#
|
74
74
|
# @param type [Object] The
|
@@ -78,7 +78,7 @@ module Pancake
|
|
78
78
|
# - :date
|
79
79
|
# - :string
|
80
80
|
#
|
81
|
-
# For details on the options supported by each type, please see the
|
81
|
+
# For details on the options supported by each type, please see the
|
82
82
|
# corresponding methods declared in the ActionOptions class. These methods
|
83
83
|
# are named as the type, prefixed with 'validate_and_coerce_'
|
84
84
|
#
|
@@ -96,9 +96,9 @@ module Pancake
|
|
96
96
|
def as(type, default = :req, opts = {})
|
97
97
|
[type, default, opts]
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
# Takes a parameters hash, and validates each entry against the options
|
101
|
-
# defined for this action. It will flag required params when missing,
|
101
|
+
# defined for this action. It will flag required params when missing,
|
102
102
|
# insert defaults or coerce values into the desired type. It mutates
|
103
103
|
# the params hash itself.
|
104
104
|
#
|
@@ -106,10 +106,10 @@ module Pancake
|
|
106
106
|
def validate_and_coerce_params(action, params)
|
107
107
|
actions[action].validate_and_coerce(params)
|
108
108
|
end
|
109
|
-
|
110
|
-
|
111
|
-
# This hook is used in conjunction with the #publish method to expose
|
112
|
-
# instance methods as actions. Obviously, it should never be called
|
109
|
+
|
110
|
+
|
111
|
+
# This hook is used in conjunction with the #publish method to expose
|
112
|
+
# instance methods as actions. Obviously, it should never be called
|
113
113
|
# directly :)
|
114
114
|
#
|
115
115
|
# @api private
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'pancake/mixins/render/template'
|
2
|
-
require 'pancake/mixins/render/render'
|
3
2
|
require 'pancake/mixins/render/view_context'
|
4
3
|
module Pancake
|
5
4
|
module Mixins
|
@@ -10,11 +9,9 @@ module Pancake
|
|
10
9
|
base.class_eval do
|
11
10
|
extend Pancake::Mixins::Render::ClassMethods
|
12
11
|
include Pancake::Mixins::Render::InstanceMethods
|
13
|
-
include Pancake::Mixins::RequestHelper
|
14
12
|
|
15
|
-
class
|
16
|
-
|
17
|
-
end
|
13
|
+
class base::ViewContext < Pancake::Mixins::Render::ViewContext; end
|
14
|
+
|
18
15
|
inheritable_inner_classes :ViewContext
|
19
16
|
|
20
17
|
unless ancestors.include?(Pancake::Paths)
|
@@ -33,16 +30,19 @@ module Pancake
|
|
33
30
|
@_template_cache ||= {}
|
34
31
|
end
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
33
|
+
# Allows you to set path label for the templates for this class
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# MyClass.push_paths(:my_templates, "somewhere", "**/*")
|
37
|
+
#
|
38
|
+
# MyClass._template_path_name #=> :my_templates
|
39
|
+
# @api public
|
40
|
+
def _template_path_name(opts = {})
|
41
|
+
opts[:template_path_name] || :views
|
42
|
+
end
|
43
43
|
|
44
|
-
|
45
|
-
|
44
|
+
def _find_template(name, opts = {})
|
45
|
+
template(name, opts)
|
46
46
|
end
|
47
47
|
|
48
48
|
def _view_context_cache
|
@@ -52,26 +52,55 @@ module Pancake
|
|
52
52
|
|
53
53
|
def _find_view_context_class_for(template)
|
54
54
|
_view_context_cache[template] ||= begin
|
55
|
-
|
55
|
+
self::ViewContext
|
56
56
|
end
|
57
57
|
_view_context_cache[template]
|
58
58
|
end
|
59
59
|
|
60
|
-
def _renderer_and_view_context_class_for(
|
61
|
-
[
|
60
|
+
def _renderer_and_view_context_class_for(tplate)
|
61
|
+
[template(tplate), _find_view_context_class_for(tplate)]
|
62
62
|
end
|
63
63
|
|
64
|
-
def _template_name_for(name, opts
|
65
|
-
|
66
|
-
"#{name}.#{opts[:format]}"
|
64
|
+
def _template_name_for(name, opts)
|
65
|
+
"#{name}"
|
67
66
|
end
|
68
67
|
|
69
|
-
def template(
|
70
|
-
case
|
71
|
-
when String, Symbol
|
72
|
-
_find_template(_template_name_for(name_or_template, opts))
|
68
|
+
def template(name, opts = {})
|
69
|
+
case name
|
73
70
|
when Template
|
74
|
-
|
71
|
+
name
|
72
|
+
when String, Symbol
|
73
|
+
|
74
|
+
template_names = case __template = _template_name_for(name, opts)
|
75
|
+
when String, Symbol
|
76
|
+
[__template]
|
77
|
+
when Array
|
78
|
+
__template
|
79
|
+
when Proc
|
80
|
+
[__template.call(opts)].flatten
|
81
|
+
else
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
renderer = _template_cache[template_names]
|
86
|
+
return renderer if renderer
|
87
|
+
|
88
|
+
unique_paths = unique_paths_for(_template_path_name(opts), :invert => true)
|
89
|
+
|
90
|
+
renderer_path = nil
|
91
|
+
template_name = nil
|
92
|
+
|
93
|
+
template_names.detect do |tn|
|
94
|
+
unique_paths.detect do |path|
|
95
|
+
if path.last =~ %r[^\/?(#{tn})\.\w+$]
|
96
|
+
template_name = tn
|
97
|
+
renderer_path = path.join
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
raise TemplateNotFound unless renderer_path
|
103
|
+
_template_cache[template_names] = Template.new(template_name, self, renderer_path)
|
75
104
|
else
|
76
105
|
nil
|
77
106
|
end
|
@@ -87,15 +116,15 @@ module Pancake
|
|
87
116
|
def render(*args)
|
88
117
|
opts = Hash === args.last ? args.pop : {}
|
89
118
|
name = args.shift
|
90
|
-
|
119
|
+
template = self.class.template(name, opts)
|
91
120
|
return opts[:text] if opts[:text]
|
92
121
|
|
93
122
|
# Get the view context for the tempalte
|
94
|
-
template, vc_class = self.class._renderer_and_view_context_class_for(
|
123
|
+
template, vc_class = self.class._renderer_and_view_context_class_for(template)
|
95
124
|
|
96
125
|
yield v if block_given?
|
97
126
|
|
98
|
-
view_context = vc_class.new(
|
127
|
+
view_context = vc_class.new(self)
|
99
128
|
view_context_before_render(view_context)
|
100
129
|
view_context.render(template, opts)
|
101
130
|
end
|
@@ -111,7 +140,7 @@ module Pancake
|
|
111
140
|
# Get the view context for the tempalte
|
112
141
|
template, vc_class = self.class._renderer_and_view_context_class_for(partial_name)
|
113
142
|
|
114
|
-
view_context = vc_class.new(
|
143
|
+
view_context = vc_class.new(self)
|
115
144
|
view_context_before_render(view_context)
|
116
145
|
|
117
146
|
out = ""
|
@@ -129,48 +158,9 @@ module Pancake
|
|
129
158
|
end
|
130
159
|
|
131
160
|
def template(name_or_template, opts = {})
|
132
|
-
opts[:format] ||= content_type
|
133
161
|
self.class.template(name_or_template, opts)
|
134
162
|
end
|
135
163
|
|
136
|
-
def negotiate_content_type!(*allowed_types)
|
137
|
-
return content_type if content_type
|
138
|
-
|
139
|
-
allowed_types = allowed_types.flatten
|
140
|
-
opts = allowed_types.pop if allowed_types.last.kind_of?(Hash)
|
141
|
-
if opts[:format]
|
142
|
-
cont, ct, mt = Pancake::MimeTypes.negotiate_by_extension(opts[:format].to_s, allowed_types)
|
143
|
-
else
|
144
|
-
env["HTTP_ACCEPT"] ||= "*/*"
|
145
|
-
cont, ct, mt = Pancake::MimeTypes.negotiate_accept_type(env["HTTP_ACCEPT"], allowed_types)
|
146
|
-
end
|
147
|
-
|
148
|
-
raise Errors::NotAcceptable unless cont
|
149
|
-
|
150
|
-
headers["Content-Type"] = ct
|
151
|
-
self.mime_type = mt
|
152
|
-
self.content_type = cont
|
153
|
-
cont
|
154
|
-
end
|
155
|
-
|
156
|
-
def content_type
|
157
|
-
env['pancake.request.format']
|
158
|
-
end
|
159
|
-
|
160
|
-
def content_type=(format)
|
161
|
-
env['pancake.request.format'] = format
|
162
|
-
end
|
163
|
-
|
164
|
-
def mime_type
|
165
|
-
env['pancake.request.mime']
|
166
|
-
end
|
167
|
-
|
168
|
-
def mime_type=(mime)
|
169
|
-
env['pancake.request.mime'] = mime
|
170
|
-
end
|
171
|
-
|
172
|
-
|
173
|
-
|
174
164
|
# A place holder method for any implementor that wants
|
175
165
|
# to configure the view context prior to rendering occuring
|
176
166
|
# any time this method is overwritten, it should call super!
|
@@ -180,8 +170,8 @@ module Pancake
|
|
180
170
|
end
|
181
171
|
|
182
172
|
private
|
173
|
+
# @api_overwritable
|
183
174
|
def _template_name_for(name, opts = {})
|
184
|
-
opts[:format] ||= content_type
|
185
175
|
self.class._template_name_for(name, opts)
|
186
176
|
end
|
187
177
|
|