rails 4.0.0 → 4.2.11.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +30 -23
  3. data/guides/CHANGELOG.md +108 -6
  4. data/guides/Rakefile +21 -6
  5. data/guides/assets/images/akshaysurve.jpg +0 -0
  6. data/guides/assets/images/edge_badge.png +0 -0
  7. data/guides/assets/images/feature_tile.gif +0 -0
  8. data/guides/assets/images/footer_tile.gif +0 -0
  9. data/guides/assets/images/fxn.png +0 -0
  10. data/guides/assets/images/getting_started/article_with_comments.png +0 -0
  11. data/guides/assets/images/getting_started/challenge.png +0 -0
  12. data/guides/assets/images/getting_started/confirm_dialog.png +0 -0
  13. data/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png +0 -0
  14. data/guides/assets/images/getting_started/form_with_errors.png +0 -0
  15. data/guides/assets/images/getting_started/index_action_with_edit_link.png +0 -0
  16. data/guides/assets/images/getting_started/new_article.png +0 -0
  17. data/guides/assets/images/getting_started/rails_welcome.png +0 -0
  18. data/guides/assets/images/getting_started/routing_error_no_controller.png +0 -0
  19. data/guides/assets/images/getting_started/routing_error_no_route_matches.png +0 -0
  20. data/guides/assets/images/getting_started/show_action_for_articles.png +0 -0
  21. data/guides/assets/images/getting_started/template_is_missing_articles_new.png +0 -0
  22. data/guides/assets/images/getting_started/unknown_action_create_for_articles.png +0 -0
  23. data/guides/assets/images/getting_started/unknown_action_new_for_articles.png +0 -0
  24. data/guides/assets/images/header_tile.gif +0 -0
  25. data/guides/assets/images/icons/README +1 -1
  26. data/guides/assets/images/icons/callouts/11.png +0 -0
  27. data/guides/assets/images/icons/callouts/12.png +0 -0
  28. data/guides/assets/images/icons/callouts/13.png +0 -0
  29. data/guides/assets/images/icons/callouts/15.png +0 -0
  30. data/guides/assets/images/icons/caution.png +0 -0
  31. data/guides/assets/images/icons/example.png +0 -0
  32. data/guides/assets/images/radar.png +0 -0
  33. data/guides/assets/images/rails4_features.png +0 -0
  34. data/guides/assets/images/rails_guides_kindle_cover.jpg +0 -0
  35. data/guides/assets/images/vijaydev.jpg +0 -0
  36. data/guides/assets/javascripts/guides.js +36 -34
  37. data/guides/assets/stylesheets/main.css +6 -2
  38. data/guides/assets/stylesheets/print.css +1 -1
  39. data/guides/bug_report_templates/action_controller_gem.rb +47 -0
  40. data/guides/bug_report_templates/action_controller_master.rb +54 -0
  41. data/guides/bug_report_templates/active_record_gem.rb +5 -2
  42. data/guides/bug_report_templates/active_record_master.rb +3 -2
  43. data/guides/bug_report_templates/generic_gem.rb +15 -0
  44. data/guides/bug_report_templates/generic_master.rb +26 -0
  45. data/guides/rails_guides.rb +23 -4
  46. data/guides/rails_guides/generator.rb +1 -1
  47. data/guides/rails_guides/helpers.rb +4 -2
  48. data/guides/rails_guides/levenshtein.rb +27 -21
  49. data/guides/rails_guides/markdown.rb +11 -7
  50. data/guides/rails_guides/markdown/renderer.rb +1 -1
  51. data/guides/source/2_2_release_notes.md +3 -3
  52. data/guides/source/2_3_release_notes.md +12 -12
  53. data/guides/source/3_0_release_notes.md +10 -13
  54. data/guides/source/3_1_release_notes.md +7 -4
  55. data/guides/source/3_2_release_notes.md +17 -14
  56. data/guides/source/4_0_release_notes.md +110 -54
  57. data/guides/source/4_1_release_notes.md +730 -0
  58. data/guides/source/4_2_release_notes.md +877 -0
  59. data/guides/source/_license.html.erb +1 -1
  60. data/guides/source/_welcome.html.erb +6 -2
  61. data/guides/source/action_controller_overview.md +223 -57
  62. data/guides/source/action_mailer_basics.md +129 -76
  63. data/guides/source/action_view_overview.md +247 -246
  64. data/guides/source/active_job_basics.md +339 -0
  65. data/guides/source/active_model_basics.md +374 -20
  66. data/guides/source/active_record_basics.md +46 -45
  67. data/guides/source/active_record_callbacks.md +83 -28
  68. data/guides/source/{migrations.md → active_record_migrations.md} +191 -275
  69. data/guides/source/active_record_postgresql.md +433 -0
  70. data/guides/source/active_record_querying.md +382 -300
  71. data/guides/source/active_record_validations.md +64 -55
  72. data/guides/source/active_support_core_extensions.md +229 -187
  73. data/guides/source/active_support_instrumentation.md +23 -22
  74. data/guides/source/api_documentation_guidelines.md +167 -15
  75. data/guides/source/asset_pipeline.md +768 -294
  76. data/guides/source/association_basics.md +188 -96
  77. data/guides/source/autoloading_and_reloading_constants.md +1311 -0
  78. data/guides/source/caching_with_rails.md +45 -11
  79. data/guides/source/command_line.md +96 -65
  80. data/guides/source/configuring.md +404 -70
  81. data/guides/source/contributing_to_ruby_on_rails.md +270 -130
  82. data/guides/source/credits.html.erb +7 -3
  83. data/guides/source/debugging_rails_applications.md +471 -284
  84. data/guides/source/development_dependencies_install.md +115 -21
  85. data/guides/source/documents.yaml +31 -9
  86. data/guides/source/engines.md +737 -291
  87. data/guides/source/form_helpers.md +137 -89
  88. data/guides/source/generators.md +60 -28
  89. data/guides/source/getting_started.md +1007 -596
  90. data/guides/source/i18n.md +178 -96
  91. data/guides/source/index.html.erb +2 -1
  92. data/guides/source/initialization.md +248 -104
  93. data/guides/source/kindle/toc.html.erb +1 -1
  94. data/guides/source/layout.html.erb +14 -22
  95. data/guides/source/layouts_and_rendering.md +78 -46
  96. data/guides/source/maintenance_policy.md +78 -0
  97. data/guides/source/nested_model_forms.md +10 -7
  98. data/guides/source/plugins.md +66 -57
  99. data/guides/source/rails_application_templates.md +49 -12
  100. data/guides/source/rails_on_rack.md +50 -60
  101. data/guides/source/routing.md +190 -139
  102. data/guides/source/ruby_on_rails_guides_guidelines.md +12 -13
  103. data/guides/source/security.md +134 -83
  104. data/guides/source/testing.md +322 -200
  105. data/guides/source/upgrading_ruby_on_rails.md +834 -37
  106. data/guides/source/working_with_javascript_in_rails.md +36 -26
  107. data/guides/w3c_validator.rb +2 -0
  108. metadata +93 -116
  109. data/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png +0 -0
  110. data/guides/assets/images/getting_started/new_post.png +0 -0
  111. data/guides/assets/images/getting_started/post_with_comments.png +0 -0
  112. data/guides/assets/images/getting_started/show_action_for_posts.png +0 -0
  113. data/guides/assets/images/getting_started/template_is_missing_posts_new.png +0 -0
  114. data/guides/assets/images/getting_started/undefined_method_post_path.png +0 -0
  115. data/guides/assets/images/getting_started/unknown_action_create_for_posts.png +0 -0
  116. data/guides/assets/images/getting_started/unknown_action_new_for_posts.png +0 -0
  117. data/guides/assets/images/jaimeiniesta.jpg +0 -0
  118. data/guides/code/getting_started/Gemfile +0 -43
  119. data/guides/code/getting_started/Gemfile.lock +0 -150
  120. data/guides/code/getting_started/README.rdoc +0 -28
  121. data/guides/code/getting_started/Rakefile +0 -6
  122. data/guides/code/getting_started/app/assets/javascripts/application.js +0 -16
  123. data/guides/code/getting_started/app/assets/javascripts/comments.js.coffee +0 -3
  124. data/guides/code/getting_started/app/assets/javascripts/posts.js.coffee +0 -3
  125. data/guides/code/getting_started/app/assets/javascripts/welcome.js.coffee +0 -3
  126. data/guides/code/getting_started/app/assets/stylesheets/application.css +0 -13
  127. data/guides/code/getting_started/app/assets/stylesheets/comments.css.scss +0 -3
  128. data/guides/code/getting_started/app/assets/stylesheets/posts.css.scss +0 -3
  129. data/guides/code/getting_started/app/assets/stylesheets/welcome.css.scss +0 -3
  130. data/guides/code/getting_started/app/controllers/application_controller.rb +0 -5
  131. data/guides/code/getting_started/app/controllers/comments_controller.rb +0 -17
  132. data/guides/code/getting_started/app/controllers/posts_controller.rb +0 -47
  133. data/guides/code/getting_started/app/controllers/welcome_controller.rb +0 -4
  134. data/guides/code/getting_started/app/helpers/application_helper.rb +0 -2
  135. data/guides/code/getting_started/app/helpers/comments_helper.rb +0 -2
  136. data/guides/code/getting_started/app/helpers/posts_helper.rb +0 -2
  137. data/guides/code/getting_started/app/helpers/welcome_helper.rb +0 -2
  138. data/guides/code/getting_started/app/models/comment.rb +0 -3
  139. data/guides/code/getting_started/app/models/post.rb +0 -7
  140. data/guides/code/getting_started/app/views/comments/_comment.html.erb +0 -15
  141. data/guides/code/getting_started/app/views/comments/_form.html.erb +0 -13
  142. data/guides/code/getting_started/app/views/layouts/application.html.erb +0 -14
  143. data/guides/code/getting_started/app/views/posts/_form.html.erb +0 -27
  144. data/guides/code/getting_started/app/views/posts/edit.html.erb +0 -5
  145. data/guides/code/getting_started/app/views/posts/index.html.erb +0 -21
  146. data/guides/code/getting_started/app/views/posts/new.html.erb +0 -5
  147. data/guides/code/getting_started/app/views/posts/show.html.erb +0 -18
  148. data/guides/code/getting_started/app/views/welcome/index.html.erb +0 -3
  149. data/guides/code/getting_started/bin/bundle +0 -4
  150. data/guides/code/getting_started/bin/rails +0 -4
  151. data/guides/code/getting_started/bin/rake +0 -4
  152. data/guides/code/getting_started/config.ru +0 -4
  153. data/guides/code/getting_started/config/application.rb +0 -18
  154. data/guides/code/getting_started/config/boot.rb +0 -4
  155. data/guides/code/getting_started/config/database.yml +0 -25
  156. data/guides/code/getting_started/config/environment.rb +0 -5
  157. data/guides/code/getting_started/config/environments/development.rb +0 -30
  158. data/guides/code/getting_started/config/environments/production.rb +0 -80
  159. data/guides/code/getting_started/config/environments/test.rb +0 -36
  160. data/guides/code/getting_started/config/initializers/backtrace_silencers.rb +0 -7
  161. data/guides/code/getting_started/config/initializers/filter_parameter_logging.rb +0 -4
  162. data/guides/code/getting_started/config/initializers/inflections.rb +0 -16
  163. data/guides/code/getting_started/config/initializers/locale.rb +0 -9
  164. data/guides/code/getting_started/config/initializers/mime_types.rb +0 -5
  165. data/guides/code/getting_started/config/initializers/secret_token.rb +0 -12
  166. data/guides/code/getting_started/config/initializers/session_store.rb +0 -3
  167. data/guides/code/getting_started/config/initializers/wrap_parameters.rb +0 -14
  168. data/guides/code/getting_started/config/locales/en.yml +0 -23
  169. data/guides/code/getting_started/config/routes.rb +0 -7
  170. data/guides/code/getting_started/db/migrate/20130122042648_create_posts.rb +0 -10
  171. data/guides/code/getting_started/db/migrate/20130122045842_create_comments.rb +0 -11
  172. data/guides/code/getting_started/db/schema.rb +0 -33
  173. data/guides/code/getting_started/db/seeds.rb +0 -7
  174. data/guides/code/getting_started/public/404.html +0 -58
  175. data/guides/code/getting_started/public/422.html +0 -58
  176. data/guides/code/getting_started/public/500.html +0 -57
  177. data/guides/code/getting_started/public/favicon.ico +0 -0
  178. data/guides/code/getting_started/public/robots.txt +0 -5
  179. data/guides/code/getting_started/test/controllers/comments_controller_test.rb +0 -7
  180. data/guides/code/getting_started/test/controllers/posts_controller_test.rb +0 -7
  181. data/guides/code/getting_started/test/controllers/welcome_controller_test.rb +0 -9
  182. data/guides/code/getting_started/test/fixtures/comments.yml +0 -11
  183. data/guides/code/getting_started/test/fixtures/posts.yml +0 -9
  184. data/guides/code/getting_started/test/helpers/comments_helper_test.rb +0 -4
  185. data/guides/code/getting_started/test/helpers/posts_helper_test.rb +0 -4
  186. data/guides/code/getting_started/test/helpers/welcome_helper_test.rb +0 -4
  187. data/guides/code/getting_started/test/models/comment_test.rb +0 -7
  188. data/guides/code/getting_started/test/models/post_test.rb +0 -7
  189. data/guides/code/getting_started/test/test_helper.rb +0 -15
  190. data/guides/source/kindle/KINDLE.md +0 -26
