wbzyl-rails3-tutorial 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/README.markdown +14 -0
  2. data/Rakefile +34 -0
  3. data/TODO +6 -0
  4. data/VERSION.yml +4 -0
  5. data/config.ru +15 -0
  6. data/lib/config.ru +12 -0
  7. data/lib/public/images/bradypus.jpg +0 -0
  8. data/lib/public/images/leniwce_controller.png +0 -0
  9. data/lib/public/images/leniwce_controller.svg +284 -0
  10. data/lib/public/images/leniwiec.png +0 -0
  11. data/lib/public/images/leniwiec.svg +284 -0
  12. data/lib/public/images/mvc.png +0 -0
  13. data/lib/public/images/mvc.svg +243 -0
  14. data/lib/public/images/pablo_picasso.jpg +0 -0
  15. data/lib/public/images/pastie.png +0 -0
  16. data/lib/public/images/rails3.png +0 -0
  17. data/lib/public/images/rails3.svg +125 -0
  18. data/lib/public/images/the_thinker.jpg +0 -0
  19. data/lib/public/javascripts/ruby3.js +1 -0
  20. data/lib/public/stylesheets/icons/doc.png +0 -0
  21. data/lib/public/stylesheets/icons/email.png +0 -0
  22. data/lib/public/stylesheets/icons/external.png +0 -0
  23. data/lib/public/stylesheets/icons/feed.png +0 -0
  24. data/lib/public/stylesheets/icons/im.png +0 -0
  25. data/lib/public/stylesheets/icons/pdf.png +0 -0
  26. data/lib/public/stylesheets/icons/visited.png +0 -0
  27. data/lib/public/stylesheets/icons/xls.png +0 -0
  28. data/lib/public/stylesheets/ie.css +27 -0
  29. data/lib/public/stylesheets/print.css +30 -0
  30. data/lib/public/stylesheets/rails3.css +139 -0
  31. data/lib/public/stylesheets/screen.css +249 -0
  32. data/lib/public/stylesheets/src/grid.png +0 -0
  33. data/lib/public/stylesheets/uv.css +121 -0
  34. data/lib/rails3-tutorial.rb +72 -0
  35. data/lib/views/answers.rdiscount +0 -0
  36. data/lib/views/authentication.rdiscount +10 -0
  37. data/lib/views/blog.rdiscount +365 -0
  38. data/lib/views/caching.rdiscount +4 -0
  39. data/lib/views/exercises.rdiscount +67 -0
  40. data/lib/views/fortune.rdiscount +592 -0
  41. data/lib/views/intro.rdiscount +68 -0
  42. data/lib/views/layout.rdiscount +38 -0
  43. data/lib/views/main.rdiscount +32 -0
  44. data/lib/views/pastie.rdiscount +371 -0
  45. data/lib/views/store.rdiscount +99 -0
  46. data/lib/views/todo.rdiscount +245 -0
  47. data/rails3-tutorial.gemspec +101 -0
  48. metadata +168 -0
