Agile Web Development with Rails, Edition 5
11.2 Iteration F2: Creating an AJAX-Based Cart
10.4 Playtime
11.1 Iteration F1: Moving the Cart
Refactor the cart view into partials, and reference the result from the layout.
Create a "partial" view, for just one line item
edit app/views/line_items/_line_item.html.erb
<tr>
<td class="quantity"><%= line_item.quantity %></td>
<td><%= line_item.product.title %></td>
<td class="price"><%= number_to_currency(line_item.total_price) %></td>
</tr>
Replace that portion of the view with a callout to the partial
edit app/views/carts/show.html.erb
<article>
<% if notice %>
<aside id="notice"><%= notice %></aside>
<% end %>
<h2>Your Cart</h2>
<table>
<%= render(@cart.line_items) %>
<tfoot>
<tr>
<th colspan="2">Total:</th>
<td class="price"><%= number_to_currency(@cart.total_price) %></td>
</tr>
</tfoot>
</table>
<%= button_to 'Empty cart', @cart,
method: :delete,
data: { confirm: 'Are you sure?' } %>
</article>
Make a copy as a partial for the cart controller
cp app/views/carts/show.html.erb app/views/carts/_cart.html.erb
Modify the copy to reference the (sub)partial and take input from @cart
edit app/views/carts/_cart.html.erb
<article>
<% if notice %>
<aside id="notice"><%= notice %></aside>
<% end %>
<h2>Your Cart</h2>
<table>
<%= render(cart.line_items) %>
<tfoot>
<tr>
<th colspan="2">Total:</th>
<td class="price"><%= number_to_currency(cart.total_price) %></td>
</tr>
</tfoot>
</table>
<%= button_to 'Empty cart', cart,
method: :delete,
data: { confirm: 'Are you sure?' } %>
</article>
Keep things DRY
edit app/views/carts/show.html.erb
<%= render @cart %>
Reference the partial from the layout.
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
'data-turbolinks-track': 'reload' %>
</head>
<body>
<header class="main">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<h1><%= @page_title %></h1>
</header>
<section class="content">
<nav class="side_nav">
<div id="cart" class="carts">
<%= render @cart %>
</div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/questions">Questions</a></li>
<li><a href="/news">News</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main class='<%= controller.controller_name %>'>
<%= yield %>
</main>
</section>
</body>
</html>
Insert a call in the controller to find the cart
edit app/controllers/store_controller.rb
class StoreController < ApplicationController
include CurrentCart
before_action :set_cart
def index
@products = Product.order(:title)
end
end
Add a small bit of style.
edit app/assets/stylesheets/application.scss
/*
* This is a manifest file that'll be compiled into application.css, which will
* include all the files listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any
* plugin's vendor/assets/stylesheets directory can be referenced here using a
* relative path.
*
* You're free to add application-wide styles to this file and they'll appear
* at the bottom of the compiled file so the styles you add here take
* precedence over styles defined in any other CSS/SCSS files in this
* directory. Styles in this file should be added after the last require_*
* statement. It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
body {
margin: 0;
padding: 0;
}
header.main {
text-align: center; // center on mobile
@media (min-width: 30em) {
text-align: left; // left align on desktop
}
background: #282;
margin: 0;
h1 {
display: none;
}
}
.notice, #notice {
background: #ffb;
border-radius: 0.5em;
border: solid 0.177em #882;
color: #882;
font-weight: bold;
margin-bottom: 1em;
padding: 1em 1.414em;
text-align: center;
}
.content {
margin: 0;
padding: 0;
display: flex;
display: -webkit-flex;
flex-direction: column; // mobile is horizontally laid out
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
@media (min-width: 30em) {
flex-direction: row; // desktop is vertically laid out
-webkit-box-orient: horizontal;
}
nav {
padding-bottom: 1em;
background: #141;
text-align: center; // mobile has centered nav
@media (min-width: 30em) {
text-align: left; // desktop nav is left-aligned
padding: 1em; // and needs more padding
}
#cart {
article {
h2 {
margin-top: 0;
}
background: white;
border-radius: 0.5em;
margin: 1em;
padding: 1.414em;
@media (min-width: 30em) {
margin: 0; // desktop doesn't need this margin
}
}
}
ul {
list-style: none;
margin: 0;
padding: 0;
@media (min-width: 30em) {
padding-right: 1em; // give desktop some extra space
}
li {
margin: 0;
padding: 0.5em;
text-transform: uppercase;
letter-spacing: 0.354em;
a {
color: #bfb;
text-decoration: none;
}
a:hover {
background: none;
color: white;
}
}
}
}
main {
padding: 0.5em;
}
}
Change the redirect to be back to the store.
edit app/controllers/line_items_controller.rb
def create
product = Product.find(params[:product_id])
@line_item = @cart.add_product(product)
respond_to do |format|
if @line_item.save
format.html { redirect_to store_index_url }
format.json { render :show,
status: :created, location: @line_item }
else
format.html { render :new }
format.json { render json: @line_item.errors,
status: :unprocessable_entity }
end
end
end
Purchase another product.
get /
Your Cart
2
Rails, Angular, Postgres, and Bootstrap
$90.00
1
Seven Mobile Apps in Seven Weeks
$26.00
Total:
$116.00
Your Pragmatic Catalog
Rails, Angular, Postgres, and Bootstrap
Powerful, Effective, and Efficient Full-Stack Web Development
As a Rails developer, you care about user experience and performance,
but you also want simple and maintainable code. Achieve all that by
embracing the full stack of web development, from styling with
Bootstrap, building an interactive user interface with AngularJS, to
storing data quickly and reliably in PostgreSQL. Take a holistic view of
full-stack development to create usable, high-performing applications,
and learn to use these technologies effectively in a Ruby on Rails
environment.
$45.00
Ruby Performance Optimization
Why Ruby Is Slow, and How to Fix It
You don’t have to accept slow Ruby or Rails performance. In this
comprehensive guide to Ruby optimization, you’ll learn how to write
faster Ruby code—but that’s just the beginning. See exactly what makes
Ruby and Rails code slow, and how to fix it. Alex Dymo will guide you
through perils of memory and CPU optimization, profiling, measuring,
performance testing, garbage collection, and tuning. You’ll find that
all those “hard” things aren’t so difficult after all, and your code
will run orders of magnitude faster.
$46.00
Seven Mobile Apps in Seven Weeks
Native Apps, Multiple Platforms
Answer the question “Can we build this for ALL the devices?” with a
resounding YES. This book will help you get there with a real-world
introduction to seven platforms, whether you’re new to mobile or an
experienced developer needing to expand your options. Plus, you’ll find
out which cross-platform solution makes the most sense for your needs.
$26.00
post /line_items?product_id=3
You are being
redirected .
get http://localhost:3000/
Your Cart
2
Rails, Angular, Postgres, and Bootstrap
$90.00
2
Seven Mobile Apps in Seven Weeks
$52.00
Total:
$142.00
Your Pragmatic Catalog
Rails, Angular, Postgres, and Bootstrap
Powerful, Effective, and Efficient Full-Stack Web Development
As a Rails developer, you care about user experience and performance,
but you also want simple and maintainable code. Achieve all that by
embracing the full stack of web development, from styling with
Bootstrap, building an interactive user interface with AngularJS, to
storing data quickly and reliably in PostgreSQL. Take a holistic view of
full-stack development to create usable, high-performing applications,
and learn to use these technologies effectively in a Ruby on Rails
environment.
$45.00
Ruby Performance Optimization
Why Ruby Is Slow, and How to Fix It
You don’t have to accept slow Ruby or Rails performance. In this
comprehensive guide to Ruby optimization, you’ll learn how to write
faster Ruby code—but that’s just the beginning. See exactly what makes
Ruby and Rails code slow, and how to fix it. Alex Dymo will guide you
through perils of memory and CPU optimization, profiling, measuring,
performance testing, garbage collection, and tuning. You’ll find that
all those “hard” things aren’t so difficult after all, and your code
will run orders of magnitude faster.
$46.00
Seven Mobile Apps in Seven Weeks
Native Apps, Multiple Platforms
Answer the question “Can we build this for ALL the devices?” with a
resounding YES. This book will help you get there with a real-world
introduction to seven platforms, whether you’re new to mobile or an
experienced developer needing to expand your options. Plus, you’ll find
out which cross-platform solution makes the most sense for your needs.
$26.00
Run tests... oops.
rails test
Run options: --seed 33674
# Running:
.......E
Error:
CartsControllerTest#test_should_get_index:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/carts_controller_test.rb:9:in `block in <class:CartsControllerTest>'
bin/rails test test/controllers/carts_controller_test.rb:8
.E
Error:
ProductsControllerTest#test_should_show_product:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/products_controller_test.rb:45:in `block in <class:ProductsControllerTest>'
bin/rails test test/controllers/products_controller_test.rb:44
E
Error:
ProductsControllerTest#test_should_get_edit:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/products_controller_test.rb:50:in `block in <class:ProductsControllerTest>'
bin/rails test test/controllers/products_controller_test.rb:49
E
Error:
ProductsControllerTest#test_should_get_index:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/products_controller_test.rb:14:in `block in <class:ProductsControllerTest>'
bin/rails test test/controllers/products_controller_test.rb:13
....E
Error:
ProductsControllerTest#test_should_get_new:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/products_controller_test.rb:19:in `block in <class:ProductsControllerTest>'
bin/rails test test/controllers/products_controller_test.rb:18
.....E
Error:
LineItemsControllerTest#test_should_get_new:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/line_items_controller_test.rb:14:in `block in <class:LineItemsControllerTest>'
bin/rails test test/controllers/line_items_controller_test.rb:13
E
Error:
LineItemsControllerTest#test_should_get_index:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/line_items_controller_test.rb:9:in `block in <class:LineItemsControllerTest>'
bin/rails test test/controllers/line_items_controller_test.rb:8
..E
Error:
LineItemsControllerTest#test_should_show_line_item:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/line_items_controller_test.rb:38:in `block in <class:LineItemsControllerTest>'
bin/rails test test/controllers/line_items_controller_test.rb:37
.E
Error:
LineItemsControllerTest#test_should_get_edit:
ActionView::Template::Error: 'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
app/views/layouts/application.html.erb:24:in `_app_views_layouts_application_html_erb___2035715321172067450_38693600'
test/controllers/line_items_controller_test.rb:43:in `block in <class:LineItemsControllerTest>'
bin/rails test test/controllers/line_items_controller_test.rb:42
.
Finished in 0.496554s, 60.4163 runs/s, 108.7494 assertions/s.
30 runs, 54 assertions, 0 failures, 9 errors, 0 skips
Verify that the products page is indeed broken
get /products
HTTP Response Code: 500
ArgumentError in
Products#index
Showing /home/rubys/git/awdwr/edition4/work/depot/app/views/layouts/application.html.erb where line #24 raised:
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
Extracted source (around line #483 ):
481
482
483
484
485
486
object.to_partial_path
else
raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
end
if @view.prefix_partial_path_with_controller_namespace
Extracted source (around line #387 ):
385
386
387
388
389
390
@path = paths.uniq.one? ? paths.first : nil
else
@path = partial_path
end
end
Extracted source (around line #298 ):
296
297
298
299
300
301
def render(context, options, block)
setup(context, options, block)
@template = find_partial
@lookup_context.rendered_format ||= begin
Extracted source (around line #49 ):
47
48
49
50
51
52
# Direct access to partial rendering.
def render_partial(context, options, &block) #:nodoc:
PartialRenderer.new(@lookup_context).render(context, options, block)
end
def cache_hits # :nodoc:
Extracted source (around line #37 ):
35
36
37
38
39
40
end
else
view_renderer.render_partial(self, partial: options, locals: locals, &block)
end
end
Extracted source (around line #24 ):
22
23
24
25
26
27
<!-- START_HIGHLIGHT -->
<div id="cart" class="carts">
<%= render @cart %>
</div>
<!-- END_HIGHLIGHT -->
<ul>
Extracted source (around line #159 ):
157
158
159
160
161
162
instrument_render_template do
compile!(view)
view.send(method_name, locals, buffer, &block)
end
rescue => e
handle_render_error(view, e)
Extracted source (around line #170 ):
168
169
170
171
172
173
instrumenter.instrument(name, payload) { yield payload if block_given? }
else
yield payload if block_given?
end
end
Extracted source (around line #354 ):
352
353
354
355
356
357
def instrument_render_template(&block)
ActiveSupport::Notifications.instrument("!render_template.action_view".freeze, instrument_payload, &block)
end
def instrument_payload
Extracted source (around line #157 ):
155
156
157
158
159
160
# consume this in production. This is only slow if it's being listened to.
def render(view, locals, buffer = nil, &block)
instrument_render_template do
compile!(view)
view.send(method_name, locals, buffer, &block)
end
Extracted source (around line #66 ):
64
65
66
67
68
69
view = @view
view.view_flow.set(:layout, content)
layout.render(view, locals) { |*name| view._layout_for(*name) }
else
content
end
Extracted source (around line #52 ):
50
51
52
53
54
55
view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout|
instrument(:template, identifier: template.identifier, layout: layout.try(:virtual_path)) do
template.render(view, locals) { |*name| view._layout_for(*name) }
end
Extracted source (around line #16 ):
14
15
16
17
18
19
@lookup_context.rendered_format ||= (template.formats.first || formats.first)
render_template(template, options[:layout], options[:locals])
end
private
Extracted source (around line #44 ):
42
43
44
45
46
47
# Direct access to template rendering.
def render_template(context, options) #:nodoc:
TemplateRenderer.new(@lookup_context).render(context, options)
end
# Direct access to partial rendering.
Extracted source (around line #25 ):
23
24
25
26
27
28
render_partial(context, options)
else
render_template(context, options)
end
end
Extracted source (around line #103 ):
101
102
103
104
105
106
lookup_context.variants = variant if variant
view_renderer.render(context, options)
end
# Assign the rendered format to look up context.
Extracted source (around line #219 ):
217
218
219
220
221
222
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
else
super
end
end
end
Extracted source (around line #84 ):
82
83
84
85
86
87
def render_to_body(options = {})
_process_options(options)
_render_template(options)
end
def rendered_format
Extracted source (around line #52 ):
50
51
52
53
54
55
def render_to_body(options = {})
super || _render_in_priorities(options) || " "
end
private
Extracted source (around line #142 ):
140
141
142
143
144
145
# <tt>ActionView::Rendering.render_to_body</tt>, if present.
def render_to_body(options)
_render_to_body_with_renderer(options) || super
end
def _render_to_body_with_renderer(options)
Extracted source (around line #25 ):
23
24
25
26
27
28
def render(*args, &block)
options = _normalize_render(*args, &block)
rendered_body = render_to_body(options)
if options[:html]
_set_html_content_type
else
Extracted source (around line #36 ):
34
35
36
37
38
39
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if response_body
super
end
# Overwrite render_to_string because body can now be set to a Rack body.
Extracted source (around line #46 ):
44
45
46
47
48
49
render_output = nil
self.view_runtime = cleanup_view_runtime do
Benchmark.ms { render_output = super }
end
render_output
end
Extracted source (around line #14 ):
12
13
14
15
16
# # => 0.074
def ms
1000 * realtime { yield }
end
end
Extracted source (around line #308 ):
306
307
308
309
310
311
def realtime # :yield:
r0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
yield
Process.clock_gettime(Process::CLOCK_MONOTONIC) - r0
end
Extracted source (around line #14 ):
12
13
14
15
16
# # => 0.074
def ms
1000 * realtime { yield }
end
end
Extracted source (around line #46 ):
44
45
46
47
48
49
render_output = nil
self.view_runtime = cleanup_view_runtime do
Benchmark.ms { render_output = super }
end
render_output
end
Extracted source (around line #87 ):
85
86
87
88
89
90
# end
def cleanup_view_runtime # :doc:
yield
end
# Every time after an action is processed, this method is invoked
Extracted source (around line #31 ):
29
30
31
32
33
34
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
self.db_runtime = (db_runtime || 0) + db_rt_before_render
runtime = super
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
self.db_runtime += db_rt_after_render
runtime - db_rt_after_render
Extracted source (around line #45 ):
43
44
45
46
47
48
def render(*args)
render_output = nil
self.view_runtime = cleanup_view_runtime do
Benchmark.ms { render_output = super }
end
render_output
Extracted source (around line #35 ):
33
34
35
36
37
38
def default_render(*args)
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
render(*args)
elsif any_templates?(action_name.to_s, _prefixes)
message = "#{self.class.name}\##{action_name} is missing a template " \
"for this request format and variant.\n" \
Extracted source (around line #6 ):
4
5
6
7
8
9
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
super.tap { default_render unless performed? }
end
def default_render(*args)
Extracted source (around line #6 ):
4
5
6
7
8
9
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
super.tap { default_render unless performed? }
end
def default_render(*args)
Extracted source (around line #6 ):
4
5
6
7
8
9
module BasicImplicitRender # :nodoc:
def send_action(method, *args)
super.tap { default_render unless performed? }
end
def default_render(*args)
Extracted source (around line #194 ):
192
193
194
195
196
197
# which is *not* necessarily the same as the action name.
def process_action(method_name, *args)
send_action(method_name, *args)
end
# Actually call the method associated with the action. Override
Extracted source (around line #30 ):
28
29
30
31
32
33
def process_action(*) #:nodoc:
self.formats = request.formats.map(&:ref).compact
super
end
# Check for double render errors and set the content_type after rendering.
Extracted source (around line #42 ):
40
41
42
43
44
45
def process_action(*args)
run_callbacks(:process_action) do
super
end
end
Extracted source (around line #132 ):
130
131
132
133
134
135
if next_sequence.final?
next_sequence.invoke_before(env)
env.value = !env.halted && (!block_given? || yield)
next_sequence.invoke_after(env)
env.value
else
Extracted source (around line #41 ):
39
40
41
42
43
44
# <tt>process_action</tt> callbacks around the normal behavior.
def process_action(*args)
run_callbacks(:process_action) do
super
end
end
Extracted source (around line #22 ):
20
21
22
23
24
25
private
def process_action(*args)
super
rescue Exception => exception
request.env["action_dispatch.show_detailed_exceptions"] ||= show_detailed_exceptions?
rescue_with_handler(exception) || raise
Extracted source (around line #34 ):
32
33
34
35
36
37
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
begin
result = super
payload[:status] = response.status
result
ensure
Extracted source (around line #168 ):
166
167
168
169
170
171
def instrument(name, payload = {})
if notifier.listening?(name)
instrumenter.instrument(name, payload) { yield payload if block_given? }
else
yield payload if block_given?
end
Extracted source (around line #23 ):
21
22
23
24
25
26
listeners_state = start name, payload
begin
yield payload
rescue Exception => e
payload[:exception] = [e.class.name, e.message]
payload[:exception_object] = e
Extracted source (around line #168 ):
166
167
168
169
170
171
def instrument(name, payload = {})
if notifier.listening?(name)
instrumenter.instrument(name, payload) { yield payload if block_given? }
else
yield payload if block_given?
end
Extracted source (around line #32 ):
30
31
32
33
34
35
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
begin
result = super
payload[:status] = response.status
Extracted source (around line #256 ):
254
255
256
257
258
259
request.filtered_parameters.merge! wrapped_filtered_hash
end
super
end
private
Extracted source (around line #24 ):
22
23
24
25
26
27
# and it won't be cleaned up by the method below.
ActiveRecord::LogSubscriber.reset_runtime
super
end
def cleanup_view_runtime
Extracted source (around line #134 ):
132
133
134
135
136
137
@_response_body = nil
process_action(action_name, *args)
end
# Delegates to the class' ::controller_path
Extracted source (around line #32 ):
30
31
32
33
34
35
def process(*) #:nodoc:
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
super
ensure
I18n.config = old_config
end
Extracted source (around line #191 ):
189
190
191
192
193
194
set_request!(request)
set_response!(response)
process(name)
request.commit_flash
to_a
end
Extracted source (around line #254 ):
252
253
254
255
256
257
middleware_stack.build(name) { |env| new.dispatch(name, req, res) }.call req.env
else
new.dispatch(name, req, res)
end
end
end
Extracted source (around line #52 ):
50
51
52
53
54
55
def dispatch(controller, action, req, res)
controller.dispatch(action, req, res)
end
end
Extracted source (around line #34 ):
32
33
34
35
36
37
controller = controller req
res = controller.make_response! req
dispatch(controller, params[:action], req, res)
rescue ActionController::RoutingError
if @raise_on_name_error
raise
Extracted source (around line #52 ):
50
51
52
53
54
55
req.path_parameters = set_params.merge parameters
status, headers, body = route.app.serve(req)
if "pass" == headers["X-Cascade"]
req.script_name = script_name
Extracted source (around line #35 ):
33
34
35
36
37
38
def serve(req)
find_routes(req).each do |match, parameters, route|
set_params = req.path_parameters
path_info = req.path_info
script_name = req.script_name
Extracted source (around line #35 ):
33
34
35
36
37
38
def serve(req)
find_routes(req).each do |match, parameters, route|
set_params = req.path_parameters
path_info = req.path_info
script_name = req.script_name
Extracted source (around line #830 ):
828
829
830
831
832
833
req = make_request(env)
req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
@router.serve(req)
end
def recognize_path(path, environment = {})
Extracted source (around line #25 ):
23
24
25
26
27
28
def call(env)
status, headers, body = @app.call(env)
if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
original_body = body
Extracted source (around line #25 ):
23
24
25
26
27
28
case env[REQUEST_METHOD]
when "GET", "HEAD"
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
if status == 200 && fresh?(env, headers)
status = 304
Extracted source (around line #12 ):
10
11
12
13
14
15
def call(env)
status, headers, body = @app.call(env)
if env[REQUEST_METHOD] == HEAD
[
Extracted source (around line #232 ):
230
231
232
233
234
235
req = make_request env
prepare_session(req)
status, headers, body = app.call(req.env)
res = Rack::Response::Raw.new status, headers
commit_session(req, res)
[status, headers, body]
Extracted source (around line #226 ):
224
225
226
227
228
229
def call(env)
context(env)
end
def context(env, app=@app)
Extracted source (around line #663 ):
661
662
663
664
665
666
request = ActionDispatch::Request.new env
status, headers, body = @app.call(env)
if request.have_cookie_jar?
cookie_jar = request.cookie_jar
Extracted source (around line #558 ):
556
557
558
559
560
561
@last_check = mtime
end
@app.call(env)
end
private
Extracted source (around line #28 ):
26
27
28
29
30
31
result = run_callbacks :call do
begin
@app.call(env)
rescue => error
end
end
Extracted source (around line #98 ):
96
97
98
99
100
101
if callbacks.empty?
yield if block_given?
else
env = Filters::Environment.new(self, false, nil)
next_sequence = callbacks.compile
Extracted source (around line #26 ):
24
25
26
27
28
29
def call(env)
error = nil
result = run_callbacks :call do
begin
@app.call(env)
rescue => error
Extracted source (around line #14 ):
12
13
14
15
16
17
state = @executor.run!
begin
response = @app.call(env)
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
ensure
state.complete! unless returned
Extracted source (around line #61 ):
59
60
61
62
63
64
def call(env)
request = ActionDispatch::Request.new env
_, headers, body = response = @app.call(env)
if headers["X-Cascade"] == "pass"
body.close if body.respond_to?(:close)
Extracted source (around line #137 ):
135
136
137
138
139
140
def call_app(env)
@app.call(env)
rescue => e
throw :app_exception, e
end
Extracted source (around line #30 ):
28
29
30
31
32
33
end
status, headers, body = call_app(env)
if session = Session.from(Thread.current) and acceptable_content_type?(headers)
response = Response.new(body, status, headers)
Extracted source (around line #20 ):
18
19
20
21
22
23
def call(env)
app_exception = catch :app_exception do
request = create_regular_or_whiny_request(env)
return call_app(env) unless request.from_whitelisted_ip?
Extracted source (around line #20 ):
18
19
20
21
22
23
def call(env)
app_exception = catch :app_exception do
request = create_regular_or_whiny_request(env)
return call_app(env) unless request.from_whitelisted_ip?
Extracted source (around line #33 ):
31
32
33
34
35
36
def call(env)
request = ActionDispatch::Request.new env
@app.call(env)
rescue Exception => exception
if request.show_exceptions?
render_exception(request, exception)
Extracted source (around line #38 ):
36
37
38
39
40
41
instrumenter.start "request.action_dispatch", request: request
logger.info { started_request_message(request) }
resp = @app.call(env)
resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
resp
rescue Exception
Extracted source (around line #26 ):
24
25
26
27
28
29
if logger.respond_to?(:tagged)
logger.tagged(compute_tags(request)) { call_app(request, env) }
else
call_app(request, env)
end
Extracted source (around line #71 ):
69
70
71
72
73
74
def tagged(*tags)
formatter.tagged(*tags) { yield self }
end
def flush
Extracted source (around line #28 ):
26
27
28
29
30
31
def tagged(*tags)
new_tags = push_tags(*tags)
yield self
ensure
pop_tags(new_tags.size)
end
Extracted source (around line #71 ):
69
70
71
72
73
74
def tagged(*tags)
formatter.tagged(*tags) { yield self }
end
def flush
Extracted source (around line #26 ):
24
25
26
27
28
29
if logger.respond_to?(:tagged)
logger.tagged(compute_tags(request)) { call_app(request, env) }
else
call_app(request, env)
end
Extracted source (around line #13 ):
11
12
13
14
15
16
::Rails.logger.silence { @app.call(env) }
else
@app.call(env)
end
end
end
Extracted source (around line #81 ):
79
80
81
82
83
84
req = ActionDispatch::Request.new env
req.remote_ip = GetIp.new(req, check_ip, proxies)
@app.call(req.env)
end
# The GetIp class exists as a way to defer processing of the request data
Extracted source (around line #27 ):
25
26
27
28
29
30
req = ActionDispatch::Request.new env
req.request_id = make_request_id(req.x_request_id)
@app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = req.request_id }
end
private
Extracted source (around line #22 ):
20
21
22
23
24
25
end
@app.call(env)
end
def method_override(env)
Extracted source (around line #22 ):
20
21
22
23
24
25
def call(env)
start_time = Utils.clock_time
status, headers, body = @app.call(env)
request_time = Utils.clock_time - start_time
unless headers.has_key?(@header_name)
Extracted source (around line #29 ):
27
28
29
30
31
32
def call(env)
LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
response = @app.call(env)
response[2] = ::Rack::BodyProxy.new(response[2]) do
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
end
Extracted source (around line #14 ):
12
13
14
15
16
17
state = @executor.run!
begin
response = @app.call(env)
returned = response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
ensure
state.complete! unless returned
Extracted source (around line #127 ):
125
126
127
128
129
130
end
@app.call(req.env)
end
end
end
Extracted source (around line #111 ):
109
110
111
112
113
114
def call(env)
status, headers, body = @app.call(env)
if body.respond_to?(:to_path)
case type = variation(env)
when 'X-Accel-Redirect'
Extracted source (around line #524 ):
522
523
524
525
526
527
def call(env)
req = build_request env
app.call req.env
end
# Defines additional Rack env configuration that is added on each call.
Extracted source (around line #225 ):
223
224
225
226
227
228
def call(env)
env[Const::PUMA_CONFIG] = @config
@app.call(env)
end
end
Extracted source (around line #605 ):
603
604
605
606
607
608
begin
begin
status, headers, res_body = @app.call(env)
return :async if req.hijacked
Extracted source (around line #437 ):
435
436
437
438
439
440
while true
case handle_request(client, buffer)
when false
return
when :async
Extracted source (around line #301 ):
299
300
301
302
303
304
else
if process_now
process_client client, buffer
else
client.set_timeout @first_data_timeout
@reactor.add client
Extracted source (around line #120 ):
118
119
120
121
122
123
begin
block.call(work, *extra)
rescue Exception => e
STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
end
Rails.root: /home/rubys/git/awdwr/edition4/work/depot
Request
Parameters :
None
_csrf_token: "Ik0swwjSqEegZDNnoKYWQ7xwe7XTNxccCP4OLt5Ny0c="
cart_id: 2
session_id: "de81ce2ce6a5a9dc4c201a7339c344eb"
GATEWAY_INTERFACE: "CGI/1.2"
HTTP_ACCEPT: "text/html"
HTTP_ACCEPT_ENCODING: "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
HTTP_VERSION: "HTTP/1.1"
ORIGINAL_SCRIPT_NAME: ""
REMOTE_ADDR: "127.0.0.1"
SERVER_NAME: "localhost"
SERVER_PROTOCOL: "HTTP/1.1"
Response
Headers :
None
Clear highlights
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
'data-turbolinks-track': 'reload' %>
</head>
<body>
<header class="main">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<h1><%= @page_title %></h1>
</header>
<section class="content">
<nav class="side_nav">
<div id="cart" class="carts">
<%= render @cart %>
</div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/questions">Questions</a></li>
<li><a href="/news">News</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main class='<%= controller.controller_name %>'>
<%= yield %>
</main>
</section>
</body>
</html>
Start side
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
'data-turbolinks-track': 'reload' %>
</head>
<body>
<header class="main">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<h1><%= @page_title %></h1>
</header>
<section class="content">
<nav class="side_nav">
<div id="cart" class="carts">
<%= render @cart %>
</div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/questions">Questions</a></li>
<li><a href="/news">News</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main class='<%= controller.controller_name %>'>
<%= yield %>
</main>
</section>
</body>
</html>
End side
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
'data-turbolinks-track': 'reload' %>
</head>
<body>
<header class="main">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<h1><%= @page_title %></h1>
</header>
<section class="content">
<nav class="side_nav">
<div id="cart" class="carts">
<%= render @cart %>
</div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/questions">Questions</a></li>
<li><a href="/news">News</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main class='<%= controller.controller_name %>'>
<%= yield %>
</main>
</section>
</body>
</html>
Add if statement
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
'data-turbolinks-track': 'reload' %>
</head>
<body>
<header class="main">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<h1><%= @page_title %></h1>
</header>
<section class="content">
<nav class="side_nav">
<% if @cart %>
<div id="cart" class="carts">
<%= render @cart %>
</div>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/questions">Questions</a></li>
<li><a href="/news">News</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main class='<%= controller.controller_name %>'>
<%= yield %>
</main>
</section>
</body>
</html>
Add end statement
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application',
'data-turbolinks-track': 'reload' %>
</head>
<body>
<header class="main">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<h1><%= @page_title %></h1>
</header>
<section class="content">
<nav class="side_nav">
<% if @cart %>
<div id="cart" class="carts">
<%= render @cart %>
</div>
<% end %>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/questions">Questions</a></li>
<li><a href="/news">News</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main class='<%= controller.controller_name %>'>
<%= yield %>
</main>
</section>
</body>
</html>
all better
rails test
Run options: --seed 733
# Running:
..............................
Finished in 0.435370s, 68.9070 runs/s, 144.7046 assertions/s.
30 runs, 63 assertions, 0 failures, 0 errors, 0 skips
11.2 Iteration F2: Creating an AJAX-Based Cart
10.4 Playtime