@@ -0,0 +1,339 @@
1
+ Active Job Basics
2
+ =================
3
+
4
+ This guide provides you with all you need to get started in creating,
5
+ enqueueing and executing background jobs.
6
+
7
+ After reading this guide, you will know:
8
+
9
+ * How to create jobs.
10
+ * How to enqueue jobs.
11
+ * How to run jobs in the background.
12
+ * How to send emails from your application async.
13
+
14
+ --------------------------------------------------------------------------------
15
+
16
+
17
+ Introduction
18
+ ------------
19
+
20
+ Active Job is a framework for declaring jobs and making them run on a variety
21
+ of queueing backends. These jobs can be everything from regularly scheduled
22
+ clean-ups, to billing charges, to mailings. Anything that can be chopped up
23
+ into small units of work and run in parallel, really.
24
+
25
+
26
+ The Purpose of Active Job
27
+ -----------------------------
28
+ The main point is to ensure that all Rails apps will have a job infrastructure
29
+ in place, even if it's in the form of an "immediate runner". We can then have
30
+ framework features and other gems build on top of that, without having to
31
+ worry about API differences between various job runners such as Delayed Job
32
+ and Resque. Picking your queuing backend becomes more of an operational concern,
33
+ then. And you'll be able to switch between them without having to rewrite your jobs.
34
+
35
+
36
+ Creating a Job
37
+ --------------
38
+
39
+ This section will provide a step-by-step guide to creating a job and enqueuing it.
40
+
41
+ ### Create the Job
42
+
43
+ Active Job provides a Rails generator to create jobs. The following will create a
44
+ job in `app/jobs` (with an attached test case under `test/jobs`):
45
+
46
+ ```bash
47
+ $ bin/rails generate job guests_cleanup
48
+ invoke test_unit
49
+ create test/jobs/guests_cleanup_job_test.rb
50
+ create app/jobs/guests_cleanup_job.rb
51
+ ```
52
+
53
+ You can also create a job that will run on a specific queue:
54
+
55
+ ```bash
56
+ $ bin/rails generate job guests_cleanup --queue urgent
57
+ ```
58
+
59
+ If you don't want to use a generator, you could create your own file inside of
60
+ `app/jobs`, just make sure that it inherits from `ActiveJob::Base`.
61
+
62
+ Here's what a job looks like:
63
+
64
+ ```ruby
65
+ class GuestsCleanupJob < ActiveJob::Base
66
+ queue_as :default
67
+
68
+ def perform(*guests)
69
+ # Do something later
70
+ end
71
+ end
72
+ ```
73
+
74
+ Note that you can define `perform` with as many arguments as you want.
75
+
76
+ ### Enqueue the Job
77
+
78
+ Enqueue a job like so:
79
+
80
+ ```ruby
81
+ # Enqueue a job to be performed as soon the queuing system is
82
+ # free.
83
+ GuestsCleanupJob.perform_later guest
84
+ ```
85
+
86
+ ```ruby
87
+ # Enqueue a job to be performed tomorrow at noon.
88
+ GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest)
89
+ ```
90
+
91
+ ```ruby
92
+ # Enqueue a job to be performed 1 week from now.
93
+ GuestsCleanupJob.set(wait: 1.week).perform_later(guest)
94
+ ```
95
+
96
+ ```ruby
97
+ # `perform_now` and `perform_later` will call `perform` under the hood so
98
+ # you can pass as many arguments as defined in the latter.
99
+ GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter')
100
+ ```
101
+
102
+ That's it!
103
+
104
+ Job Execution
105
+ -------------
106
+
107
+ If no adapter is set, the job is immediately executed.
108
+
109
+ ### Backends
110
+
111
+ Active Job has built-in adapters for multiple queueing backends (Sidekiq,
112
+ Resque, Delayed Job and others). To get an up-to-date list of the adapters
113
+ see the API Documentation for [ActiveJob::QueueAdapters](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html).
114
+
115
+ ### Setting the Backend
116
+
117
+ You can easily set your queueing backend:
118
+
119
+ ```ruby
120
+ # config/application.rb
121
+ module YourApp
122
+ class Application < Rails::Application
123
+ # Be sure to have the adapter's gem in your Gemfile and follow
124
+ # the adapter's specific installation and deployment instructions.
125
+ config.active_job.queue_adapter = :sidekiq
126
+ end
127
+ end
128
+ ```
129
+
130
+
131
+ Queues
132
+ ------
133
+
134
+ Most of the adapters support multiple queues. With Active Job you can schedule
135
+ the job to run on a specific queue:
136
+
137
+ ```ruby
138
+ class GuestsCleanupJob < ActiveJob::Base
139
+ queue_as :low_priority
140
+ #....
141
+ end
142
+ ```
143
+
144
+ You can prefix the queue name for all your jobs using
145
+ `config.active_job.queue_name_prefix` in `application.rb`:
146
+
147
+ ```ruby
148
+ # config/application.rb
149
+ module YourApp
150
+ class Application < Rails::Application
151
+ config.active_job.queue_name_prefix = Rails.env
152
+ end
153
+ end
154
+
155
+ # app/jobs/guests_cleanup.rb
156
+ class GuestsCleanupJob < ActiveJob::Base
157
+ queue_as :low_priority
158
+ #....
159
+ end
160
+
161
+ # Now your job will run on queue production_low_priority on your
162
+ # production environment and on staging_low_priority on your staging
163
+ # environment
164
+ ```
165
+
166
+ The default queue name prefix delimiter is '\_'. This can be changed by setting
167
+ `config.active_job.queue_name_delimiter` in `application.rb`:
168
+
169
+ ```ruby
170
+ # config/application.rb
171
+ module YourApp
172
+ class Application < Rails::Application
173
+ config.active_job.queue_name_prefix = Rails.env
174
+ config.active_job.queue_name_delimiter = '.'
175
+ end
176
+ end
177
+
178
+ # app/jobs/guests_cleanup.rb
179
+ class GuestsCleanupJob < ActiveJob::Base
180
+ queue_as :low_priority
181
+ #....
182
+ end
183
+
184
+ # Now your job will run on queue production.low_priority on your
185
+ # production environment and on staging.low_priority on your staging
186
+ # environment
187
+ ```
188
+
189
+ If you want more control on what queue a job will be run you can pass a `:queue`
190
+ option to `#set`:
191
+
192
+ ```ruby
193
+ MyJob.set(queue: :another_queue).perform_later(record)
194
+ ```
195
+
196
+ To control the queue from the job level you can pass a block to `#queue_as`. The
197
+ block will be executed in the job context (so you can access `self.arguments`)
198
+ and you must return the queue name:
199
+
200
+ ```ruby
201
+ class ProcessVideoJob < ActiveJob::Base
202
+ queue_as do
203
+ video = self.arguments.first
204
+ if video.owner.premium?
205
+ :premium_videojobs
206
+ else
207
+ :videojobs
208
+ end
209
+ end
210
+
211
+ def perform(video)
212
+ # do process video
213
+ end
214
+ end
215
+
216
+ ProcessVideoJob.perform_later(Video.last)
217
+ ```
218
+
219
+ NOTE: Make sure your queueing backend "listens" on your queue name. For some
220
+ backends you need to specify the queues to listen to.
221
+
222
+
223
+ Callbacks
224
+ ---------
225
+
226
+ Active Job provides hooks during the lifecycle of a job. Callbacks allow you to
227
+ trigger logic during the lifecycle of a job.
228
+
229
+ ### Available callbacks
230
+
231
+ * `before_enqueue`
232
+ * `around_enqueue`
233
+ * `after_enqueue`
234
+ * `before_perform`
235
+ * `around_perform`
236
+ * `after_perform`
237
+
238
+ ### Usage
239
+
240
+ ```ruby
241
+ class GuestsCleanupJob < ActiveJob::Base
242
+ queue_as :default
243
+
244
+ before_enqueue do |job|
245
+ # do something with the job instance
246
+ end
247
+
248
+ around_perform do |job, block|
249
+ # do something before perform
250
+ block.call
251
+ # do something after perform
252
+ end
253
+
254
+ def perform
255
+ # Do something later
256
+ end
257
+ end
258
+ ```
259
+
260
+
261
+ Action Mailer
262
+ ------------
263
+
264
+ One of the most common jobs in a modern web application is sending emails outside
265
+ of the request-response cycle, so the user doesn't have to wait on it. Active Job
266
+ is integrated with Action Mailer so you can easily send emails asynchronously:
267
+
268
+ ```ruby
269
+ # If you want to send the email now use #deliver_now
270
+ UserMailer.welcome(@user).deliver_now
271
+
272
+ # If you want to send the email through Active Job use #deliver_later
273
+ UserMailer.welcome(@user).deliver_later
274
+ ```
275
+
276
+
277
+ Internationalization
278
+ --------------------
279
+
280
+ Each job uses the `I18n.locale` set when the job was created. Useful if you send
281
+ emails asynchronously:
282
+
283
+ ```ruby
284
+ I18n.locale = :eo
285
+
286
+ UserMailer.welcome(@user).deliver_later # Email will be localized to Esparanto.
287
+ ```
288
+
289
+
290
+ GlobalID
291
+ --------
292
+
293
+ Active Job supports GlobalID for parameters. This makes it possible to pass live
294
+ Active Record objects to your job instead of class/id pairs, which you then have
295
+ to manually deserialize. Before, jobs would look like this:
296
+
297
+ ```ruby
298
+ class TrashableCleanupJob < ActiveJob::Base
299
+ def perform(trashable_class, trashable_id, depth)
300
+ trashable = trashable_class.constantize.find(trashable_id)
301
+ trashable.cleanup(depth)
302
+ end
303
+ end
304
+ ```
305
+
306
+ Now you can simply do:
307
+
308
+ ```ruby
309
+ class TrashableCleanupJob < ActiveJob::Base
310
+ def perform(trashable, depth)
311
+ trashable.cleanup(depth)
312
+ end
313
+ end
314
+ ```
315
+
316
+ This works with any class that mixes in `GlobalID::Identification`, which
317
+ by default has been mixed into Active Model classes.
318
+
319
+
320
+ Exceptions
321
+ ----------
322
+
323
+ Active Job provides a way to catch exceptions raised during the execution of the
324
+ job:
325
+
326
+ ```ruby
327
+
328
+ class GuestsCleanupJob < ActiveJob::Base
329
+ queue_as :default
330
+
331
+ rescue_from(ActiveRecord::RecordNotFound) do |exception|
332
+ # do something with the exception
333
+ end
334
+
335
+ def perform
336
+ # Do something later
337
+ end
338
+ end
339
+ ```
@@ -1,20 +1,32 @@
1
1
  Active Model Basics
