wbzyl-rails3-tutorial 0.0.2

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