rails 4.2.11.3 → 5.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +11 -7
- metadata +38 -237
- data/guides/CHANGELOG.md +0 -113
- data/guides/Rakefile +0 -92
- data/guides/assets/images/akshaysurve.jpg +0 -0
- data/guides/assets/images/belongs_to.png +0 -0
- data/guides/assets/images/book_icon.gif +0 -0
- data/guides/assets/images/bullet.gif +0 -0
- data/guides/assets/images/chapters_icon.gif +0 -0
- data/guides/assets/images/check_bullet.gif +0 -0
- data/guides/assets/images/credits_pic_blank.gif +0 -0
- data/guides/assets/images/csrf.png +0 -0
- data/guides/assets/images/edge_badge.png +0 -0
- data/guides/assets/images/favicon.ico +0 -0
- data/guides/assets/images/feature_tile.gif +0 -0
- data/guides/assets/images/footer_tile.gif +0 -0
- data/guides/assets/images/fxn.png +0 -0
- data/guides/assets/images/getting_started/article_with_comments.png +0 -0
- data/guides/assets/images/getting_started/challenge.png +0 -0
- data/guides/assets/images/getting_started/confirm_dialog.png +0 -0
- data/guides/assets/images/getting_started/forbidden_attributes_for_new_article.png +0 -0
- data/guides/assets/images/getting_started/form_with_errors.png +0 -0
- data/guides/assets/images/getting_started/index_action_with_edit_link.png +0 -0
- data/guides/assets/images/getting_started/new_article.png +0 -0
- data/guides/assets/images/getting_started/rails_welcome.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_controller.png +0 -0
- data/guides/assets/images/getting_started/routing_error_no_route_matches.png +0 -0
- data/guides/assets/images/getting_started/show_action_for_articles.png +0 -0
- data/guides/assets/images/getting_started/template_is_missing_articles_new.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_create_for_articles.png +0 -0
- data/guides/assets/images/getting_started/unknown_action_new_for_articles.png +0 -0
- data/guides/assets/images/grey_bullet.gif +0 -0
- data/guides/assets/images/habtm.png +0 -0
- data/guides/assets/images/has_many.png +0 -0
- data/guides/assets/images/has_many_through.png +0 -0
- data/guides/assets/images/has_one.png +0 -0
- data/guides/assets/images/has_one_through.png +0 -0
- data/guides/assets/images/header_backdrop.png +0 -0
- data/guides/assets/images/header_tile.gif +0 -0
- data/guides/assets/images/i18n/demo_html_safe.png +0 -0
- data/guides/assets/images/i18n/demo_localized_pirate.png +0 -0
- data/guides/assets/images/i18n/demo_translated_en.png +0 -0
- data/guides/assets/images/i18n/demo_translated_pirate.png +0 -0
- data/guides/assets/images/i18n/demo_translation_missing.png +0 -0
- data/guides/assets/images/i18n/demo_untranslated.png +0 -0
- data/guides/assets/images/icons/README +0 -5
- data/guides/assets/images/icons/callouts/1.png +0 -0
- data/guides/assets/images/icons/callouts/10.png +0 -0
- data/guides/assets/images/icons/callouts/11.png +0 -0
- data/guides/assets/images/icons/callouts/12.png +0 -0
- data/guides/assets/images/icons/callouts/13.png +0 -0
- data/guides/assets/images/icons/callouts/14.png +0 -0
- data/guides/assets/images/icons/callouts/15.png +0 -0
- data/guides/assets/images/icons/callouts/2.png +0 -0
- data/guides/assets/images/icons/callouts/3.png +0 -0
- data/guides/assets/images/icons/callouts/4.png +0 -0
- data/guides/assets/images/icons/callouts/5.png +0 -0
- data/guides/assets/images/icons/callouts/6.png +0 -0
- data/guides/assets/images/icons/callouts/7.png +0 -0
- data/guides/assets/images/icons/callouts/8.png +0 -0
- data/guides/assets/images/icons/callouts/9.png +0 -0
- data/guides/assets/images/icons/caution.png +0 -0
- data/guides/assets/images/icons/example.png +0 -0
- data/guides/assets/images/icons/home.png +0 -0
- data/guides/assets/images/icons/important.png +0 -0
- data/guides/assets/images/icons/next.png +0 -0
- data/guides/assets/images/icons/note.png +0 -0
- data/guides/assets/images/icons/prev.png +0 -0
- data/guides/assets/images/icons/tip.png +0 -0
- data/guides/assets/images/icons/up.png +0 -0
- data/guides/assets/images/icons/warning.png +0 -0
- data/guides/assets/images/nav_arrow.gif +0 -0
- data/guides/assets/images/oscardelben.jpg +0 -0
- data/guides/assets/images/polymorphic.png +0 -0
- data/guides/assets/images/radar.png +0 -0
- data/guides/assets/images/rails4_features.png +0 -0
- data/guides/assets/images/rails_guides_kindle_cover.jpg +0 -0
- data/guides/assets/images/rails_guides_logo.gif +0 -0
- data/guides/assets/images/rails_logo_remix.gif +0 -0
- data/guides/assets/images/session_fixation.png +0 -0
- data/guides/assets/images/tab_grey.gif +0 -0
- data/guides/assets/images/tab_info.gif +0 -0
- data/guides/assets/images/tab_note.gif +0 -0
- data/guides/assets/images/tab_red.gif +0 -0
- data/guides/assets/images/tab_yellow.gif +0 -0
- data/guides/assets/images/tab_yellow.png +0 -0
- data/guides/assets/images/vijaydev.jpg +0 -0
- data/guides/assets/javascripts/guides.js +0 -59
- data/guides/assets/javascripts/jquery.min.js +0 -4
- data/guides/assets/javascripts/responsive-tables.js +0 -43
- data/guides/assets/javascripts/syntaxhighlighter/shBrushAS3.js +0 -59
- data/guides/assets/javascripts/syntaxhighlighter/shBrushAppleScript.js +0 -75
- data/guides/assets/javascripts/syntaxhighlighter/shBrushBash.js +0 -59
- data/guides/assets/javascripts/syntaxhighlighter/shBrushCSharp.js +0 -65
- data/guides/assets/javascripts/syntaxhighlighter/shBrushColdFusion.js +0 -100
- data/guides/assets/javascripts/syntaxhighlighter/shBrushCpp.js +0 -97
- data/guides/assets/javascripts/syntaxhighlighter/shBrushCss.js +0 -91
- data/guides/assets/javascripts/syntaxhighlighter/shBrushDelphi.js +0 -55
- data/guides/assets/javascripts/syntaxhighlighter/shBrushDiff.js +0 -41
- data/guides/assets/javascripts/syntaxhighlighter/shBrushErlang.js +0 -52
- data/guides/assets/javascripts/syntaxhighlighter/shBrushGroovy.js +0 -67
- data/guides/assets/javascripts/syntaxhighlighter/shBrushJScript.js +0 -52
- data/guides/assets/javascripts/syntaxhighlighter/shBrushJava.js +0 -57
- data/guides/assets/javascripts/syntaxhighlighter/shBrushJavaFX.js +0 -58
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPerl.js +0 -72
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPhp.js +0 -88
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPlain.js +0 -33
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPowerShell.js +0 -74
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPython.js +0 -64
- data/guides/assets/javascripts/syntaxhighlighter/shBrushRuby.js +0 -55
- data/guides/assets/javascripts/syntaxhighlighter/shBrushSass.js +0 -94
- data/guides/assets/javascripts/syntaxhighlighter/shBrushScala.js +0 -51
- data/guides/assets/javascripts/syntaxhighlighter/shBrushSql.js +0 -66
- data/guides/assets/javascripts/syntaxhighlighter/shBrushVb.js +0 -56
- data/guides/assets/javascripts/syntaxhighlighter/shBrushXml.js +0 -69
- data/guides/assets/javascripts/syntaxhighlighter/shCore.js +0 -17
- data/guides/assets/stylesheets/fixes.css +0 -16
- data/guides/assets/stylesheets/kindle.css +0 -11
- data/guides/assets/stylesheets/main.css +0 -713
- data/guides/assets/stylesheets/print.css +0 -52
- data/guides/assets/stylesheets/reset.css +0 -43
- data/guides/assets/stylesheets/responsive-tables.css +0 -50
- data/guides/assets/stylesheets/style.css +0 -13
- data/guides/assets/stylesheets/syntaxhighlighter/shCore.css +0 -226
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreDefault.css +0 -328
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreDjango.css +0 -331
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreEclipse.css +0 -339
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreEmacs.css +0 -324
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreFadeToGrey.css +0 -328
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreMDUltra.css +0 -324
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreMidnight.css +0 -324
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreRDark.css +0 -324
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeDefault.css +0 -117
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeDjango.css +0 -120
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeEclipse.css +0 -128
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeEmacs.css +0 -113
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeFadeToGrey.css +0 -117
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeMDUltra.css +0 -113
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeMidnight.css +0 -113
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeRDark.css +0 -113
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeRailsGuides.css +0 -116
- data/guides/bug_report_templates/action_controller_gem.rb +0 -47
- data/guides/bug_report_templates/action_controller_master.rb +0 -54
- data/guides/bug_report_templates/active_record_gem.rb +0 -40
- data/guides/bug_report_templates/active_record_master.rb +0 -49
- data/guides/bug_report_templates/generic_gem.rb +0 -15
- data/guides/bug_report_templates/generic_master.rb +0 -26
- data/guides/rails_guides.rb +0 -63
- data/guides/rails_guides/generator.rb +0 -248
- data/guides/rails_guides/helpers.rb +0 -53
- data/guides/rails_guides/indexer.rb +0 -68
- data/guides/rails_guides/kindle.rb +0 -119
- data/guides/rails_guides/levenshtein.rb +0 -37
- data/guides/rails_guides/markdown.rb +0 -167
- data/guides/rails_guides/markdown/renderer.rb +0 -82
- data/guides/source/2_2_release_notes.md +0 -435
- data/guides/source/2_3_release_notes.md +0 -621
- data/guides/source/3_0_release_notes.md +0 -611
- data/guides/source/3_1_release_notes.md +0 -559
- data/guides/source/3_2_release_notes.md +0 -568
- data/guides/source/4_0_release_notes.md +0 -279
- data/guides/source/4_1_release_notes.md +0 -730
- data/guides/source/4_2_release_notes.md +0 -877
- data/guides/source/_license.html.erb +0 -2
- data/guides/source/_welcome.html.erb +0 -23
- data/guides/source/action_controller_overview.md +0 -1192
- data/guides/source/action_mailer_basics.md +0 -757
- data/guides/source/action_view_overview.md +0 -1561
- data/guides/source/active_job_basics.md +0 -339
- data/guides/source/active_model_basics.md +0 -554
- data/guides/source/active_record_basics.md +0 -374
- data/guides/source/active_record_callbacks.md +0 -413
- data/guides/source/active_record_migrations.md +0 -1018
- data/guides/source/active_record_postgresql.md +0 -433
- data/guides/source/active_record_querying.md +0 -1781
- data/guides/source/active_record_validations.md +0 -1179
- data/guides/source/active_support_core_extensions.md +0 -3856
- data/guides/source/active_support_instrumentation.md +0 -488
- data/guides/source/api_documentation_guidelines.md +0 -361
- data/guides/source/asset_pipeline.md +0 -1304
- data/guides/source/association_basics.md +0 -2245
- data/guides/source/autoloading_and_reloading_constants.md +0 -1311
- data/guides/source/caching_with_rails.md +0 -379
- data/guides/source/command_line.md +0 -625
- data/guides/source/configuring.md +0 -1070
- data/guides/source/contributing_to_ruby_on_rails.md +0 -628
- data/guides/source/credits.html.erb +0 -80
- data/guides/source/debugging_rails_applications.md +0 -861
- data/guides/source/development_dependencies_install.md +0 -289
- data/guides/source/documents.yaml +0 -205
- data/guides/source/engines.md +0 -1412
- data/guides/source/form_helpers.md +0 -1024
- data/guides/source/generators.md +0 -676
- data/guides/source/getting_started.md +0 -2086
- data/guides/source/i18n.md +0 -1087
- data/guides/source/index.html.erb +0 -28
- data/guides/source/initialization.md +0 -704
- data/guides/source/kindle/copyright.html.erb +0 -1
- data/guides/source/kindle/layout.html.erb +0 -27
- data/guides/source/kindle/rails_guides.opf.erb +0 -52
- data/guides/source/kindle/toc.html.erb +0 -24
- data/guides/source/kindle/toc.ncx.erb +0 -64
- data/guides/source/kindle/welcome.html.erb +0 -5
- data/guides/source/layout.html.erb +0 -140
- data/guides/source/layouts_and_rendering.md +0 -1226
- data/guides/source/maintenance_policy.md +0 -78
- data/guides/source/nested_model_forms.md +0 -228
- data/guides/source/plugins.md +0 -444
- data/guides/source/rails_application_templates.md +0 -266
- data/guides/source/rails_on_rack.md +0 -335
- data/guides/source/routing.md +0 -1155
- data/guides/source/ruby_on_rails_guides_guidelines.md +0 -127
- data/guides/source/security.md +0 -1024
- data/guides/source/testing.md +0 -1132
- data/guides/source/upgrading_ruby_on_rails.md +0 -1186
- data/guides/source/working_with_javascript_in_rails.md +0 -407
- data/guides/w3c_validator.rb +0 -97
data/guides/source/engines.md
DELETED
@@ -1,1412 +0,0 @@
|
|
1
|
-
Getting Started with Engines
|
2
|
-
============================
|
3
|
-
|
4
|
-
In this guide you will learn about engines and how they can be used to provide
|
5
|
-
additional functionality to their host applications through a clean and very
|
6
|
-
easy-to-use interface.
|
7
|
-
|
8
|
-
After reading this guide, you will know:
|
9
|
-
|
10
|
-
* What makes an engine.
|
11
|
-
* How to generate an engine.
|
12
|
-
* Building features for the engine.
|
13
|
-
* Hooking the engine into an application.
|
14
|
-
* Overriding engine functionality in the application.
|
15
|
-
|
16
|
-
--------------------------------------------------------------------------------
|
17
|
-
|
18
|
-
What are engines?
|
19
|
-
-----------------
|
20
|
-
|
21
|
-
Engines can be considered miniature applications that provide functionality to
|
22
|
-
their host applications. A Rails application is actually just a "supercharged"
|
23
|
-
engine, with the `Rails::Application` class inheriting a lot of its behavior
|
24
|
-
from `Rails::Engine`.
|
25
|
-
|
26
|
-
Therefore, engines and applications can be thought of almost the same thing,
|
27
|
-
just with subtle differences, as you'll see throughout this guide. Engines and
|
28
|
-
applications also share a common structure.
|
29
|
-
|
30
|
-
Engines are also closely related to plugins. The two share a common `lib`
|
31
|
-
directory structure, and are both generated using the `rails plugin new`
|
32
|
-
generator. The difference is that an engine is considered a "full plugin" by
|
33
|
-
Rails (as indicated by the `--full` option that's passed to the generator
|
34
|
-
command). We'll actually be using the `--mountable` option here, which includes
|
35
|
-
all the features of `--full`, and then some. This guide will refer to these
|
36
|
-
"full plugins" simply as "engines" throughout. An engine **can** be a plugin,
|
37
|
-
and a plugin **can** be an engine.
|
38
|
-
|
39
|
-
The engine that will be created in this guide will be called "blorgh". This
|
40
|
-
engine will provide blogging functionality to its host applications, allowing
|
41
|
-
for new articles and comments to be created. At the beginning of this guide, you
|
42
|
-
will be working solely within the engine itself, but in later sections you'll
|
43
|
-
see how to hook it into an application.
|
44
|
-
|
45
|
-
Engines can also be isolated from their host applications. This means that an
|
46
|
-
application is able to have a path provided by a routing helper such as
|
47
|
-
`articles_path` and use an engine also that provides a path also called
|
48
|
-
`articles_path`, and the two would not clash. Along with this, controllers, models
|
49
|
-
and table names are also namespaced. You'll see how to do this later in this
|
50
|
-
guide.
|
51
|
-
|
52
|
-
It's important to keep in mind at all times that the application should
|
53
|
-
**always** take precedence over its engines. An application is the object that
|
54
|
-
has final say in what goes on in its environment. The engine should
|
55
|
-
only be enhancing it, rather than changing it drastically.
|
56
|
-
|
57
|
-
To see demonstrations of other engines, check out
|
58
|
-
[Devise](https://github.com/plataformatec/devise), an engine that provides
|
59
|
-
authentication for its parent applications, or
|
60
|
-
[Forem](https://github.com/radar/forem), an engine that provides forum
|
61
|
-
functionality. There's also [Spree](https://github.com/spree/spree) which
|
62
|
-
provides an e-commerce platform, and
|
63
|
-
[RefineryCMS](https://github.com/refinery/refinerycms), a CMS engine.
|
64
|
-
|
65
|
-
Finally, engines would not have been possible without the work of James Adam,
|
66
|
-
Piotr Sarnacki, the Rails Core Team, and a number of other people. If you ever
|
67
|
-
meet them, don't forget to say thanks!
|
68
|
-
|
69
|
-
Generating an engine
|
70
|
-
--------------------
|
71
|
-
|
72
|
-
To generate an engine, you will need to run the plugin generator and pass it
|
73
|
-
options as appropriate to the need. For the "blorgh" example, you will need to
|
74
|
-
create a "mountable" engine, running this command in a terminal:
|
75
|
-
|
76
|
-
```bash
|
77
|
-
$ rails plugin new blorgh --mountable
|
78
|
-
```
|
79
|
-
|
80
|
-
The full list of options for the plugin generator may be seen by typing:
|
81
|
-
|
82
|
-
```bash
|
83
|
-
$ rails plugin --help
|
84
|
-
```
|
85
|
-
|
86
|
-
The `--mountable` option tells the generator that you want to create a
|
87
|
-
"mountable" and namespace-isolated engine. This generator will provide the same
|
88
|
-
skeleton structure as would the `--full` option. The `--full` option tells the
|
89
|
-
generator that you want to create an engine, including a skeleton structure
|
90
|
-
that provides the following:
|
91
|
-
|
92
|
-
* An `app` directory tree
|
93
|
-
* A `config/routes.rb` file:
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
Rails.application.routes.draw do
|
97
|
-
end
|
98
|
-
```
|
99
|
-
|
100
|
-
* A file at `lib/blorgh/engine.rb`, which is identical in function to a
|
101
|
-
standard Rails application's `config/application.rb` file:
|
102
|
-
|
103
|
-
```ruby
|
104
|
-
module Blorgh
|
105
|
-
class Engine < ::Rails::Engine
|
106
|
-
end
|
107
|
-
end
|
108
|
-
```
|
109
|
-
|
110
|
-
The `--mountable` option will add to the `--full` option:
|
111
|
-
|
112
|
-
* Asset manifest files (`application.js` and `application.css`)
|
113
|
-
* A namespaced `ApplicationController` stub
|
114
|
-
* A namespaced `ApplicationHelper` stub
|
115
|
-
* A layout view template for the engine
|
116
|
-
* Namespace isolation to `config/routes.rb`:
|
117
|
-
|
118
|
-
```ruby
|
119
|
-
Blorgh::Engine.routes.draw do
|
120
|
-
end
|
121
|
-
```
|
122
|
-
|
123
|
-
* Namespace isolation to `lib/blorgh/engine.rb`:
|
124
|
-
|
125
|
-
```ruby
|
126
|
-
module Blorgh
|
127
|
-
class Engine < ::Rails::Engine
|
128
|
-
isolate_namespace Blorgh
|
129
|
-
end
|
130
|
-
end
|
131
|
-
```
|
132
|
-
|
133
|
-
Additionally, the `--mountable` option tells the generator to mount the engine
|
134
|
-
inside the dummy testing application located at `test/dummy` by adding the
|
135
|
-
following to the dummy application's routes file at
|
136
|
-
`test/dummy/config/routes.rb`:
|
137
|
-
|
138
|
-
```ruby
|
139
|
-
mount Blorgh::Engine => "/blorgh"
|
140
|
-
```
|
141
|
-
|
142
|
-
### Inside an Engine
|
143
|
-
|
144
|
-
#### Critical Files
|
145
|
-
|
146
|
-
At the root of this brand new engine's directory lives a `blorgh.gemspec` file.
|
147
|
-
When you include the engine into an application later on, you will do so with
|
148
|
-
this line in the Rails application's `Gemfile`:
|
149
|
-
|
150
|
-
```ruby
|
151
|
-
gem 'blorgh', path: "vendor/engines/blorgh"
|
152
|
-
```
|
153
|
-
|
154
|
-
Don't forget to run `bundle install` as usual. By specifying it as a gem within
|
155
|
-
the `Gemfile`, Bundler will load it as such, parsing this `blorgh.gemspec` file
|
156
|
-
and requiring a file within the `lib` directory called `lib/blorgh.rb`. This
|
157
|
-
file requires the `blorgh/engine.rb` file (located at `lib/blorgh/engine.rb`)
|
158
|
-
and defines a base module called `Blorgh`.
|
159
|
-
|
160
|
-
```ruby
|
161
|
-
require "blorgh/engine"
|
162
|
-
|
163
|
-
module Blorgh
|
164
|
-
end
|
165
|
-
```
|
166
|
-
|
167
|
-
TIP: Some engines choose to use this file to put global configuration options
|
168
|
-
for their engine. It's a relatively good idea, so if you want to offer
|
169
|
-
configuration options, the file where your engine's `module` is defined is
|
170
|
-
perfect for that. Place the methods inside the module and you'll be good to go.
|
171
|
-
|
172
|
-
Within `lib/blorgh/engine.rb` is the base class for the engine:
|
173
|
-
|
174
|
-
```ruby
|
175
|
-
module Blorgh
|
176
|
-
class Engine < ::Rails::Engine
|
177
|
-
isolate_namespace Blorgh
|
178
|
-
end
|
179
|
-
end
|
180
|
-
```
|
181
|
-
|
182
|
-
By inheriting from the `Rails::Engine` class, this gem notifies Rails that
|
183
|
-
there's an engine at the specified path, and will correctly mount the engine
|
184
|
-
inside the application, performing tasks such as adding the `app` directory of
|
185
|
-
the engine to the load path for models, mailers, controllers and views.
|
186
|
-
|
187
|
-
The `isolate_namespace` method here deserves special notice. This call is
|
188
|
-
responsible for isolating the controllers, models, routes and other things into
|
189
|
-
their own namespace, away from similar components inside the application.
|
190
|
-
Without this, there is a possibility that the engine's components could "leak"
|
191
|
-
into the application, causing unwanted disruption, or that important engine
|
192
|
-
components could be overridden by similarly named things within the application.
|
193
|
-
One of the examples of such conflicts is helpers. Without calling
|
194
|
-
`isolate_namespace`, the engine's helpers would be included in an application's
|
195
|
-
controllers.
|
196
|
-
|
197
|
-
NOTE: It is **highly** recommended that the `isolate_namespace` line be left
|
198
|
-
within the `Engine` class definition. Without it, classes generated in an engine
|
199
|
-
**may** conflict with an application.
|
200
|
-
|
201
|
-
What this isolation of the namespace means is that a model generated by a call
|
202
|
-
to `bin/rails g model`, such as `bin/rails g model article`, won't be called `Article`, but
|
203
|
-
instead be namespaced and called `Blorgh::Article`. In addition, the table for the
|
204
|
-
model is namespaced, becoming `blorgh_articles`, rather than simply `articles`.
|
205
|
-
Similar to the model namespacing, a controller called `ArticlesController` becomes
|
206
|
-
`Blorgh::ArticlesController` and the views for that controller will not be at
|
207
|
-
`app/views/articles`, but `app/views/blorgh/articles` instead. Mailers are namespaced
|
208
|
-
as well.
|
209
|
-
|
210
|
-
Finally, routes will also be isolated within the engine. This is one of the most
|
211
|
-
important parts about namespacing, and is discussed later in the
|
212
|
-
[Routes](#routes) section of this guide.
|
213
|
-
|
214
|
-
#### `app` Directory
|
215
|
-
|
216
|
-
Inside the `app` directory are the standard `assets`, `controllers`, `helpers`,
|
217
|
-
`mailers`, `models` and `views` directories that you should be familiar with
|
218
|
-
from an application. The `helpers`, `mailers` and `models` directories are
|
219
|
-
empty, so they aren't described in this section. We'll look more into models in
|
220
|
-
a future section, when we're writing the engine.
|
221
|
-
|
222
|
-
Within the `app/assets` directory, there are the `images`, `javascripts` and
|
223
|
-
`stylesheets` directories which, again, you should be familiar with due to their
|
224
|
-
similarity to an application. One difference here, however, is that each
|
225
|
-
directory contains a sub-directory with the engine name. Because this engine is
|
226
|
-
going to be namespaced, its assets should be too.
|
227
|
-
|
228
|
-
Within the `app/controllers` directory there is a `blorgh` directory that
|
229
|
-
contains a file called `application_controller.rb`. This file will provide any
|
230
|
-
common functionality for the controllers of the engine. The `blorgh` directory
|
231
|
-
is where the other controllers for the engine will go. By placing them within
|
232
|
-
this namespaced directory, you prevent them from possibly clashing with
|
233
|
-
identically-named controllers within other engines or even within the
|
234
|
-
application.
|
235
|
-
|
236
|
-
NOTE: The `ApplicationController` class inside an engine is named just like a
|
237
|
-
Rails application in order to make it easier for you to convert your
|
238
|
-
applications into engines.
|
239
|
-
|
240
|
-
Lastly, the `app/views` directory contains a `layouts` folder, which contains a
|
241
|
-
file at `blorgh/application.html.erb`. This file allows you to specify a layout
|
242
|
-
for the engine. If this engine is to be used as a stand-alone engine, then you
|
243
|
-
would add any customization to its layout in this file, rather than the
|
244
|
-
application's `app/views/layouts/application.html.erb` file.
|
245
|
-
|
246
|
-
If you don't want to force a layout on to users of the engine, then you can
|
247
|
-
delete this file and reference a different layout in the controllers of your
|
248
|
-
engine.
|
249
|
-
|
250
|
-
#### `bin` Directory
|
251
|
-
|
252
|
-
This directory contains one file, `bin/rails`, which enables you to use the
|
253
|
-
`rails` sub-commands and generators just like you would within an application.
|
254
|
-
This means that you will be able to generate new controllers and models for this
|
255
|
-
engine very easily by running commands like this:
|
256
|
-
|
257
|
-
```bash
|
258
|
-
$ bin/rails g model
|
259
|
-
```
|
260
|
-
|
261
|
-
Keep in mind, of course, that anything generated with these commands inside of
|
262
|
-
an engine that has `isolate_namespace` in the `Engine` class will be namespaced.
|
263
|
-
|
264
|
-
#### `test` Directory
|
265
|
-
|
266
|
-
The `test` directory is where tests for the engine will go. To test the engine,
|
267
|
-
there is a cut-down version of a Rails application embedded within it at
|
268
|
-
`test/dummy`. This application will mount the engine in the
|
269
|
-
`test/dummy/config/routes.rb` file:
|
270
|
-
|
271
|
-
```ruby
|
272
|
-
Rails.application.routes.draw do
|
273
|
-
mount Blorgh::Engine => "/blorgh"
|
274
|
-
end
|
275
|
-
```
|
276
|
-
|
277
|
-
This line mounts the engine at the path `/blorgh`, which will make it accessible
|
278
|
-
through the application only at that path.
|
279
|
-
|
280
|
-
Inside the test directory there is the `test/integration` directory, where
|
281
|
-
integration tests for the engine should be placed. Other directories can be
|
282
|
-
created in the `test` directory as well. For example, you may wish to create a
|
283
|
-
`test/models` directory for your model tests.
|
284
|
-
|
285
|
-
Providing engine functionality
|
286
|
-
------------------------------
|
287
|
-
|
288
|
-
The engine that this guide covers provides submitting articles and commenting
|
289
|
-
functionality and follows a similar thread to the [Getting Started
|
290
|
-
Guide](getting_started.html), with some new twists.
|
291
|
-
|
292
|
-
### Generating an Article Resource
|
293
|
-
|
294
|
-
The first thing to generate for a blog engine is the `Article` model and related
|
295
|
-
controller. To quickly generate this, you can use the Rails scaffold generator.
|
296
|
-
|
297
|
-
```bash
|
298
|
-
$ bin/rails generate scaffold article title:string text:text
|
299
|
-
```
|
300
|
-
|
301
|
-
This command will output this information:
|
302
|
-
|
303
|
-
```
|
304
|
-
invoke active_record
|
305
|
-
create db/migrate/[timestamp]_create_blorgh_articles.rb
|
306
|
-
create app/models/blorgh/article.rb
|
307
|
-
invoke test_unit
|
308
|
-
create test/models/blorgh/article_test.rb
|
309
|
-
create test/fixtures/blorgh/articles.yml
|
310
|
-
invoke resource_route
|
311
|
-
route resources :articles
|
312
|
-
invoke scaffold_controller
|
313
|
-
create app/controllers/blorgh/articles_controller.rb
|
314
|
-
invoke erb
|
315
|
-
create app/views/blorgh/articles
|
316
|
-
create app/views/blorgh/articles/index.html.erb
|
317
|
-
create app/views/blorgh/articles/edit.html.erb
|
318
|
-
create app/views/blorgh/articles/show.html.erb
|
319
|
-
create app/views/blorgh/articles/new.html.erb
|
320
|
-
create app/views/blorgh/articles/_form.html.erb
|
321
|
-
invoke test_unit
|
322
|
-
create test/controllers/blorgh/articles_controller_test.rb
|
323
|
-
invoke helper
|
324
|
-
create app/helpers/blorgh/articles_helper.rb
|
325
|
-
invoke assets
|
326
|
-
invoke js
|
327
|
-
create app/assets/javascripts/blorgh/articles.js
|
328
|
-
invoke css
|
329
|
-
create app/assets/stylesheets/blorgh/articles.css
|
330
|
-
invoke css
|
331
|
-
create app/assets/stylesheets/scaffold.css
|
332
|
-
```
|
333
|
-
|
334
|
-
The first thing that the scaffold generator does is invoke the `active_record`
|
335
|
-
generator, which generates a migration and a model for the resource. Note here,
|
336
|
-
however, that the migration is called `create_blorgh_articles` rather than the
|
337
|
-
usual `create_articles`. This is due to the `isolate_namespace` method called in
|
338
|
-
the `Blorgh::Engine` class's definition. The model here is also namespaced,
|
339
|
-
being placed at `app/models/blorgh/article.rb` rather than `app/models/article.rb` due
|
340
|
-
to the `isolate_namespace` call within the `Engine` class.
|
341
|
-
|
342
|
-
Next, the `test_unit` generator is invoked for this model, generating a model
|
343
|
-
test at `test/models/blorgh/article_test.rb` (rather than
|
344
|
-
`test/models/article_test.rb`) and a fixture at `test/fixtures/blorgh/articles.yml`
|
345
|
-
(rather than `test/fixtures/articles.yml`).
|
346
|
-
|
347
|
-
After that, a line for the resource is inserted into the `config/routes.rb` file
|
348
|
-
for the engine. This line is simply `resources :articles`, turning the
|
349
|
-
`config/routes.rb` file for the engine into this:
|
350
|
-
|
351
|
-
```ruby
|
352
|
-
Blorgh::Engine.routes.draw do
|
353
|
-
resources :articles
|
354
|
-
end
|
355
|
-
```
|
356
|
-
|
357
|
-
Note here that the routes are drawn upon the `Blorgh::Engine` object rather than
|
358
|
-
the `YourApp::Application` class. This is so that the engine routes are confined
|
359
|
-
to the engine itself and can be mounted at a specific point as shown in the
|
360
|
-
[test directory](#test-directory) section. It also causes the engine's routes to
|
361
|
-
be isolated from those routes that are within the application. The
|
362
|
-
[Routes](#routes) section of this guide describes it in detail.
|
363
|
-
|
364
|
-
Next, the `scaffold_controller` generator is invoked, generating a controller
|
365
|
-
called `Blorgh::ArticlesController` (at
|
366
|
-
`app/controllers/blorgh/articles_controller.rb`) and its related views at
|
367
|
-
`app/views/blorgh/articles`. This generator also generates a test for the
|
368
|
-
controller (`test/controllers/blorgh/articles_controller_test.rb`) and a helper
|
369
|
-
(`app/helpers/blorgh/articles_controller.rb`).
|
370
|
-
|
371
|
-
Everything this generator has created is neatly namespaced. The controller's
|
372
|
-
class is defined within the `Blorgh` module:
|
373
|
-
|
374
|
-
```ruby
|
375
|
-
module Blorgh
|
376
|
-
class ArticlesController < ApplicationController
|
377
|
-
...
|
378
|
-
end
|
379
|
-
end
|
380
|
-
```
|
381
|
-
|
382
|
-
NOTE: The `ApplicationController` class being inherited from here is the
|
383
|
-
`Blorgh::ApplicationController`, not an application's `ApplicationController`.
|
384
|
-
|
385
|
-
The helper inside `app/helpers/blorgh/articles_helper.rb` is also namespaced:
|
386
|
-
|
387
|
-
```ruby
|
388
|
-
module Blorgh
|
389
|
-
module ArticlesHelper
|
390
|
-
...
|
391
|
-
end
|
392
|
-
end
|
393
|
-
```
|
394
|
-
|
395
|
-
This helps prevent conflicts with any other engine or application that may have
|
396
|
-
an article resource as well.
|
397
|
-
|
398
|
-
Finally, the assets for this resource are generated in two files:
|
399
|
-
`app/assets/javascripts/blorgh/articles.js` and
|
400
|
-
`app/assets/stylesheets/blorgh/articles.css`. You'll see how to use these a little
|
401
|
-
later.
|
402
|
-
|
403
|
-
By default, the scaffold styling is not applied to the engine because the
|
404
|
-
engine's layout file, `app/views/layouts/blorgh/application.html.erb`, doesn't
|
405
|
-
load it. To make the scaffold styling apply, insert this line into the `<head>`
|
406
|
-
tag of this layout:
|
407
|
-
|
408
|
-
```erb
|
409
|
-
<%= stylesheet_link_tag "scaffold" %>
|
410
|
-
```
|
411
|
-
|
412
|
-
You can see what the engine has so far by running `rake db:migrate` at the root
|
413
|
-
of our engine to run the migration generated by the scaffold generator, and then
|
414
|
-
running `rails server` in `test/dummy`. When you open
|
415
|
-
`http://localhost:3000/blorgh/articles` you will see the default scaffold that has
|
416
|
-
been generated. Click around! You've just generated your first engine's first
|
417
|
-
functions.
|
418
|
-
|
419
|
-
If you'd rather play around in the console, `rails console` will also work just
|
420
|
-
like a Rails application. Remember: the `Article` model is namespaced, so to
|
421
|
-
reference it you must call it as `Blorgh::Article`.
|
422
|
-
|
423
|
-
```ruby
|
424
|
-
>> Blorgh::Article.find(1)
|
425
|
-
=> #<Blorgh::Article id: 1 ...>
|
426
|
-
```
|
427
|
-
|
428
|
-
One final thing is that the `articles` resource for this engine should be the root
|
429
|
-
of the engine. Whenever someone goes to the root path where the engine is
|
430
|
-
mounted, they should be shown a list of articles. This can be made to happen if
|
431
|
-
this line is inserted into the `config/routes.rb` file inside the engine:
|
432
|
-
|
433
|
-
```ruby
|
434
|
-
root to: "articles#index"
|
435
|
-
```
|
436
|
-
|
437
|
-
Now people will only need to go to the root of the engine to see all the articles,
|
438
|
-
rather than visiting `/articles`. This means that instead of
|
439
|
-
`http://localhost:3000/blorgh/articles`, you only need to go to
|
440
|
-
`http://localhost:3000/blorgh` now.
|
441
|
-
|
442
|
-
### Generating a Comments Resource
|
443
|
-
|
444
|
-
Now that the engine can create new articles, it only makes sense to add
|
445
|
-
commenting functionality as well. To do this, you'll need to generate a comment
|
446
|
-
model, a comment controller and then modify the articles scaffold to display
|
447
|
-
comments and allow people to create new ones.
|
448
|
-
|
449
|
-
From the application root, run the model generator. Tell it to generate a
|
450
|
-
`Comment` model, with the related table having two columns: a `article_id` integer
|
451
|
-
and `text` text column.
|
452
|
-
|
453
|
-
```bash
|
454
|
-
$ bin/rails generate model Comment article_id:integer text:text
|
455
|
-
```
|
456
|
-
|
457
|
-
This will output the following:
|
458
|
-
|
459
|
-
```
|
460
|
-
invoke active_record
|
461
|
-
create db/migrate/[timestamp]_create_blorgh_comments.rb
|
462
|
-
create app/models/blorgh/comment.rb
|
463
|
-
invoke test_unit
|
464
|
-
create test/models/blorgh/comment_test.rb
|
465
|
-
create test/fixtures/blorgh/comments.yml
|
466
|
-
```
|
467
|
-
|
468
|
-
This generator call will generate just the necessary model files it needs,
|
469
|
-
namespacing the files under a `blorgh` directory and creating a model class
|
470
|
-
called `Blorgh::Comment`. Now run the migration to create our blorgh_comments
|
471
|
-
table:
|
472
|
-
|
473
|
-
```bash
|
474
|
-
$ rake db:migrate
|
475
|
-
```
|
476
|
-
|
477
|
-
To show the comments on an article, edit `app/views/blorgh/articles/show.html.erb` and
|
478
|
-
add this line before the "Edit" link:
|
479
|
-
|
480
|
-
```html+erb
|
481
|
-
<h3>Comments</h3>
|
482
|
-
<%= render @article.comments %>
|
483
|
-
```
|
484
|
-
|
485
|
-
This line will require there to be a `has_many` association for comments defined
|
486
|
-
on the `Blorgh::Article` model, which there isn't right now. To define one, open
|
487
|
-
`app/models/blorgh/article.rb` and add this line into the model:
|
488
|
-
|
489
|
-
```ruby
|
490
|
-
has_many :comments
|
491
|
-
```
|
492
|
-
|
493
|
-
Turning the model into this:
|
494
|
-
|
495
|
-
```ruby
|
496
|
-
module Blorgh
|
497
|
-
class Article < ActiveRecord::Base
|
498
|
-
has_many :comments
|
499
|
-
end
|
500
|
-
end
|
501
|
-
```
|
502
|
-
|
503
|
-
NOTE: Because the `has_many` is defined inside a class that is inside the
|
504
|
-
`Blorgh` module, Rails will know that you want to use the `Blorgh::Comment`
|
505
|
-
model for these objects, so there's no need to specify that using the
|
506
|
-
`:class_name` option here.
|
507
|
-
|
508
|
-
Next, there needs to be a form so that comments can be created on an article. To
|
509
|
-
add this, put this line underneath the call to `render @article.comments` in
|
510
|
-
`app/views/blorgh/articles/show.html.erb`:
|
511
|
-
|
512
|
-
```erb
|
513
|
-
<%= render "blorgh/comments/form" %>
|
514
|
-
```
|
515
|
-
|
516
|
-
Next, the partial that this line will render needs to exist. Create a new
|
517
|
-
directory at `app/views/blorgh/comments` and in it a new file called
|
518
|
-
`_form.html.erb` which has this content to create the required partial:
|
519
|
-
|
520
|
-
```html+erb
|
521
|
-
<h3>New comment</h3>
|
522
|
-
<%= form_for [@article, @article.comments.build] do |f| %>
|
523
|
-
<p>
|
524
|
-
<%= f.label :text %><br>
|
525
|
-
<%= f.text_area :text %>
|
526
|
-
</p>
|
527
|
-
<%= f.submit %>
|
528
|
-
<% end %>
|
529
|
-
```
|
530
|
-
|
531
|
-
When this form is submitted, it is going to attempt to perform a `POST` request
|
532
|
-
to a route of `/articles/:article_id/comments` within the engine. This route doesn't
|
533
|
-
exist at the moment, but can be created by changing the `resources :articles` line
|
534
|
-
inside `config/routes.rb` into these lines:
|
535
|
-
|
536
|
-
```ruby
|
537
|
-
resources :articles do
|
538
|
-
resources :comments
|
539
|
-
end
|
540
|
-
```
|
541
|
-
|
542
|
-
This creates a nested route for the comments, which is what the form requires.
|
543
|
-
|
544
|
-
The route now exists, but the controller that this route goes to does not. To
|
545
|
-
create it, run this command from the application root:
|
546
|
-
|
547
|
-
```bash
|
548
|
-
$ bin/rails g controller comments
|
549
|
-
```
|
550
|
-
|
551
|
-
This will generate the following things:
|
552
|
-
|
553
|
-
```
|
554
|
-
create app/controllers/blorgh/comments_controller.rb
|
555
|
-
invoke erb
|
556
|
-
exist app/views/blorgh/comments
|
557
|
-
invoke test_unit
|
558
|
-
create test/controllers/blorgh/comments_controller_test.rb
|
559
|
-
invoke helper
|
560
|
-
create app/helpers/blorgh/comments_helper.rb
|
561
|
-
invoke assets
|
562
|
-
invoke js
|
563
|
-
create app/assets/javascripts/blorgh/comments.js
|
564
|
-
invoke css
|
565
|
-
create app/assets/stylesheets/blorgh/comments.css
|
566
|
-
```
|
567
|
-
|
568
|
-
The form will be making a `POST` request to `/articles/:article_id/comments`, which
|
569
|
-
will correspond with the `create` action in `Blorgh::CommentsController`. This
|
570
|
-
action needs to be created, which can be done by putting the following lines
|
571
|
-
inside the class definition in `app/controllers/blorgh/comments_controller.rb`:
|
572
|
-
|
573
|
-
```ruby
|
574
|
-
def create
|
575
|
-
@article = Article.find(params[:article_id])
|
576
|
-
@comment = @article.comments.create(comment_params)
|
577
|
-
flash[:notice] = "Comment has been created!"
|
578
|
-
redirect_to articles_path
|
579
|
-
end
|
580
|
-
|
581
|
-
private
|
582
|
-
def comment_params
|
583
|
-
params.require(:comment).permit(:text)
|
584
|
-
end
|
585
|
-
```
|
586
|
-
|
587
|
-
This is the final step required to get the new comment form working. Displaying
|
588
|
-
the comments, however, is not quite right yet. If you were to create a comment
|
589
|
-
right now, you would see this error:
|
590
|
-
|
591
|
-
```
|
592
|
-
Missing partial blorgh/comments/_comment with {:handlers=>[:erb, :builder],
|
593
|
-
:formats=>[:html], :locale=>[:en, :en]}. Searched in: *
|
594
|
-
"/Users/ryan/Sites/side_projects/blorgh/test/dummy/app/views" *
|
595
|
-
"/Users/ryan/Sites/side_projects/blorgh/app/views"
|
596
|
-
```
|
597
|
-
|
598
|
-
The engine is unable to find the partial required for rendering the comments.
|
599
|
-
Rails looks first in the application's (`test/dummy`) `app/views` directory and
|
600
|
-
then in the engine's `app/views` directory. When it can't find it, it will throw
|
601
|
-
this error. The engine knows to look for `blorgh/comments/_comment` because the
|
602
|
-
model object it is receiving is from the `Blorgh::Comment` class.
|
603
|
-
|
604
|
-
This partial will be responsible for rendering just the comment text, for now.
|
605
|
-
Create a new file at `app/views/blorgh/comments/_comment.html.erb` and put this
|
606
|
-
line inside it:
|
607
|
-
|
608
|
-
```erb
|
609
|
-
<%= comment_counter + 1 %>. <%= comment.text %>
|
610
|
-
```
|
611
|
-
|
612
|
-
The `comment_counter` local variable is given to us by the `<%= render
|
613
|
-
@article.comments %>` call, which will define it automatically and increment the
|
614
|
-
counter as it iterates through each comment. It's used in this example to
|
615
|
-
display a small number next to each comment when it's created.
|
616
|
-
|
617
|
-
That completes the comment function of the blogging engine. Now it's time to use
|
618
|
-
it within an application.
|
619
|
-
|
620
|
-
Hooking Into an Application
|
621
|
-
---------------------------
|
622
|
-
|
623
|
-
Using an engine within an application is very easy. This section covers how to
|
624
|
-
mount the engine into an application and the initial setup required, as well as
|
625
|
-
linking the engine to a `User` class provided by the application to provide
|
626
|
-
ownership for articles and comments within the engine.
|
627
|
-
|
628
|
-
### Mounting the Engine
|
629
|
-
|
630
|
-
First, the engine needs to be specified inside the application's `Gemfile`. If
|
631
|
-
there isn't an application handy to test this out in, generate one using the
|
632
|
-
`rails new` command outside of the engine directory like this:
|
633
|
-
|
634
|
-
```bash
|
635
|
-
$ rails new unicorn
|
636
|
-
```
|
637
|
-
|
638
|
-
Usually, specifying the engine inside the Gemfile would be done by specifying it
|
639
|
-
as a normal, everyday gem.
|
640
|
-
|
641
|
-
```ruby
|
642
|
-
gem 'devise'
|
643
|
-
```
|
644
|
-
|
645
|
-
However, because you are developing the `blorgh` engine on your local machine,
|
646
|
-
you will need to specify the `:path` option in your `Gemfile`:
|
647
|
-
|
648
|
-
```ruby
|
649
|
-
gem 'blorgh', path: "/path/to/blorgh"
|
650
|
-
```
|
651
|
-
|
652
|
-
Then run `bundle` to install the gem.
|
653
|
-
|
654
|
-
As described earlier, by placing the gem in the `Gemfile` it will be loaded when
|
655
|
-
Rails is loaded. It will first require `lib/blorgh.rb` from the engine, then
|
656
|
-
`lib/blorgh/engine.rb`, which is the file that defines the major pieces of
|
657
|
-
functionality for the engine.
|
658
|
-
|
659
|
-
To make the engine's functionality accessible from within an application, it
|
660
|
-
needs to be mounted in that application's `config/routes.rb` file:
|
661
|
-
|
662
|
-
```ruby
|
663
|
-
mount Blorgh::Engine, at: "/blog"
|
664
|
-
```
|
665
|
-
|
666
|
-
This line will mount the engine at `/blog` in the application. Making it
|
667
|
-
accessible at `http://localhost:3000/blog` when the application runs with `rails
|
668
|
-
server`.
|
669
|
-
|
670
|
-
NOTE: Other engines, such as Devise, handle this a little differently by making
|
671
|
-
you specify custom helpers (such as `devise_for`) in the routes. These helpers
|
672
|
-
do exactly the same thing, mounting pieces of the engines's functionality at a
|
673
|
-
pre-defined path which may be customizable.
|
674
|
-
|
675
|
-
### Engine setup
|
676
|
-
|
677
|
-
The engine contains migrations for the `blorgh_articles` and `blorgh_comments`
|
678
|
-
table which need to be created in the application's database so that the
|
679
|
-
engine's models can query them correctly. To copy these migrations into the
|
680
|
-
application use this command:
|
681
|
-
|
682
|
-
```bash
|
683
|
-
$ rake blorgh:install:migrations
|
684
|
-
```
|
685
|
-
|
686
|
-
If you have multiple engines that need migrations copied over, use
|
687
|
-
`railties:install:migrations` instead:
|
688
|
-
|
689
|
-
```bash
|
690
|
-
$ rake railties:install:migrations
|
691
|
-
```
|
692
|
-
|
693
|
-
This command, when run for the first time, will copy over all the migrations
|
694
|
-
from the engine. When run the next time, it will only copy over migrations that
|
695
|
-
haven't been copied over already. The first run for this command will output
|
696
|
-
something such as this:
|
697
|
-
|
698
|
-
```bash
|
699
|
-
Copied migration [timestamp_1]_create_blorgh_articles.blorgh.rb from blorgh
|
700
|
-
Copied migration [timestamp_2]_create_blorgh_comments.blorgh.rb from blorgh
|
701
|
-
```
|
702
|
-
|
703
|
-
The first timestamp (`[timestamp_1]`) will be the current time, and the second
|
704
|
-
timestamp (`[timestamp_2]`) will be the current time plus a second. The reason
|
705
|
-
for this is so that the migrations for the engine are run after any existing
|
706
|
-
migrations in the application.
|
707
|
-
|
708
|
-
To run these migrations within the context of the application, simply run `rake
|
709
|
-
db:migrate`. When accessing the engine through `http://localhost:3000/blog`, the
|
710
|
-
articles will be empty. This is because the table created inside the application is
|
711
|
-
different from the one created within the engine. Go ahead, play around with the
|
712
|
-
newly mounted engine. You'll find that it's the same as when it was only an
|
713
|
-
engine.
|
714
|
-
|
715
|
-
If you would like to run migrations only from one engine, you can do it by
|
716
|
-
specifying `SCOPE`:
|
717
|
-
|
718
|
-
```bash
|
719
|
-
rake db:migrate SCOPE=blorgh
|
720
|
-
```
|
721
|
-
|
722
|
-
This may be useful if you want to revert engine's migrations before removing it.
|
723
|
-
To revert all migrations from blorgh engine you can run code such as:
|
724
|
-
|
725
|
-
```bash
|
726
|
-
rake db:migrate SCOPE=blorgh VERSION=0
|
727
|
-
```
|
728
|
-
|
729
|
-
### Using a Class Provided by the Application
|
730
|
-
|
731
|
-
#### Using a Model Provided by the Application
|
732
|
-
|
733
|
-
When an engine is created, it may want to use specific classes from an
|
734
|
-
application to provide links between the pieces of the engine and the pieces of
|
735
|
-
the application. In the case of the `blorgh` engine, making articles and comments
|
736
|
-
have authors would make a lot of sense.
|
737
|
-
|
738
|
-
A typical application might have a `User` class that would be used to represent
|
739
|
-
authors for an article or a comment. But there could be a case where the
|
740
|
-
application calls this class something different, such as `Person`. For this
|
741
|
-
reason, the engine should not hardcode associations specifically for a `User`
|
742
|
-
class.
|
743
|
-
|
744
|
-
To keep it simple in this case, the application will have a class called `User`
|
745
|
-
that represents the users of the application (we'll get into making this
|
746
|
-
configurable further on). It can be generated using this command inside the
|
747
|
-
application:
|
748
|
-
|
749
|
-
```bash
|
750
|
-
rails g model user name:string
|
751
|
-
```
|
752
|
-
|
753
|
-
The `rake db:migrate` command needs to be run here to ensure that our
|
754
|
-
application has the `users` table for future use.
|
755
|
-
|
756
|
-
Also, to keep it simple, the articles form will have a new text field called
|
757
|
-
`author_name`, where users can elect to put their name. The engine will then
|
758
|
-
take this name and either create a new `User` object from it, or find one that
|
759
|
-
already has that name. The engine will then associate the article with the found or
|
760
|
-
created `User` object.
|
761
|
-
|
762
|
-
First, the `author_name` text field needs to be added to the
|
763
|
-
`app/views/blorgh/articles/_form.html.erb` partial inside the engine. This can be
|
764
|
-
added above the `title` field with this code:
|
765
|
-
|
766
|
-
```html+erb
|
767
|
-
<div class="field">
|
768
|
-
<%= f.label :author_name %><br>
|
769
|
-
<%= f.text_field :author_name %>
|
770
|
-
</div>
|
771
|
-
```
|
772
|
-
|
773
|
-
Next, we need to update our `Blorgh::ArticleController#article_params` method to
|
774
|
-
permit the new form parameter:
|
775
|
-
|
776
|
-
```ruby
|
777
|
-
def article_params
|
778
|
-
params.require(:article).permit(:title, :text, :author_name)
|
779
|
-
end
|
780
|
-
```
|
781
|
-
|
782
|
-
The `Blorgh::Article` model should then have some code to convert the `author_name`
|
783
|
-
field into an actual `User` object and associate it as that article's `author`
|
784
|
-
before the article is saved. It will also need to have an `attr_accessor` set up
|
785
|
-
for this field, so that the setter and getter methods are defined for it.
|
786
|
-
|
787
|
-
To do all this, you'll need to add the `attr_accessor` for `author_name`, the
|
788
|
-
association for the author and the `before_save` call into
|
789
|
-
`app/models/blorgh/article.rb`. The `author` association will be hard-coded to the
|
790
|
-
`User` class for the time being.
|
791
|
-
|
792
|
-
```ruby
|
793
|
-
attr_accessor :author_name
|
794
|
-
belongs_to :author, class_name: "User"
|
795
|
-
|
796
|
-
before_save :set_author
|
797
|
-
|
798
|
-
private
|
799
|
-
def set_author
|
800
|
-
self.author = User.find_or_create_by(name: author_name)
|
801
|
-
end
|
802
|
-
```
|
803
|
-
|
804
|
-
By representing the `author` association's object with the `User` class, a link
|
805
|
-
is established between the engine and the application. There needs to be a way
|
806
|
-
of associating the records in the `blorgh_articles` table with the records in the
|
807
|
-
`users` table. Because the association is called `author`, there should be an
|
808
|
-
`author_id` column added to the `blorgh_articles` table.
|
809
|
-
|
810
|
-
To generate this new column, run this command within the engine:
|
811
|
-
|
812
|
-
```bash
|
813
|
-
$ bin/rails g migration add_author_id_to_blorgh_articles author_id:integer
|
814
|
-
```
|
815
|
-
|
816
|
-
NOTE: Due to the migration's name and the column specification after it, Rails
|
817
|
-
will automatically know that you want to add a column to a specific table and
|
818
|
-
write that into the migration for you. You don't need to tell it any more than
|
819
|
-
this.
|
820
|
-
|
821
|
-
This migration will need to be run on the application. To do that, it must first
|
822
|
-
be copied using this command:
|
823
|
-
|
824
|
-
```bash
|
825
|
-
$ rake blorgh:install:migrations
|
826
|
-
```
|
827
|
-
|
828
|
-
Notice that only _one_ migration was copied over here. This is because the first
|
829
|
-
two migrations were copied over the first time this command was run.
|
830
|
-
|
831
|
-
```
|
832
|
-
NOTE Migration [timestamp]_create_blorgh_articles.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
|
833
|
-
NOTE Migration [timestamp]_create_blorgh_comments.blorgh.rb from blorgh has been skipped. Migration with the same name already exists.
|
834
|
-
Copied migration [timestamp]_add_author_id_to_blorgh_articles.blorgh.rb from blorgh
|
835
|
-
```
|
836
|
-
|
837
|
-
Run the migration using:
|
838
|
-
|
839
|
-
```bash
|
840
|
-
$ rake db:migrate
|
841
|
-
```
|
842
|
-
|
843
|
-
Now with all the pieces in place, an action will take place that will associate
|
844
|
-
an author - represented by a record in the `users` table - with an article,
|
845
|
-
represented by the `blorgh_articles` table from the engine.
|
846
|
-
|
847
|
-
Finally, the author's name should be displayed on the article's page. Add this code
|
848
|
-
above the "Title" output inside `app/views/blorgh/articles/show.html.erb`:
|
849
|
-
|
850
|
-
```html+erb
|
851
|
-
<p>
|
852
|
-
<b>Author:</b>
|
853
|
-
<%= @article.author %>
|
854
|
-
</p>
|
855
|
-
```
|
856
|
-
|
857
|
-
By outputting `@article.author` using the `<%=` tag, the `to_s` method will be
|
858
|
-
called on the object. By default, this will look quite ugly:
|
859
|
-
|
860
|
-
```
|
861
|
-
#<User:0x00000100ccb3b0>
|
862
|
-
```
|
863
|
-
|
864
|
-
This is undesirable. It would be much better to have the user's name there. To
|
865
|
-
do this, add a `to_s` method to the `User` class within the application:
|
866
|
-
|
867
|
-
```ruby
|
868
|
-
def to_s
|
869
|
-
name
|
870
|
-
end
|
871
|
-
```
|
872
|
-
|
873
|
-
Now instead of the ugly Ruby object output, the author's name will be displayed.
|
874
|
-
|
875
|
-
#### Using a Controller Provided by the Application
|
876
|
-
|
877
|
-
Because Rails controllers generally share code for things like authentication
|
878
|
-
and accessing session variables, they inherit from `ApplicationController` by
|
879
|
-
default. Rails engines, however are scoped to run independently from the main
|
880
|
-
application, so each engine gets a scoped `ApplicationController`. This
|
881
|
-
namespace prevents code collisions, but often engine controllers need to access
|
882
|
-
methods in the main application's `ApplicationController`. An easy way to
|
883
|
-
provide this access is to change the engine's scoped `ApplicationController` to
|
884
|
-
inherit from the main application's `ApplicationController`. For our Blorgh
|
885
|
-
engine this would be done by changing
|
886
|
-
`app/controllers/blorgh/application_controller.rb` to look like:
|
887
|
-
|
888
|
-
```ruby
|
889
|
-
module Blorgh
|
890
|
-
class ApplicationController < ::ApplicationController
|
891
|
-
end
|
892
|
-
end
|
893
|
-
```
|
894
|
-
|
895
|
-
By default, the engine's controllers inherit from
|
896
|
-
`Blorgh::ApplicationController`. So, after making this change they will have
|
897
|
-
access to the main application's `ApplicationController`, as though they were
|
898
|
-
part of the main application.
|
899
|
-
|
900
|
-
This change does require that the engine is run from a Rails application that
|
901
|
-
has an `ApplicationController`.
|
902
|
-
|
903
|
-
### Configuring an Engine
|
904
|
-
|
905
|
-
This section covers how to make the `User` class configurable, followed by
|
906
|
-
general configuration tips for the engine.
|
907
|
-
|
908
|
-
#### Setting Configuration Settings in the Application
|
909
|
-
|
910
|
-
The next step is to make the class that represents a `User` in the application
|
911
|
-
customizable for the engine. This is because that class may not always be
|
912
|
-
`User`, as previously explained. To make this setting customizable, the engine
|
913
|
-
will have a configuration setting called `author_class` that will be used to
|
914
|
-
specify which class represents users inside the application.
|
915
|
-
|
916
|
-
To define this configuration setting, you should use a `mattr_accessor` inside
|
917
|
-
the `Blorgh` module for the engine. Add this line to `lib/blorgh.rb` inside the
|
918
|
-
engine:
|
919
|
-
|
920
|
-
```ruby
|
921
|
-
mattr_accessor :author_class
|
922
|
-
```
|
923
|
-
|
924
|
-
This method works like its brothers, `attr_accessor` and `cattr_accessor`, but
|
925
|
-
provides a setter and getter method on the module with the specified name. To
|
926
|
-
use it, it must be referenced using `Blorgh.author_class`.
|
927
|
-
|
928
|
-
The next step is to switch the `Blorgh::Article` model over to this new setting.
|
929
|
-
Change the `belongs_to` association inside this model
|
930
|
-
(`app/models/blorgh/article.rb`) to this:
|
931
|
-
|
932
|
-
```ruby
|
933
|
-
belongs_to :author, class_name: Blorgh.author_class
|
934
|
-
```
|
935
|
-
|
936
|
-
The `set_author` method in the `Blorgh::Article` model should also use this class:
|
937
|
-
|
938
|
-
```ruby
|
939
|
-
self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name)
|
940
|
-
```
|
941
|
-
|
942
|
-
To save having to call `constantize` on the `author_class` result all the time,
|
943
|
-
you could instead just override the `author_class` getter method inside the
|
944
|
-
`Blorgh` module in the `lib/blorgh.rb` file to always call `constantize` on the
|
945
|
-
saved value before returning the result:
|
946
|
-
|
947
|
-
```ruby
|
948
|
-
def self.author_class
|
949
|
-
@@author_class.constantize
|
950
|
-
end
|
951
|
-
```
|
952
|
-
|
953
|
-
This would then turn the above code for `set_author` into this:
|
954
|
-
|
955
|
-
```ruby
|
956
|
-
self.author = Blorgh.author_class.find_or_create_by(name: author_name)
|
957
|
-
```
|
958
|
-
|
959
|
-
Resulting in something a little shorter, and more implicit in its behavior. The
|
960
|
-
`author_class` method should always return a `Class` object.
|
961
|
-
|
962
|
-
Since we changed the `author_class` method to return a `Class` instead of a
|
963
|
-
`String`, we must also modify our `belongs_to` definition in the `Blorgh::Article`
|
964
|
-
model:
|
965
|
-
|
966
|
-
```ruby
|
967
|
-
belongs_to :author, class_name: Blorgh.author_class.to_s
|
968
|
-
```
|
969
|
-
|
970
|
-
To set this configuration setting within the application, an initializer should
|
971
|
-
be used. By using an initializer, the configuration will be set up before the
|
972
|
-
application starts and calls the engine's models, which may depend on this
|
973
|
-
configuration setting existing.
|
974
|
-
|
975
|
-
Create a new initializer at `config/initializers/blorgh.rb` inside the
|
976
|
-
application where the `blorgh` engine is installed and put this content in it:
|
977
|
-
|
978
|
-
```ruby
|
979
|
-
Blorgh.author_class = "User"
|
980
|
-
```
|
981
|
-
|
982
|
-
WARNING: It's very important here to use the `String` version of the class,
|
983
|
-
rather than the class itself. If you were to use the class, Rails would attempt
|
984
|
-
to load that class and then reference the related table. This could lead to
|
985
|
-
problems if the table wasn't already existing. Therefore, a `String` should be
|
986
|
-
used and then converted to a class using `constantize` in the engine later on.
|
987
|
-
|
988
|
-
Go ahead and try to create a new article. You will see that it works exactly in the
|
989
|
-
same way as before, except this time the engine is using the configuration
|
990
|
-
setting in `config/initializers/blorgh.rb` to learn what the class is.
|
991
|
-
|
992
|
-
There are now no strict dependencies on what the class is, only what the API for
|
993
|
-
the class must be. The engine simply requires this class to define a
|
994
|
-
`find_or_create_by` method which returns an object of that class, to be
|
995
|
-
associated with an article when it's created. This object, of course, should have
|
996
|
-
some sort of identifier by which it can be referenced.
|
997
|
-
|
998
|
-
#### General Engine Configuration
|
999
|
-
|
1000
|
-
Within an engine, there may come a time where you wish to use things such as
|
1001
|
-
initializers, internationalization or other configuration options. The great
|
1002
|
-
news is that these things are entirely possible, because a Rails engine shares
|
1003
|
-
much the same functionality as a Rails application. In fact, a Rails
|
1004
|
-
application's functionality is actually a superset of what is provided by
|
1005
|
-
engines!
|
1006
|
-
|
1007
|
-
If you wish to use an initializer - code that should run before the engine is
|
1008
|
-
loaded - the place for it is the `config/initializers` folder. This directory's
|
1009
|
-
functionality is explained in the [Initializers
|
1010
|
-
section](configuring.html#initializers) of the Configuring guide, and works
|
1011
|
-
precisely the same way as the `config/initializers` directory inside an
|
1012
|
-
application. The same thing goes if you want to use a standard initializer.
|
1013
|
-
|
1014
|
-
For locales, simply place the locale files in the `config/locales` directory,
|
1015
|
-
just like you would in an application.
|
1016
|
-
|
1017
|
-
Testing an engine
|
1018
|
-
-----------------
|
1019
|
-
|
1020
|
-
When an engine is generated, there is a smaller dummy application created inside
|
1021
|
-
it at `test/dummy`. This application is used as a mounting point for the engine,
|
1022
|
-
to make testing the engine extremely simple. You may extend this application by
|
1023
|
-
generating controllers, models or views from within the directory, and then use
|
1024
|
-
those to test your engine.
|
1025
|
-
|
1026
|
-
The `test` directory should be treated like a typical Rails testing environment,
|
1027
|
-
allowing for unit, functional and integration tests.
|
1028
|
-
|
1029
|
-
### Functional Tests
|
1030
|
-
|
1031
|
-
A matter worth taking into consideration when writing functional tests is that
|
1032
|
-
the tests are going to be running on an application - the `test/dummy`
|
1033
|
-
application - rather than your engine. This is due to the setup of the testing
|
1034
|
-
environment; an engine needs an application as a host for testing its main
|
1035
|
-
functionality, especially controllers. This means that if you were to make a
|
1036
|
-
typical `GET` to a controller in a controller's functional test like this:
|
1037
|
-
|
1038
|
-
```ruby
|
1039
|
-
module Blorgh
|
1040
|
-
class FooControllerTest < ActionController::TestCase
|
1041
|
-
def test_index
|
1042
|
-
get :index
|
1043
|
-
...
|
1044
|
-
end
|
1045
|
-
end
|
1046
|
-
end
|
1047
|
-
```
|
1048
|
-
|
1049
|
-
It may not function correctly. This is because the application doesn't know how
|
1050
|
-
to route these requests to the engine unless you explicitly tell it **how**. To
|
1051
|
-
do this, you must set the `@routes` instance variable to the engine's route set
|
1052
|
-
in your setup code:
|
1053
|
-
|
1054
|
-
```ruby
|
1055
|
-
module Blorgh
|
1056
|
-
class FooControllerTest < ActionController::TestCase
|
1057
|
-
setup do
|
1058
|
-
@routes = Engine.routes
|
1059
|
-
end
|
1060
|
-
|
1061
|
-
def test_index
|
1062
|
-
get :index
|
1063
|
-
...
|
1064
|
-
end
|
1065
|
-
end
|
1066
|
-
end
|
1067
|
-
```
|
1068
|
-
|
1069
|
-
This tells the application that you still want to perform a `GET` request to the
|
1070
|
-
`index` action of this controller, but you want to use the engine's route to get
|
1071
|
-
there, rather than the application's one.
|
1072
|
-
|
1073
|
-
This also ensures that the engine's URL helpers will work as expected in your
|
1074
|
-
tests.
|
1075
|
-
|
1076
|
-
Improving engine functionality
|
1077
|
-
------------------------------
|
1078
|
-
|
1079
|
-
This section explains how to add and/or override engine MVC functionality in the
|
1080
|
-
main Rails application.
|
1081
|
-
|
1082
|
-
### Overriding Models and Controllers
|
1083
|
-
|
1084
|
-
Engine model and controller classes can be extended by open classing them in the
|
1085
|
-
main Rails application (since model and controller classes are just Ruby classes
|
1086
|
-
that inherit Rails specific functionality). Open classing an Engine class
|
1087
|
-
redefines it for use in the main application. This is usually implemented by
|
1088
|
-
using the decorator pattern.
|
1089
|
-
|
1090
|
-
For simple class modifications, use `Class#class_eval`. For complex class
|
1091
|
-
modifications, consider using `ActiveSupport::Concern`.
|
1092
|
-
|
1093
|
-
#### A note on Decorators and Loading Code
|
1094
|
-
|
1095
|
-
Because these decorators are not referenced by your Rails application itself,
|
1096
|
-
Rails' autoloading system will not kick in and load your decorators. This means
|
1097
|
-
that you need to require them yourself.
|
1098
|
-
|
1099
|
-
Here is some sample code to do this:
|
1100
|
-
|
1101
|
-
```ruby
|
1102
|
-
# lib/blorgh/engine.rb
|
1103
|
-
module Blorgh
|
1104
|
-
class Engine < ::Rails::Engine
|
1105
|
-
isolate_namespace Blorgh
|
1106
|
-
|
1107
|
-
config.to_prepare do
|
1108
|
-
Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
|
1109
|
-
require_dependency(c)
|
1110
|
-
end
|
1111
|
-
end
|
1112
|
-
end
|
1113
|
-
end
|
1114
|
-
```
|
1115
|
-
|
1116
|
-
This doesn't apply to just Decorators, but anything that you add in an engine
|
1117
|
-
that isn't referenced by your main application.
|
1118
|
-
|
1119
|
-
#### Implementing Decorator Pattern Using Class#class_eval
|
1120
|
-
|
1121
|
-
**Adding** `Article#time_since_created`:
|
1122
|
-
|
1123
|
-
```ruby
|
1124
|
-
# MyApp/app/decorators/models/blorgh/article_decorator.rb
|
1125
|
-
|
1126
|
-
Blorgh::Article.class_eval do
|
1127
|
-
def time_since_created
|
1128
|
-
Time.current - created_at
|
1129
|
-
end
|
1130
|
-
end
|
1131
|
-
```
|
1132
|
-
|
1133
|
-
```ruby
|
1134
|
-
# Blorgh/app/models/article.rb
|
1135
|
-
|
1136
|
-
class Article < ActiveRecord::Base
|
1137
|
-
has_many :comments
|
1138
|
-
end
|
1139
|
-
```
|
1140
|
-
|
1141
|
-
|
1142
|
-
**Overriding** `Article#summary`:
|
1143
|
-
|
1144
|
-
```ruby
|
1145
|
-
# MyApp/app/decorators/models/blorgh/article_decorator.rb
|
1146
|
-
|
1147
|
-
Blorgh::Article.class_eval do
|
1148
|
-
def summary
|
1149
|
-
"#{title} - #{truncate(text)}"
|
1150
|
-
end
|
1151
|
-
end
|
1152
|
-
```
|
1153
|
-
|
1154
|
-
```ruby
|
1155
|
-
# Blorgh/app/models/article.rb
|
1156
|
-
|
1157
|
-
class Article < ActiveRecord::Base
|
1158
|
-
has_many :comments
|
1159
|
-
def summary
|
1160
|
-
"#{title}"
|
1161
|
-
end
|
1162
|
-
end
|
1163
|
-
```
|
1164
|
-
|
1165
|
-
#### Implementing Decorator Pattern Using ActiveSupport::Concern
|
1166
|
-
|
1167
|
-
Using `Class#class_eval` is great for simple adjustments, but for more complex
|
1168
|
-
class modifications, you might want to consider using [`ActiveSupport::Concern`]
|
1169
|
-
(http://api.rubyonrails.org/classes/ActiveSupport/Concern.html).
|
1170
|
-
ActiveSupport::Concern manages load order of interlinked dependent modules and
|
1171
|
-
classes at run time allowing you to significantly modularize your code.
|
1172
|
-
|
1173
|
-
**Adding** `Article#time_since_created` and **Overriding** `Article#summary`:
|
1174
|
-
|
1175
|
-
```ruby
|
1176
|
-
# MyApp/app/models/blorgh/article.rb
|
1177
|
-
|
1178
|
-
class Blorgh::Article < ActiveRecord::Base
|
1179
|
-
include Blorgh::Concerns::Models::Article
|
1180
|
-
|
1181
|
-
def time_since_created
|
1182
|
-
Time.current - created_at
|
1183
|
-
end
|
1184
|
-
|
1185
|
-
def summary
|
1186
|
-
"#{title} - #{truncate(text)}"
|
1187
|
-
end
|
1188
|
-
end
|
1189
|
-
```
|
1190
|
-
|
1191
|
-
```ruby
|
1192
|
-
# Blorgh/app/models/article.rb
|
1193
|
-
|
1194
|
-
class Article < ActiveRecord::Base
|
1195
|
-
include Blorgh::Concerns::Models::Article
|
1196
|
-
end
|
1197
|
-
```
|
1198
|
-
|
1199
|
-
```ruby
|
1200
|
-
# Blorgh/lib/concerns/models/article
|
1201
|
-
|
1202
|
-
module Blorgh::Concerns::Models::Article
|
1203
|
-
extend ActiveSupport::Concern
|
1204
|
-
|
1205
|
-
# 'included do' causes the included code to be evaluated in the
|
1206
|
-
# context where it is included (article.rb), rather than being
|
1207
|
-
# executed in the module's context (blorgh/concerns/models/article).
|
1208
|
-
included do
|
1209
|
-
attr_accessor :author_name
|
1210
|
-
belongs_to :author, class_name: "User"
|
1211
|
-
|
1212
|
-
before_save :set_author
|
1213
|
-
|
1214
|
-
private
|
1215
|
-
def set_author
|
1216
|
-
self.author = User.find_or_create_by(name: author_name)
|
1217
|
-
end
|
1218
|
-
end
|
1219
|
-
|
1220
|
-
def summary
|
1221
|
-
"#{title}"
|
1222
|
-
end
|
1223
|
-
|
1224
|
-
module ClassMethods
|
1225
|
-
def some_class_method
|
1226
|
-
'some class method string'
|
1227
|
-
end
|
1228
|
-
end
|
1229
|
-
end
|
1230
|
-
```
|
1231
|
-
|
1232
|
-
### Overriding Views
|
1233
|
-
|
1234
|
-
When Rails looks for a view to render, it will first look in the `app/views`
|
1235
|
-
directory of the application. If it cannot find the view there, it will check in
|
1236
|
-
the `app/views` directories of all engines that have this directory.
|
1237
|
-
|
1238
|
-
When the application is asked to render the view for `Blorgh::ArticlesController`'s
|
1239
|
-
index action, it will first look for the path
|
1240
|
-
`app/views/blorgh/articles/index.html.erb` within the application. If it cannot
|
1241
|
-
find it, it will look inside the engine.
|
1242
|
-
|
1243
|
-
You can override this view in the application by simply creating a new file at
|
1244
|
-
`app/views/blorgh/articles/index.html.erb`. Then you can completely change what
|
1245
|
-
this view would normally output.
|
1246
|
-
|
1247
|
-
Try this now by creating a new file at `app/views/blorgh/articles/index.html.erb`
|
1248
|
-
and put this content in it:
|
1249
|
-
|
1250
|
-
```html+erb
|
1251
|
-
<h1>Articles</h1>
|
1252
|
-
<%= link_to "New Article", new_article_path %>
|
1253
|
-
<% @articles.each do |article| %>
|
1254
|
-
<h2><%= article.title %></h2>
|
1255
|
-
<small>By <%= article.author %></small>
|
1256
|
-
<%= simple_format(article.text) %>
|
1257
|
-
<hr>
|
1258
|
-
<% end %>
|
1259
|
-
```
|
1260
|
-
|
1261
|
-
### Routes
|
1262
|
-
|
1263
|
-
Routes inside an engine are isolated from the application by default. This is
|
1264
|
-
done by the `isolate_namespace` call inside the `Engine` class. This essentially
|
1265
|
-
means that the application and its engines can have identically named routes and
|
1266
|
-
they will not clash.
|
1267
|
-
|
1268
|
-
Routes inside an engine are drawn on the `Engine` class within
|
1269
|
-
`config/routes.rb`, like this:
|
1270
|
-
|
1271
|
-
```ruby
|
1272
|
-
Blorgh::Engine.routes.draw do
|
1273
|
-
resources :articles
|
1274
|
-
end
|
1275
|
-
```
|
1276
|
-
|
1277
|
-
By having isolated routes such as this, if you wish to link to an area of an
|
1278
|
-
engine from within an application, you will need to use the engine's routing
|
1279
|
-
proxy method. Calls to normal routing methods such as `articles_path` may end up
|
1280
|
-
going to undesired locations if both the application and the engine have such a
|
1281
|
-
helper defined.
|
1282
|
-
|
1283
|
-
For instance, the following example would go to the application's `articles_path`
|
1284
|
-
if that template was rendered from the application, or the engine's `articles_path`
|
1285
|
-
if it was rendered from the engine:
|
1286
|
-
|
1287
|
-
```erb
|
1288
|
-
<%= link_to "Blog articles", articles_path %>
|
1289
|
-
```
|
1290
|
-
|
1291
|
-
To make this route always use the engine's `articles_path` routing helper method,
|
1292
|
-
we must call the method on the routing proxy method that shares the same name as
|
1293
|
-
the engine.
|
1294
|
-
|
1295
|
-
```erb
|
1296
|
-
<%= link_to "Blog articles", blorgh.articles_path %>
|
1297
|
-
```
|
1298
|
-
|
1299
|
-
If you wish to reference the application inside the engine in a similar way, use
|
1300
|
-
the `main_app` helper:
|
1301
|
-
|
1302
|
-
```erb
|
1303
|
-
<%= link_to "Home", main_app.root_path %>
|
1304
|
-
```
|
1305
|
-
|
1306
|
-
If you were to use this inside an engine, it would **always** go to the
|
1307
|
-
application's root. If you were to leave off the `main_app` "routing proxy"
|
1308
|
-
method call, it could potentially go to the engine's or application's root,
|
1309
|
-
depending on where it was called from.
|
1310
|
-
|
1311
|
-
If a template rendered from within an engine attempts to use one of the
|
1312
|
-
application's routing helper methods, it may result in an undefined method call.
|
1313
|
-
If you encounter such an issue, ensure that you're not attempting to call the
|
1314
|
-
application's routing methods without the `main_app` prefix from within the
|
1315
|
-
engine.
|
1316
|
-
|
1317
|
-
### Assets
|
1318
|
-
|
1319
|
-
Assets within an engine work in an identical way to a full application. Because
|
1320
|
-
the engine class inherits from `Rails::Engine`, the application will know to
|
1321
|
-
look up assets in the engine's 'app/assets' and 'lib/assets' directories.
|
1322
|
-
|
1323
|
-
Like all of the other components of an engine, the assets should be namespaced.
|
1324
|
-
This means that if you have an asset called `style.css`, it should be placed at
|
1325
|
-
`app/assets/stylesheets/[engine name]/style.css`, rather than
|
1326
|
-
`app/assets/stylesheets/style.css`. If this asset isn't namespaced, there is a
|
1327
|
-
possibility that the host application could have an asset named identically, in
|
1328
|
-
which case the application's asset would take precedence and the engine's one
|
1329
|
-
would be ignored.
|
1330
|
-
|
1331
|
-
Imagine that you did have an asset located at
|
1332
|
-
`app/assets/stylesheets/blorgh/style.css` To include this asset inside an
|
1333
|
-
application, just use `stylesheet_link_tag` and reference the asset as if it
|
1334
|
-
were inside the engine:
|
1335
|
-
|
1336
|
-
```erb
|
1337
|
-
<%= stylesheet_link_tag "blorgh/style.css" %>
|
1338
|
-
```
|
1339
|
-
|
1340
|
-
You can also specify these assets as dependencies of other assets using Asset
|
1341
|
-
Pipeline require statements in processed files:
|
1342
|
-
|
1343
|
-
```
|
1344
|
-
/*
|
1345
|
-
*= require blorgh/style
|
1346
|
-
*/
|
1347
|
-
```
|
1348
|
-
|
1349
|
-
INFO. Remember that in order to use languages like Sass or CoffeeScript, you
|
1350
|
-
should add the relevant library to your engine's `.gemspec`.
|
1351
|
-
|
1352
|
-
### Separate Assets & Precompiling
|
1353
|
-
|
1354
|
-
There are some situations where your engine's assets are not required by the
|
1355
|
-
host application. For example, say that you've created an admin functionality
|
1356
|
-
that only exists for your engine. In this case, the host application doesn't
|
1357
|
-
need to require `admin.css` or `admin.js`. Only the gem's admin layout needs
|
1358
|
-
these assets. It doesn't make sense for the host app to include
|
1359
|
-
`"blorgh/admin.css"` in its stylesheets. In this situation, you should
|
1360
|
-
explicitly define these assets for precompilation. This tells sprockets to add
|
1361
|
-
your engine assets when `rake assets:precompile` is triggered.
|
1362
|
-
|
1363
|
-
You can define assets for precompilation in `engine.rb`:
|
1364
|
-
|
1365
|
-
```ruby
|
1366
|
-
initializer "blorgh.assets.precompile" do |app|
|
1367
|
-
app.config.assets.precompile += %w(admin.css admin.js)
|
1368
|
-
end
|
1369
|
-
```
|
1370
|
-
|
1371
|
-
For more information, read the [Asset Pipeline guide](asset_pipeline.html).
|
1372
|
-
|
1373
|
-
### Other Gem Dependencies
|
1374
|
-
|
1375
|
-
Gem dependencies inside an engine should be specified inside the `.gemspec` file
|
1376
|
-
at the root of the engine. The reason is that the engine may be installed as a
|
1377
|
-
gem. If dependencies were to be specified inside the `Gemfile`, these would not
|
1378
|
-
be recognized by a traditional gem install and so they would not be installed,
|
1379
|
-
causing the engine to malfunction.
|
1380
|
-
|
1381
|
-
To specify a dependency that should be installed with the engine during a
|
1382
|
-
traditional `gem install`, specify it inside the `Gem::Specification` block
|
1383
|
-
inside the `.gemspec` file in the engine:
|
1384
|
-
|
1385
|
-
```ruby
|
1386
|
-
s.add_dependency "moo"
|
1387
|
-
```
|
1388
|
-
|
1389
|
-
To specify a dependency that should only be installed as a development
|
1390
|
-
dependency of the application, specify it like this:
|
1391
|
-
|
1392
|
-
```ruby
|
1393
|
-
s.add_development_dependency "moo"
|
1394
|
-
```
|
1395
|
-
|
1396
|
-
Both kinds of dependencies will be installed when `bundle install` is run inside
|
1397
|
-
of the application. The development dependencies for the gem will only be used
|
1398
|
-
when the tests for the engine are running.
|
1399
|
-
|
1400
|
-
Note that if you want to immediately require dependencies when the engine is
|
1401
|
-
required, you should require them before the engine's initialization. For
|
1402
|
-
example:
|
1403
|
-
|
1404
|
-
```ruby
|
1405
|
-
require 'other_engine/engine'
|
1406
|
-
require 'yet_another_engine/engine'
|
1407
|
-
|
1408
|
-
module MyEngine
|
1409
|
-
class Engine < ::Rails::Engine
|
1410
|
-
end
|
1411
|
-
end
|
1412
|
-
```
|