2
2
  ===================
3
3
 
4
- This guide should provide you with all you need to get started using model classes. Active Model allows for Action Pack helpers to interact with non-Active Record models. Active Model also helps building custom ORMs for use outside of the Rails framework.
4
+ This guide should provide you with all you need to get started using model
5
+ classes. Active Model allows for Action Pack helpers to interact with
6
+ plain Ruby objects. Active Model also helps build custom ORMs for use
7
+ outside of the Rails framework.
5
8
 
6
- After reading this guide, you will know:
9
+ After reading this guide, you will be able to add to plain Ruby objects:
10
+
11
+ * The ability to behave like an Active Record model.
12
+ * Callbacks and validations like Active Record.
13
+ * Serializers.
14
+ * Integration with the Rails internationalization (i18n) framework.
7
15
 
8
16
  --------------------------------------------------------------------------------
9
17
 
10
18
  Introduction
11
19
  ------------
12
20
 
13
- Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below.
21
+ Active Model is a library containing various modules used in developing
22
+ classes that need some features present on Active Record.
23
+ Some of these modules are explained below.
14
24
 
15
- ### AttributeMethods
25
+ ### Attribute Methods
16
26
 
17
- The AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes, which methods on the object will use them.
27
+ The `ActiveModel::AttributeMethods` module can add custom prefixes and suffixes
28
+ on methods of a class. It is used by defining the prefixes and suffixes and
29
+ which methods on the object will use them.
18
30
 