@@ -0,0 +1,4 @@
1
+ #### {% title "Skalowanie aplikacji Rails 3" %}
2
+
3
+ Zobacz [Scaling Rails](http://railslab.newrelic.com/scaling-rails/)
4
+ 13 screencastów na ten temat.
@@ -0,0 +1,67 @@
1
+ #### {% title "Ćwiczenia" %}
2
+
3
+ # Ćwiczenia
4
+
5
+ 1\. Udoskonalamy „Fortunkę”:
6
+
7
+ * Utworzyć nową stronę główną aplikacji.
8
+ Umieścić na niej po jednym losowym cytacie
9
+ z każdej kategorii oraz linki do stron
10
+ z indeksami kategorii.
11
+ * Zaimplementować tagowanie obrazków tak jak to jest pokazane na screencaście
12
+ [More on Virtual Attributes](http://railscasts.com/episodes/167-more-on-virtual-attributes).
13
+ * Jeszcze raz otagować obrazki. Tym razem skorzystać z
14
+ [Acts As Taggable on Steroids Ruby On Rails Plugin With
15
+ Paginate](http://burm.net/2008/10/08/acts-as-taggable-on-steroids-ruby-on-rails-plugin-with-paginate/)
16
+
17
+ 2\. Wystylizować „Leniwca z o.o.”.
18
+
19
+ Do strony z wynikami wyszukiwania dodać paginację.
20
+ Skorzystać z gemu
21
+ [mislav-will-paginate](http://github.com/mislav/will_paginate/).
22
+
23
+ Do wyszukiwarki dodać select-menu z listą języków
24
+ obsługiwanych przez gem *Ultraviolet*
25
+ (`uv -l syntaxes`). Zmodyfikować kod *leniwca.local*, tak aby
26
+ było możliwe wyszukiwanie fragmentów kodu z wybranego
27
+ języka. Wskazówka: objerzeć screencast
28
+ [Advanced Search Form](http://railscasts.com/episodes/111-advanced-search-form)
29
+ Uwaga: na liście języków umieścić „Wszystkie języki”,
30
+ „Języki skryptowe“ i „Języki kompilowane”.
31
+
32
+ Wpisać listę wszystkich języków do tabelki *languages*.
33
+ Do select-menu pobrać tę listę z tej tabelki.
34
+
35
+ Dodać „full text search”. Wykorzystać gem
36
+ [xapit](http://github.com/ryanb/xapit/) albo
37
+ [thinking-sphinx](http://github.com/freelancing-god/thinking-sphinx/).
38
+ Przykład korzystający *thinking-sphinx* można obejrzeć
39
+ na screencaście R. Batesa,
40
+ [Thinking Sphinx](http://railscasts.com/episodes/120-thinking-sphinx).
41
+
42
+ Użyć innego ORM, np. Datamapper
43
+ \([przykład](http://github.com/dkubb/datamapper-on-rails/)\).
44
+
45
+
46
+ 3\. [A code snippet tool, intended for localhost usage](http://github.com/navyrain/navysnip/)
47
+
48
+ Add the following:
49
+
50
+ * Command line submission script
51
+ * Doubleclick to select all
52
+ * Submit-to-pastie button
53
+ * Search
54
+ * Add Rails-ish parsers to SHJS (erb, haml, sass)
55
+ * Tweak the PRE CSS or do something clever with wrapping
56
+ to make long lines display nicely
57
+
58
+
59
+ 4\. [Extremely Simple Photo Album in
60
+ Rails](http://rubyplus.org/episodes/92-Extremely-Simple-Photo-Album-in-Rails.html)
61
+
62
+ * You can extend this album so that the photo upload does not tie up
63
+ the Mongrel process by using Background DRB, Merb etc.
64
+ * Add the feature where users can only add/edit photos to their album.
65
+ * When a photo is added instead of displaying the album index page,
66
+ display the album show page so that users can continue uploading
67
+ photos.
@@ -0,0 +1,592 @@
1
+ #### {% title "Fortunka" %}
2
+
3
+ # Fortunka w Ruby on Rails
4
+
5
+ Zaczynamy od wygenerowania szkieletu aplikacji:
6
+
7
+ rails fortune
8
+
9
+ Generator rails utworzył wiele katalogów. Po co?
10
+
11
+ cd fortune
12
+
13
+ I juz mozemy przetestować szkielet aplikacji.
14
+ Uruchamiamy cienkiego:
15
+
16
+ script/server thin -p 3000
17
+
18
+ i oglądamy stronę testową:
19
+
20
+ http://localhost:3000
21
+
22
+ Plikowi ze stroną, którą oglądamy zmieniamy nazwę
23
+ na `welcome.html`:
24
+
25
+ cd public
26
+ mv index.html welcome.html
27
+
28
+ Teraz strona ta dosŧepna jest pod takim url:
29
+
30
+ http://localhost:3000/welcome.html
31
+
32
+ A stronę główną aplikacji wykonamy za chwilę.
33
+
34
+
35
+ ## Generujemy rusztowanie dla modelu Education
36
+
37
+ Zaczynamy od nauczenia Rails, że liczba mnoga od
38
+ *education* to *education*.
39
+
40
+ **Oj będą problemy!**
41
+
42
+ W pliku *inflections.rb* dopisujemy regułę:
43
+
44
+ :::ruby
45
+ # Add new inflection rules using the following format
46
+ # (all these examples are active by default):
47
+ ActiveSupport::Inflector.inflections do |inflect|
48
+ # inflect.plural /^(ox)$/i, '\1en'
49
+ # inflect.singular /^(ox)en/i, '\1'
50
+ # inflect.irregular 'person', 'people'
51
+ # inflect.uncountable %w( fish sheep )
52
+ inflect.uncountable %w( education )
53
+ end
54
+
55
+ Teraz podejrzymy jakie generatory są zainstalowane w systemie:
56
+
57
+ ./script/generate
58
+
59
+ Installed Generators
60
+ ... scaffold ...
61
+
62
+ ./script/generate scaffold
63
+
64
+ Description:
65
+ Scaffolds an entire resource, from model and migration to controller and
66
+ views, along with a full test suite. The resource is ready to use as a
67
+ starting point for your RESTful, resource-oriented application.
68
+
69
+ Model *Education* wygenerujemy tak:
70
+
71
+ ./script/generate scaffold Education author:string quotation:text
72
+
73
+ Polecenie:
74
+
75
+ ./script/generate scaffold Computer ..
76
+
77
+ utworzy następujące pliki:
78
+
79
+ <table class="span-19" summary="Scaffold">
80
+ <colgroup>
81
+ <col class="table1"/>
82
+ <col class="table2"/>
83
+ </colgroup>
84
+ <caption><em>Domyślne rusztowanie</em></caption>
85
+ <thead>
86
+ <tr>
87
+ <th class="span-8">Plik</th>
88
+ <th class="span-11">Po co?</th>
89
+ </tr>
90
+ </thead>
91
+
92
+ <tbody>
93
+ <tr>
94
+ <td>app/models/post.rb</td>
95
+ <td>The Computer model</td>
96
+ </tr>
97
+
98
+ <tr>
99
+ <td>db/migrate/......._create_computers.rb</td>
100
+ <td>Migration to create the computers table in your database</td>
101
+ </tr>
102
+
103
+ <tr>
104
+ <td>app/controllers/computers_controller.rb</td>
105
+ <td>The Computers controller</td>
106
+ </tr>
107
+
108
+ <tr>
109
+ <td>app/views/computers/index.html.erb</td>
110
+ <td>A view to display an index of all computers</td>
111
+ </tr>
112
+
113
+ <tr>
114
+ <td>app/views/computers/show.html.erb</td>
115
+ <td>A view to display a single post</td>
116
+ </tr>
117
+
118
+ <tr>
119
+ <td>app/views/computers/new.html.erb</td>
120
+ <td>A view to create a new post</td>
121
+ </tr>
122
+
123
+ <tr>
124
+ <td>app/views/computers/edit.html.erb</td>
125
+ <td>A view to edit an existing post</td>
126
+ </tr>
127
+
128
+ <tr>
129
+ <td>app/views/layouts/computers.html.erb</td>
130
+ <td>A view to control the overall look and feel of the other computers views</td>
131
+ </tr>
132
+
133
+ <tr>
134
+ <td>public/stylesheets/scaffold.css</td>
135
+ <td>Cascading style sheet to make the scaffolded views look better</td>
136
+ </tr>
137
+
138
+ <tr>
139
+ <td>app/helpers/computers_helper.rb</td>
140
+ <td>Helper functions to be used from the computers views</td>
141
+ </tr>
142
+
143
+ <tr>
144
+ <td>config/routes.rb</td>
145
+ <td>Edited to include routing information for computers</td>
146
+ </tr>
147
+
148
+ <!--
149
+
150
+ <tr>
151
+ <td>test/functional/computers_controller_test.rb</td>
152
+ <td>Functional testing harness for the computers controller</td>
153
+ </tr>
154
+
155
+ <tr>
156
+ <td>test/fixtures/computers.yml</td>
157
+ <td>Dummy computers for use in testing</td>
158
+ </tr>
159
+
160
+ <tr>
161
+ <td>test/unit/post_test.rb</td>
162
+ <td>Unit testing harness for the computers model</td>
163
+ </tr>
164
+
165
+ <tr>
166
+ <td>test/unit/helpers/computers_helper_test.rb </td>
167
+ <td>Unit testing harness for the computers helper</td>
168
+ </tr>
169
+
170
+ -->
171
+
172
+ </tbody>
173
+ </table>
174
+
175
+
176
+ Migrujemy:
177
+
178
+ rake db:create
179
+ rake db:migrate
180
+
181
+ Podglądamy routing:
182
+
183
+ rake routes
184
+
185
+ Stronę z listą cytatów podmontowujemy jako stronę główną witrynki.
186
+ W tym celu w pliku *routes.rb* edytujemy wiersz z `map.root`:
187
+
188
+ :::ruby
189
+ # You can have the root of your site routed with map.root --
190
+ # just remember to delete public/index.html.
191
+ map.root :controller => "education"
192
+
193
+ Wyniki naszej pracy możemy podejrzeć tutaj:
194
+
195
+ http://localhost:3000
196
+
197
+ Ale po kliknięciu na 'NEW' dostajemy *weird error*.
198
+ Generator wygenerował błędny kod? Poprawiamy generator
199
+ zamieniając w widokach, `education_path` na `education_index_path`,
200
+ przykład:
201
+
202
+ :::ruby
203
+ link_to 'Back', education_index_path
204
+
205
+ I jeszcze raz podglądamy routing:
206
+
207
+ rake routes
208
+
209
+ Jeśli nie zmienialibyśmy liczby mnogiej, to w wygenerowanych
210
+ widokach byłoby tak:
211
+
212
+ :::ruby
213
+ link_to 'Back', educations
214
+
215
+ i nie byłoby problemów, a tak mamy wygenerowany
216
+ link z `education` i rails oczekuje jakiegoś `id`
217
+ (dlaczego?, konwencja REST).
218
+
219
+
220
+ ## A co z polskimi literkami?
221
+
222
+ Komunikaty o błędach też powinny być po polsku.
223
+ Jak to zrobić? Zaglądamy do katalogu *locales* po
224
+ wskazówki:
225
+
226
+ :::ruby
227
+ # Sample localization file for English.
228
+ # Add more files in this directory for other locales.
229
+ # See http://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale
230
+ # for starting points.
231
+
232
+ Obejrzenie, krótkiego
233
+ [screecastu R. Batesa](http://railscasts.com/episodes/138-i18n)
234
+ też ma sens.
235
+
236
+ W pliku *environment.rb* odkomentowujemy i edytujemy wiersz
237
+ z `config.i18n.default_locale`:
238
+
239
+ # The default locale is :en and all translations
240
+ # from config/locales/*.rb,yml are auto loaded.
241
+ # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')]
242
+ config.i18n.default_locale = :pl
243
+ end
244
+
245
+ ## Suszymy kod: refaktoryzacja widoków
246
+
247
+ Wspólny kod z plików *new.html.erb* oraz *edit.html.erb*
248
+ przenosimy do pliku *_form.htm.erb*. Zamiast tego kodu
249
+ wstawiamy w pliku *new.html.erb*:
250
+
251
+ :::html_rails
252
+ <%= render :partial => "form",
253
+ :locals => { :f => f, :button_label => 'Create' } %>
254
+
255
+ a w pliku *edit.html.erb*:
256
+
257
+ :::html_rails
258
+ <%= render :partial => "form",
259
+ :locals => { :f => f, :button_label => 'Update' } %>
260
+
261
+
262
+ ## Suszymy kod: refaktoryzacja kontrolera za pomocą filtrów
263
+
264
+ Jeden wiersz powatarza się cztery razy w różnych akcjach:
265
+
266
+ :::ruby
267
+ class EducationController < ApplicationController
268
+ ...
269
+ def show
270
+ @education = Education.find(params[:id])
271
+ ...
272
+ def edit
273
+ @education = Education.find(params[:id])
274
+ ...
275
+ def update
276
+ @education = Education.find(params[:id])
277
+ ...
278
+ def destroy
279
+ @education = Education.find(params[:id])
280
+ ...
281
+
282
+ Usuwamy powtarzający się wiersz z akcji/metod i modyfikujemy
283
+ kod kontrolera w następujący sposób:
284
+
285
+ :::ruby
286
+ class EducationController < ApplicationController
287
+ before_filter :find_education, :only => [:show, :edit, :update, :destroy]
288
+ ...
289
+ private
290
+ def find_education
291
+ @education = Education.find(params[:id])
292
+ end
293
+ end
294
+
295
+ ## Dodajemy nową kolumnę do tabelki: *source*
296
+
297
+ Zaczynamy od rozpoznania terenu:
298
+
299
+ script/generate migration
300
+
301
+ Po lekturze decydujemy się na taką nazwę migracji:
302
+
303
+ script/generate migration AddSourceToEducation source:string
304
+
305
+ Wygenerowanego pliku, nie musimy poprawiać:
306
+
307
+ :::ruby
308
+ class AddSourceToEducation < ActiveRecord::Migration
309
+ def self.up
310
+ add_column :education, :source, :string
311
+ end
312
+
313
+ def self.down
314
+ remove_column :education, :source
315
+ end
316
+ end
317
+
318
+ Migrujemy:
319
+
320
+ rake db:migrate
321
+
322
+ Teraz poprawiamy wygenerowane widoki, dodając nową kolumnę.
323
+
324
+ Uruchamiamy aplikację i edytujemy wpis:
325
+
326
+ Oscar Wilde, "The Critic as Artist"
327
+
328
+ przerzucając nazwę książki do kolumny *Source*.
329
+
330
+
331
+ ## Generujemy rusztowanie dla modelu Frazes (Komunał)
332
+
333
+ Teraz uwzględniamy nową kolumnę.
334
+
335
+ ./script/generate scaffold Platitude author:string source:string quotation:text
336
+
337
+ Sprawdzamy jak to działa wpisując kilka komunałów.
338
+
339
+ Wykonujemy jeszcze raz te same kroki co przy modelu *Education*.
340
+ Teraz do partial mozemy wrzucić więcej kodu:
341
+ nie ma problemu z liczbą pojedynczą i mnogą.
342
+
343
+
344
+ ## Problemy, problemy: znowu czeka nas suszenie kodu
345
+
346
+ > Given sufficient time, what you put off doing
347
+ > today will get done by itself.
348
+
349
+ Powinniśmy mieć jeden model: *Quotation* z dodatkowym
350
+ atrybutem *category*. Co robić?
351
+
352
+ Na razie skorzystamy z takiego frazesu:
353
+
354
+ ## Dodajemy Basic HTTP Authentication
355
+
356
+ W tabelce *educations* jest trochę fajnych cytatów.
357
+ Nie chcemy aby ktoś nam grzebał w tabelkach.
358
+
359
+ :::ruby
360
+ class EducationController < ApplicationController
361
+ # USERNAME, PASSWORD = "wbzyl", "sekret"
362
+ # logger.info("Digest::SHA1: #{Digest::SHA1.hexdigest('sekret')}")
363
+ USERNAME, PASSWORD = "wbzyl", "a1b9892611956aa13a5ab9ccf01f49662583f2d2"
364
+ before_filter :authenticate, :only => [:new, :edit, :destroy]
365
+ ...
366
+ private
367
+ def authenticate
368
+ authenticate_or_request_with_http_basic do |username, password|
369
+ #username == USERNAME && password == PASSWORD
370
+ username == USERNAME && (Digest::SHA1.hexdigest(password) == PASSWORD)
371
+ end
372
+ end
373
+
374
+ Klasyka z railscasts [HTTP Basic
375
+ Authentication](http://railscasts.com/episodes/82-http-basic-authentication/).
376
+
377
+
378
+ ## Digest HTTP Authentication
379
+
380
+ > For those that may now know the difference, basic authentication only
381
+ > base 64 encodes the authenticating username and password (making it
382
+ > easily decoded) whereas digest authentication sends an MD5 hash of
383
+ > your username and password. To simplify, digest is more secure than
384
+ > basic.
385
+
386
+ W tabelce * platitudes* też jest trochę fajnych cytatów.
387
+ Dla odmiany zabezpieczymy cały kontroler za pomocą digest authentication.
388
+
389
+ :::ruby
390
+ class PlatitudesController < ApplicationController
391
+ USERS = { "wbzyl" => "sekret" }
392
+ before_filter :authenticate
393
+ ...
394
+ def authenticate
395
+ authenticate_or_request_with_http_digest do |username|
396
+ USERS[username]
397
+ end
398
+ end
399
+ end
400
+
401
+ ## Paginacja
402
+
403
+ Skorzystamy z gemu
404
+ [will_paginate](http://github.com/mislav/will_paginate/)
405
+
406
+ W pliku *environment.rb* dopisujemy:
407
+
408
+ :::ruby
409
+ Rails::Initializer.run do |config|
410
+ config.gem 'mislav-will_paginate', :version => '~> 2.3.6', :lib => 'will_paginate',
411
+ :source => 'http://gems.github.com'
412
+ end
413
+
414
+ i po kończącym blok end dopisujemy:
415
+
416
+ :::ruby
417
+ end
418
+ require "will_paginate"
419
+
420
+ Następnie sprawdzamy czy gem jest już zainstalowany w odpowiedniej wersji:
421
+
422
+ rake gems
423
+
424
+ Jeśli nie to wykonujemy polecenie:
425
+
426
+ rake gems:install
427
+
428
+ Tyle o instalacji.
429
+
430
+ W kodzie metody `index` kontrolera *Education* wymieniamy wiersz:
431
+
432
+ :::ruby
433
+ @education = Education.all
434
+
435
+ na
436
+
437
+ :::ruby
438
+ @education = Education.paginate :page => params[:page], :per_page => 5,
439
+ :order => 'updated_at DESC'
440
+
441
+ W widoku *index* dopisujemy:
442
+
443
+ :::html_rails
444
+ <%= will_paginate @education %>
445
+
446
+ No jeszcze należy wystylizować element:
447
+
448
+ :::html
449
+ <div class="pagination">
450
+
451
+ Tworzymy plik *application.css* gdzie wpisujemy:
452
+
453
+ :::css
454
+ .pagination {
455
+ margin-top: 1em;
456
+ }
457
+
458
+ Nowy plik stylu dopisujemy w layoucie kontrolera.
459
+
460
+
461
+ ## Dodajemy obrazki korzystając z gemu Paperclip
462
+
463
+ Do każdego cytatu będzie można dodać obrazek ze zdjęciem
464
+ autora cytatu lub czegokolwiek.
465
+
466
+ Tutaj znalazłem krótke howto:
467
+ [The Ruby On Rails Paperclip Plugin Tutorial - Easy Image
468
+ Attachments](http://burm.net/2008/10/07/the-ruby-on-rails-paperclip-plugin-tutorial-easy-image-attachments/)
469
+
470
+ Jak zwykle sprawdzamy czy gem jest już zainstalowany:
471
+
472
+ gem list
473
+
474
+ Jeśli nie to instalujemy go tak:
475
+
476
+ gem sources -a http://gems.github.com
477
+ sudo gem install thoughtbot-paperclip
478
+
479
+ albo, lepiej, w pliku *environment.rb* dopisujemy:
480
+
481
+ :::ruby
482
+ Rails::Initializer.run do |config|
483
+ config.gem 'thoughtbot-paperclip', :version => '~> 2.2.8', :lib => 'paperclip',
484
+ :source => 'http://gems.github.com'
485
+ end
486
+
487
+ i teraz wykonujemy
488
+
489
+ sudo rake gems:install
490
+
491
+ OK. Chcemy mieć attaczmęt w modelu *Education*. Nazwiemy go *photo*.
492
+
493
+ Generujemy migrację:
494
+
495
+ script/generate migration AddPhotosToEducation \
496
+ photo_file_name:string photo_content_type:string photo_file_size:integer
497
+
498
+ Migrujemy:
499
+
500
+ rake db:migrate
501
+
502
+ W modelu *Education* wpisujemy:
503
+
504
+ :::ruby
505
+ # paperclip
506
+ has_attached_file :photo, :styles => {
507
+ :thumb=> "100x100#",
508
+ :small => "150x150>",
509
+ }
510
+
511
+ W formularzach umieszczonych w widokach dopisujemy:
512
+ `html => { :multipart => true }`:
513
+
514
+ :::html_rails
515
+ # edit.html.erb
516
+ <% form_for(@education, :html => { :multipart => true }) do |f| %>
517
+ # new.html.erb
518
+ <% form_for(@education, :url => education_index_path,
519
+ :html => { :multipart => true }) do |f| %>
520
+
521
+ W widoku częściowym (partial) *_form.html.erb* dopisujemy:
522
+
523
+ :::html_rails
524
+ <p>
525
+ <%= f.label 'Photo' %><br />
526
+ <%= f.file_field :photo %>
527
+ </p>
528
+
529
+ W widoku *index.html.erb* dopisujemy:
530
+
531
+ :::html_rails
532
+ <% if education.photo.exists? %>
533
+ <td><%= image_tag education.photo.url(:thumb) %></td>
534
+ <% else %>
535
+ <td>[no photo]</td>
536
+ <% end %>
537
+
538
+ W widoku *show.html.erb* dopisujemy:
539
+
540
+ :::html_rails
541
+ <% if @education.photo.exists? %>
542
+ <p><%= image_tag @education.photo.url(:small) %></p>
543
+ <% else %>
544
+ <p> There are no photo's attached, upload one.</p>
545
+ <% end %>
546
+
547
+
548
+ ## Dodajemy middleware: Rack::HtmlTidy
549
+
550
+ Jak walidować HTML stron wygenerowanych przez Rails?
551
+ Wchodzenie na stronę [W3C Markup Validation Service](http://validator.w3.org/)
552
+ i ręczne wklejanie adresów jest uciążliwe i męczące.
553
+ Może zainstalować jakiś dodatek do Firefoxa? Może…
554
+
555
+ A może tak „prześwietlić” wygenerowaną stronę
556
+ HTML Tidy? Jak to zrobić opisałem na
557
+ [github.com](http://github.com/wbzyl/rack-htmltidy/).
558
+
559
+
560
+ ## Tytuły stron (bardzo ważne)
561
+
562
+ W pliku z metodami pomocniczymi dla całej aplikacji wpisujemy:
563
+
564
+ :::ruby
565
+ module ApplicationHelper
566
+ def title(title)
567
+ content_for(:title) { title }
568
+ end
569
+ end
570
+
571
+ W layoutach podmieniamy wygenerowany tytuł
572
+
573
+ :::html_rails
574
+ <title>Education: <%= controller.action_name %></title>
575
+
576
+ na
577
+
578
+ :::html_rails
579
+ <title><%= yield(:title) || "WB_Quotations" -%></title>
580
+
581
+ W widokach definiujemy tytuł wpisując:
582
+
583
+ :::html_rails
584
+ <% title 'WB_RAILS – Aktywne bazy danych' -%>
585
+
586
+
587
+ ## Różne
588
+
589
+ Skorzystać z wtyczki:
590
+ Bryan Helmkamp,
591
+ [rack-bug](http://github.com/brynary/rack-bug/) —
592
+ debugging toolbar for Rack applications implemented as middleware.