19
31
  ```ruby
20
32
  class Person
@@ -38,14 +50,17 @@ end
38
50
 
39
51
  person = Person.new
40
52
  person.age = 110
41
- person.age_highest? # true
42
- person.reset_age # 0
43
- person.age_highest? # false
53
+ person.age_highest? # => true
54
+ person.reset_age # => 0
55
+ person.age_highest? # => false
44
56
  ```
45
57
 
46
58
  ### Callbacks
47
59
 
48
- Callbacks gives Active Record style callbacks. This provides the ability to define the callbacks and those will run at appropriate time. After defining a callbacks you can wrap with before, after and around custom methods.
60
+ `ActiveModel::Callbacks` gives Active Record style callbacks. This provides an
61
+ ability to define callbacks which run at appropriate times.
62
+ After defining callbacks, you can wrap them with before, after and around
63
+ custom methods.
49
64
 
50
65
  ```ruby
51
66
  class Person
@@ -57,19 +72,21 @@ class Person
57
72
 
58
73
  def update
59
74
  run_callbacks(:update) do
60
- # This will call when we are trying to call update on object.
75
+ # This method is called when update is called on an object.
61
76
  end
62
77
  end
63
78
 
64
79
  def reset_me
65
- # This method will call when you are calling update on object as a before_update callback as defined.
80
+ # This method is called when update is called on an object as a before_update callback is defined.
66
81
  end
67
82
  end
68
83
  ```
69
84
 
70
85
  ### Conversion
71
86
 
72
- If a class defines `persisted?` and `id` methods then you can include `Conversion` module in that class and you can able to call Rails conversion methods to objects of that class.
87
+ If a class defines `persisted?` and `id` methods, then you can include the
88
+ `ActiveModel::Conversion` module in that class and call the Rails conversion
89
+ methods on objects of that class.
73
90
 
74
91
  ```ruby
75
92
  class Person
@@ -92,11 +109,13 @@ person.to_param # => nil
92
109
 
93
110
  ### Dirty
94
111
 
95
- An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes `first_name` and `last_name`:
112
+ An object becomes dirty when it has gone through one or more changes to its
113
+ attributes and has not been saved. `ActiveModel::Dirty` gives the ability to
114
+ check whether an object has been changed or not. It also has attribute based
115
+ accessor methods. Let's consider a Person class with attributes `first_name`
116
+ and `last_name`:
96
117
 
97
118
  ```ruby
98
- require 'active_model'
99
-
100
119
  class Person
101
120
  include ActiveModel::Dirty
102
121
  define_attribute_methods :first_name, :last_name
@@ -120,8 +139,8 @@ class Person
120
139
  end
121
140
 
122
141
  def save
123
- @previously_changed = changes
124
142
  # do save work...
143
+ changes_applied
125
144
  end
126
145
  end
127
146
  ```
@@ -162,10 +181,11 @@ Track what was the previous value of the attribute.
162
181
 
163
182
  ```ruby
164
183
  # attr_name_was accessor
165
- person.first_name_was # => "First Name"
184
+ person.first_name_was # => nil
166
185
  ```
167
186
 
168
- Track both previous and current value of the changed attribute. Returns an array if changed, else returns nil.
187
+ Track both previous and current value of the changed attribute. Returns an array
188
+ if changed, else returns nil.
169
189
 
170
190
  ```ruby
171
191
  # attr_name_change
@@ -175,7 +195,8 @@ person.last_name_change # => nil
175
195
 
176
196
  ### Validations
177
197
 
178
- Validations module adds the ability to class objects to validate them in Active Record style.
198
+ `ActiveModel::Validations` module adds the ability to validate class objects
199
+ like in Active Record.
179
200
 
180
201
  ```ruby
181
202
  class Person
@@ -188,7 +209,8 @@ class Person
188
209
  validates! :token, presence: true
189
210
  end
190
211
 
191
- person = Person.new(token: "2b1f325")
212
+ person = Person.new
213
+ person.token = "2b1f325"
192
214
  person.valid? # => false
193
215
  person.name = 'vishnu'
194
216
  person.email = 'me'
@@ -198,3 +220,335 @@ person.valid? # => true
198
220
  person.token = nil
199
221
  person.valid? # => raises ActiveModel::StrictValidationFailed
200
222
  ```
223
+
224
+ ### Naming
225
+
226
+ `ActiveModel::Naming` adds a number of class methods which make the naming and routing
227
+ easier to manage. The module defines the `model_name` class method which
228
+ will define a number of accessors using some `ActiveSupport::Inflector` methods.
229
+
230
+ ```ruby
231
+ class Person
232
+ extend ActiveModel::Naming
233
+ end
234
+
235
+ Person.model_name.name # => "Person"
236
+ Person.model_name.singular # => "person"
237
+ Person.model_name.plural # => "people"
238
+ Person.model_name.element # => "person"
239
+ Person.model_name.human # => "Person"
240
+ Person.model_name.collection # => "people"
241
+ Person.model_name.param_key # => "person"
242
+ Person.model_name.i18n_key # => :person
243
+ Person.model_name.route_key # => "people"
244
+ Person.model_name.singular_route_key # => "person"
245
+ ```
246
+
247
+ ### Model
248
+
249
+ `ActiveModel::Model` adds the ability to a class to work with Action Pack and
250
+ Action View right out of the box.
251
+
252
+ ```ruby
253
+ class EmailContact
254
+ include ActiveModel::Model
255
+
256
+ attr_accessor :name, :email, :message
257
+ validates :name, :email, :message, presence: true
258
+
259
+ def deliver
260
+ if valid?
261
+ # deliver email
262
+ end
263
+ end
264
+ end
265
+ ```
266
+
267
+ When including `ActiveModel::Model` you get some features like:
268
+
269
+ - model name introspection
270
+ - conversions
271
+ - translations
272
+ - validations
273
+
274
+ It also gives you the ability to initialize an object with a hash of attributes,
275
+ much like any Active Record object.
276
+
277
+ ```ruby
278
+ email_contact = EmailContact.new(name: 'David',
279
+ email: 'david@example.com',
280
+ message: 'Hello World')
281
+ email_contact.name # => 'David'
282
+ email_contact.email # => 'david@example.com'
283
+ email_contact.valid? # => true
284
+ email_contact.persisted? # => false
285
+ ```
286
+
287
+ Any class that includes `ActiveModel::Model` can be used with `form_for`,
288
+ `render` and any other Action View helper methods, just like Active Record
289
+ objects.
290
+
291
+ ### Serialization
292
+
293
+ `ActiveModel::Serialization` provides a basic serialization for your object.
294
+ You need to declare an attributes hash which contains the attributes you want to
295
+ serialize. Attributes must be strings, not symbols.
296
+
297
+ ```ruby
298
+ class Person
299
+ include ActiveModel::Serialization
300
+
301
+ attr_accessor :name
302
+
303
+ def attributes
304
+ {'name' => nil}
305
+ end
306
+ end
307
+ ```
308
+
309
+ Now you can access a serialized hash of your object using the `serializable_hash`.
310
+
311
+ ```ruby
312
+ person = Person.new
313
+ person.serializable_hash # => {"name"=>nil}
314
+ person.name = "Bob"
315
+ person.serializable_hash # => {"name"=>"Bob"}
316
+ ```
317
+
318
+ #### ActiveModel::Serializers
319
+
320
+ Rails provides two serializers `ActiveModel::Serializers::JSON` and
321
+ `ActiveModel::Serializers::Xml`. Both of these modules automatically include
322
+ the `ActiveModel::Serialization`.
323
+
324
+ ##### ActiveModel::Serializers::JSON
325
+
326
+ To use the `ActiveModel::Serializers::JSON` you only need to change from
327
+ `ActiveModel::Serialization` to `ActiveModel::Serializers::JSON`.
328
+
329
+ ```ruby
330
+ class Person
331
+ include ActiveModel::Serializers::JSON
332
+
333
+ attr_accessor :name
334
+
335
+ def attributes
336
+ {'name' => nil}
337
+ end
338
+ end
339
+ ```
340
+
341
+ With the `as_json` you have a hash representing the model.
342
+
343
+ ```ruby
344
+ person = Person.new
345
+ person.as_json # => {"name"=>nil}
346
+ person.name = "Bob"
347
+ person.as_json # => {"name"=>"Bob"}
348
+ ```
349
+
350
+ From a JSON string you define the attributes of the model.
351
+ You need to have the `attributes=` method defined on your class:
352
+
353
+ ```ruby
354
+ class Person
355
+ include ActiveModel::Serializers::JSON
356
+
357
+ attr_accessor :name
358
+
359
+ def attributes=(hash)
360
+ hash.each do |key, value|
361
+ send("#{key}=", value)
362
+ end
363
+ end
364
+
365
+ def attributes
366
+ {'name' => nil}
367
+ end
368
+ end
369
+ ```
370
+
371
+ Now it is possible to create an instance of person and set the attributes using `from_json`.
372
+
373
+ ```ruby
374
+ json = { name: 'Bob' }.to_json
375
+ person = Person.new
376
+ person.from_json(json) # => #<Person:0x00000100c773f0 @name="Bob">
377
+ person.name # => "Bob"
378
+ ```
379
+
380
+ ##### ActiveModel::Serializers::Xml
381
+
382
+ To use the `ActiveModel::Serializers::Xml` you only need to change from
383
+ `ActiveModel::Serialization` to `ActiveModel::Serializers::Xml`.
384
+
385
+ ```ruby
386
+ class Person
387
+ include ActiveModel::Serializers::Xml
388
+
389
+ attr_accessor :name
390
+
391
+ def attributes
392
+ {'name' => nil}
393
+ end
394
+ end
395
+ ```
396
+
397
+ With the `to_xml` you have a XML representing the model.
398
+
399
+ ```ruby
400
+ person = Person.new
401
+ person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<person>\n <name nil=\"true\"/>\n</person>\n"
402
+ person.name = "Bob"
403
+ person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<person>\n <name>Bob</name>\n</person>\n"
404
+ ```
405
+
406
+ From a XML string you define the attributes of the model.
407
+ You need to have the `attributes=` method defined on your class:
408
+
409
+ ```ruby
410
+ class Person
411
+ include ActiveModel::Serializers::Xml
412
+
413
+ attr_accessor :name
414
+
415
+ def attributes=(hash)
416
+ hash.each do |key, value|
417
+ send("#{key}=", value)
418
+ end
419
+ end
420
+
421
+ def attributes
422
+ {'name' => nil}
423
+ end
424
+ end
425
+ ```
426
+
427
+ Now it is possible to create an instance of person and set the attributes using `from_xml`.
428
+
429
+ ```ruby
430
+ xml = { name: 'Bob' }.to_xml
431
+ person = Person.new
432
+ person.from_xml(xml) # => #<Person:0x00000100c773f0 @name="Bob">
433
+ person.name # => "Bob"
434
+ ```
435
+
436
+ ### Translation
437
+
438
+ `ActiveModel::Translation` provides integration between your object and the Rails
439
+ internationalization (i18n) framework.
440
+
441
+ ```ruby
442
+ class Person
443
+ extend ActiveModel::Translation
444
+ end
445
+ ```
446
+
447
+ With the `human_attribute_name` you can transform attribute names into a more
448
+ human format. The human format is defined in your locale file.
449
+
450
+ * config/locales/app.pt-BR.yml
451
+
452
+ ```yml
453
+ pt-BR:
454
+ activemodel:
455
+ attributes:
456
+ person:
457
+ name: 'Nome'
458
+ ```
459
+
460
+ ```ruby
461
+ Person.human_attribute_name('name') # => "Nome"
462
+ ```
463
+
464
+ ### Lint Tests
465
+
466
+ `ActiveModel::Lint::Tests` allow you to test whether an object is compliant with
467
+ the Active Model API.
468
+
469
+ * app/models/person.rb
470
+
471
+ ```ruby
472
+ class Person
473
+ include ActiveModel::Model
474
+
475
+ end
476
+ ```
477
+
478
+ * test/models/person_test.rb
479
+
480
+ ```ruby
481
+ require 'test_helper'
482
+
483
+ class PersonTest < ActiveSupport::TestCase
484
+ include ActiveModel::Lint::Tests
485
+
486
+ def setup
487
+ @model = Person.new
488
+ end
489
+ end
490
+ ```
491
+
492
+ ```bash
493
+ $ rake test
494
+
495
+ Run options: --seed 14596
496
+
497
+ # Running:
498
+
499
+ ......
500
+
501
+ Finished in 0.024899s, 240.9735 runs/s, 1204.8677 assertions/s.
502
+
503
+ 6 runs, 30 assertions, 0 failures, 0 errors, 0 skips
504
+ ```
505
+
506
+ An object is not required to implement all APIs in order to work with
507
+ Action Pack. This module only intends to provide guidance in case you want all
508
+ features out of the box.
509
+
510
+ ### SecurePassword
511
+
512
+ `ActiveModel::SecurePassword` provides a way to securely store any
513
+ password in an encrypted form. On including this module, a
514
+ `has_secure_password` class method is provided which defines
515
+ an accessor named `password` with certain validations on it.
516
+
517
+ #### Requirements
518
+
519
+ `ActiveModel::SecurePassword` depends on the [`bcrypt`](https://github.com/codahale/bcrypt-ruby 'BCrypt'),
520
+ so include this gem in your Gemfile to use `ActiveModel::SecurePassword` correctly.
521
+ In order to make this work, the model must have an accessor named `password_digest`.
522
+ The `has_secure_password` will add the following validations on the `password` accessor:
523
+
524
+ 1. Password should be present.
525
+ 2. Password should be equal to its confirmation.
526
+ 3. This maximum length of a password is 72 (required by `bcrypt` on which ActiveModel::SecurePassword depends)
527
+
528
+ #### Examples
529
+
530
+ ```ruby
531
+ class Person
532
+ include ActiveModel::SecurePassword
533
+ has_secure_password
534
+ attr_accessor :password_digest
535
+ end
536
+
537
+ person = Person.new
538
+
539
+ # When password is blank.
540
+ person.valid? # => false
541
+
542
+ # When the confirmation doesn't match the password.
543
+ person.password = 'aditya'
544
+ person.password_confirmation = 'nomatch'
545
+ person.valid? # => false
546
+
547
+ # When the length of password, exceeds 72.
548
+ person.password = person.password_confirmation = 'a' * 100
549
+ person.valid? # => false
550
+
551
+ # When all validations are passed.
552
+ person.password = person.password_confirmation = 'aditya'
553
+ person.valid? # => true
554
+ ```