rails_test 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +83 -0
- data/guides/CHANGELOG.md +27 -0
- data/guides/Rakefile +77 -0
- 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 +5 -0
- 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 +59 -0
- data/guides/assets/javascripts/jquery.min.js +4 -0
- data/guides/assets/javascripts/responsive-tables.js +43 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushAS3.js +59 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushAppleScript.js +75 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushBash.js +59 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushCSharp.js +65 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushColdFusion.js +100 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushCpp.js +97 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushCss.js +91 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushDelphi.js +55 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushDiff.js +41 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushErlang.js +52 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushGroovy.js +67 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushJScript.js +52 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushJava.js +57 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushJavaFX.js +58 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPerl.js +72 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPhp.js +88 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPlain.js +33 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPowerShell.js +74 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushPython.js +64 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushRuby.js +55 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushSass.js +94 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushScala.js +51 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushSql.js +66 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushVb.js +56 -0
- data/guides/assets/javascripts/syntaxhighlighter/shBrushXml.js +69 -0
- data/guides/assets/javascripts/syntaxhighlighter/shCore.js +17 -0
- data/guides/assets/stylesheets/fixes.css +16 -0
- data/guides/assets/stylesheets/kindle.css +11 -0
- data/guides/assets/stylesheets/main.css +713 -0
- data/guides/assets/stylesheets/print.css +52 -0
- data/guides/assets/stylesheets/reset.css +43 -0
- data/guides/assets/stylesheets/responsive-tables.css +50 -0
- data/guides/assets/stylesheets/style.css +13 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCore.css +226 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreDefault.css +328 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreDjango.css +331 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreEclipse.css +339 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreEmacs.css +324 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreFadeToGrey.css +328 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreMDUltra.css +324 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreMidnight.css +324 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shCoreRDark.css +324 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeDefault.css +117 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeDjango.css +120 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeEclipse.css +128 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeEmacs.css +113 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeFadeToGrey.css +117 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeMDUltra.css +113 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeMidnight.css +113 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeRDark.css +113 -0
- data/guides/assets/stylesheets/syntaxhighlighter/shThemeRailsGuides.css +116 -0
- data/guides/bug_report_templates/action_controller_gem.rb +47 -0
- data/guides/bug_report_templates/action_controller_master.rb +56 -0
- data/guides/bug_report_templates/active_record_gem.rb +40 -0
- data/guides/bug_report_templates/active_record_master.rb +51 -0
- data/guides/rails_guides.rb +63 -0
- data/guides/rails_guides/generator.rb +248 -0
- data/guides/rails_guides/helpers.rb +53 -0
- data/guides/rails_guides/indexer.rb +68 -0
- data/guides/rails_guides/kindle.rb +119 -0
- data/guides/rails_guides/levenshtein.rb +39 -0
- data/guides/rails_guides/markdown.rb +167 -0
- data/guides/rails_guides/markdown/renderer.rb +82 -0
- data/guides/source/2_2_release_notes.md +435 -0
- data/guides/source/2_3_release_notes.md +621 -0
- data/guides/source/3_0_release_notes.md +611 -0
- data/guides/source/3_1_release_notes.md +559 -0
- data/guides/source/3_2_release_notes.md +568 -0
- data/guides/source/4_0_release_notes.md +279 -0
- data/guides/source/4_1_release_notes.md +730 -0
- data/guides/source/4_2_release_notes.md +736 -0
- data/guides/source/_license.html.erb +2 -0
- data/guides/source/_welcome.html.erb +19 -0
- data/guides/source/action_controller_overview.md +1247 -0
- data/guides/source/action_mailer_basics.md +738 -0
- data/guides/source/action_view_overview.md +1621 -0
- data/guides/source/active_job_basics.md +285 -0
- data/guides/source/active_model_basics.md +223 -0
- data/guides/source/active_record_basics.md +374 -0
- data/guides/source/active_record_callbacks.md +413 -0
- data/guides/source/active_record_migrations.md +1016 -0
- data/guides/source/active_record_postgresql.md +438 -0
- data/guides/source/active_record_querying.md +1785 -0
- data/guides/source/active_record_validations.md +1169 -0
- data/guides/source/active_support_core_extensions.md +3924 -0
- data/guides/source/active_support_instrumentation.md +497 -0
- data/guides/source/api_documentation_guidelines.md +361 -0
- data/guides/source/asset_pipeline.md +1361 -0
- data/guides/source/association_basics.md +2238 -0
- data/guides/source/caching_with_rails.md +379 -0
- data/guides/source/command_line.md +634 -0
- data/guides/source/configuring.md +1019 -0
- data/guides/source/contributing_to_ruby_on_rails.md +607 -0
- data/guides/source/credits.html.erb +80 -0
- data/guides/source/debugging_rails_applications.md +861 -0
- data/guides/source/development_dependencies_install.md +289 -0
- data/guides/source/documents.yaml +197 -0
- data/guides/source/engines.md +1401 -0
- data/guides/source/form_helpers.md +1010 -0
- data/guides/source/generators.md +676 -0
- data/guides/source/getting_started.md +2085 -0
- data/guides/source/i18n.md +1073 -0
- data/guides/source/index.html.erb +28 -0
- data/guides/source/initialization.md +704 -0
- data/guides/source/kindle/copyright.html.erb +1 -0
- data/guides/source/kindle/layout.html.erb +27 -0
- data/guides/source/kindle/rails_guides.opf.erb +52 -0
- data/guides/source/kindle/toc.html.erb +24 -0
- data/guides/source/kindle/toc.ncx.erb +64 -0
- data/guides/source/kindle/welcome.html.erb +5 -0
- data/guides/source/layout.html.erb +143 -0
- data/guides/source/layouts_and_rendering.md +1227 -0
- data/guides/source/maintenance_policy.md +75 -0
- data/guides/source/nested_model_forms.md +228 -0
- data/guides/source/plugins.md +444 -0
- data/guides/source/rails_application_templates.md +266 -0
- data/guides/source/rails_on_rack.md +332 -0
- data/guides/source/routing.md +1141 -0
- data/guides/source/ruby_on_rails_guides_guidelines.md +126 -0
- data/guides/source/security.md +1024 -0
- data/guides/source/testing.md +1055 -0
- data/guides/source/upgrading_ruby_on_rails.md +1046 -0
- data/guides/source/working_with_javascript_in_rails.md +407 -0
- data/guides/w3c_validator.rb +97 -0
- metadata +404 -0
@@ -0,0 +1,1401 @@
|
|
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
|
+
$ bin/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
|
+
$ bin/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.rb from blorgh
|
700
|
+
Copied migration [timestamp_2]_create_blorgh_comments.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.rb from blorgh has been
|
833
|
+
skipped. Migration with the same name already exists. NOTE Migration
|
834
|
+
[timestamp]_create_blorgh_comments.rb from blorgh has been skipped. Migration
|
835
|
+
with the same name already exists. Copied migration
|
836
|
+
[timestamp]_add_author_id_to_blorgh_articles.rb from blorgh
|
837
|
+
```
|
838
|
+
|
839
|
+
Run the migration using:
|
840
|
+
|
841
|
+
```bash
|
842
|
+
$ rake db:migrate
|
843
|
+
```
|
844
|
+
|
845
|
+
Now with all the pieces in place, an action will take place that will associate
|
846
|
+
an author - represented by a record in the `users` table - with an article,
|
847
|
+
represented by the `blorgh_articles` table from the engine.
|
848
|
+
|
849
|
+
Finally, the author's name should be displayed on the article's page. Add this code
|
850
|
+
above the "Title" output inside `app/views/blorgh/articles/show.html.erb`:
|
851
|
+
|
852
|
+
```html+erb
|
853
|
+
<p>
|
854
|
+
<b>Author:</b>
|
855
|
+
<%= @article.author %>
|
856
|
+
</p>
|
857
|
+
```
|
858
|
+
|
859
|
+
By outputting `@article.author` using the `<%=` tag, the `to_s` method will be
|
860
|
+
called on the object. By default, this will look quite ugly:
|
861
|
+
|
862
|
+
```
|
863
|
+
#<User:0x00000100ccb3b0>
|
864
|
+
```
|
865
|
+
|
866
|
+
This is undesirable. It would be much better to have the user's name there. To
|
867
|
+
do this, add a `to_s` method to the `User` class within the application:
|
868
|
+
|
869
|
+
```ruby
|
870
|
+
def to_s
|
871
|
+
name
|
872
|
+
end
|
873
|
+
```
|
874
|
+
|
875
|
+
Now instead of the ugly Ruby object output, the author's name will be displayed.
|
876
|
+
|
877
|
+
#### Using a Controller Provided by the Application
|
878
|
+
|
879
|
+
Because Rails controllers generally share code for things like authentication
|
880
|
+
and accessing session variables, they inherit from `ApplicationController` by
|
881
|
+
default. Rails engines, however are scoped to run independently from the main
|
882
|
+
application, so each engine gets a scoped `ApplicationController`. This
|
883
|
+
namespace prevents code collisions, but often engine controllers need to access
|
884
|
+
methods in the main application's `ApplicationController`. An easy way to
|
885
|
+
provide this access is to change the engine's scoped `ApplicationController` to
|
886
|
+
inherit from the main application's `ApplicationController`. For our Blorgh
|
887
|
+
engine this would be done by changing
|
888
|
+
`app/controllers/blorgh/application_controller.rb` to look like:
|
889
|
+
|
890
|
+
```ruby
|
891
|
+
class Blorgh::ApplicationController < ApplicationController
|
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
|
+
get :index
|
1040
|
+
```
|
1041
|
+
|
1042
|
+
It may not function correctly. This is because the application doesn't know how
|
1043
|
+
to route these requests to the engine unless you explicitly tell it **how**. To
|
1044
|
+
do this, you must also pass the `:use_route` option as a parameter on these
|
1045
|
+
requests:
|
1046
|
+
|
1047
|
+
```ruby
|
1048
|
+
get :index, use_route: :blorgh
|
1049
|
+
```
|
1050
|
+
|
1051
|
+
This tells the application that you still want to perform a `GET` request to the
|
1052
|
+
`index` action of this controller, but you want to use the engine's route to get
|
1053
|
+
there, rather than the application's one.
|
1054
|
+
|
1055
|
+
Another way to do this is to assign the `@routes` instance variable to `Engine.routes` in your test setup:
|
1056
|
+
|
1057
|
+
```ruby
|
1058
|
+
setup do
|
1059
|
+
@routes = Engine.routes
|
1060
|
+
end
|
1061
|
+
```
|
1062
|
+
|
1063
|
+
This will also ensure url helpers for the engine will work as expected in your tests.
|
1064
|
+
|
1065
|
+
Improving engine functionality
|
1066
|
+
------------------------------
|
1067
|
+
|
1068
|
+
This section explains how to add and/or override engine MVC functionality in the
|
1069
|
+
main Rails application.
|
1070
|
+
|
1071
|
+
### Overriding Models and Controllers
|
1072
|
+
|
1073
|
+
Engine model and controller classes can be extended by open classing them in the
|
1074
|
+
main Rails application (since model and controller classes are just Ruby classes
|
1075
|
+
that inherit Rails specific functionality). Open classing an Engine class
|
1076
|
+
redefines it for use in the main application. This is usually implemented by
|
1077
|
+
using the decorator pattern.
|
1078
|
+
|
1079
|
+
For simple class modifications, use `Class#class_eval`. For complex class
|
1080
|
+
modifications, consider using `ActiveSupport::Concern`.
|
1081
|
+
|
1082
|
+
#### A note on Decorators and Loading Code
|
1083
|
+
|
1084
|
+
Because these decorators are not referenced by your Rails application itself,
|
1085
|
+
Rails' autoloading system will not kick in and load your decorators. This means
|
1086
|
+
that you need to require them yourself.
|
1087
|
+
|
1088
|
+
Here is some sample code to do this:
|
1089
|
+
|
1090
|
+
```ruby
|
1091
|
+
# lib/blorgh/engine.rb
|
1092
|
+
module Blorgh
|
1093
|
+
class Engine < ::Rails::Engine
|
1094
|
+
isolate_namespace Blorgh
|
1095
|
+
|
1096
|
+
config.to_prepare do
|
1097
|
+
Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
|
1098
|
+
require_dependency(c)
|
1099
|
+
end
|
1100
|
+
end
|
1101
|
+
end
|
1102
|
+
end
|
1103
|
+
```
|
1104
|
+
|
1105
|
+
This doesn't apply to just Decorators, but anything that you add in an engine
|
1106
|
+
that isn't referenced by your main application.
|
1107
|
+
|
1108
|
+
#### Implementing Decorator Pattern Using Class#class_eval
|
1109
|
+
|
1110
|
+
**Adding** `Article#time_since_created`:
|
1111
|
+
|
1112
|
+
```ruby
|
1113
|
+
# MyApp/app/decorators/models/blorgh/article_decorator.rb
|
1114
|
+
|
1115
|
+
Blorgh::Article.class_eval do
|
1116
|
+
def time_since_created
|
1117
|
+
Time.current - created_at
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
```
|
1121
|
+
|
1122
|
+
```ruby
|
1123
|
+
# Blorgh/app/models/article.rb
|
1124
|
+
|
1125
|
+
class Article < ActiveRecord::Base
|
1126
|
+
has_many :comments
|
1127
|
+
end
|
1128
|
+
```
|
1129
|
+
|
1130
|
+
|
1131
|
+
**Overriding** `Article#summary`:
|
1132
|
+
|
1133
|
+
```ruby
|
1134
|
+
# MyApp/app/decorators/models/blorgh/article_decorator.rb
|
1135
|
+
|
1136
|
+
Blorgh::Article.class_eval do
|
1137
|
+
def summary
|
1138
|
+
"#{title} - #{truncate(text)}"
|
1139
|
+
end
|
1140
|
+
end
|
1141
|
+
```
|
1142
|
+
|
1143
|
+
```ruby
|
1144
|
+
# Blorgh/app/models/article.rb
|
1145
|
+
|
1146
|
+
class Article < ActiveRecord::Base
|
1147
|
+
has_many :comments
|
1148
|
+
def summary
|
1149
|
+
"#{title}"
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
```
|
1153
|
+
|
1154
|
+
#### Implementing Decorator Pattern Using ActiveSupport::Concern
|
1155
|
+
|
1156
|
+
Using `Class#class_eval` is great for simple adjustments, but for more complex
|
1157
|
+
class modifications, you might want to consider using [`ActiveSupport::Concern`]
|
1158
|
+
(http://edgeapi.rubyonrails.org/classes/ActiveSupport/Concern.html).
|
1159
|
+
ActiveSupport::Concern manages load order of interlinked dependent modules and
|
1160
|
+
classes at run time allowing you to significantly modularize your code.
|
1161
|
+
|
1162
|
+
**Adding** `Article#time_since_created` and **Overriding** `Article#summary`:
|
1163
|
+
|
1164
|
+
```ruby
|
1165
|
+
# MyApp/app/models/blorgh/article.rb
|
1166
|
+
|
1167
|
+
class Blorgh::Article < ActiveRecord::Base
|
1168
|
+
include Blorgh::Concerns::Models::Article
|
1169
|
+
|
1170
|
+
def time_since_created
|
1171
|
+
Time.current - created_at
|
1172
|
+
end
|
1173
|
+
|
1174
|
+
def summary
|
1175
|
+
"#{title} - #{truncate(text)}"
|
1176
|
+
end
|
1177
|
+
end
|
1178
|
+
```
|
1179
|
+
|
1180
|
+
```ruby
|
1181
|
+
# Blorgh/app/models/article.rb
|
1182
|
+
|
1183
|
+
class Article < ActiveRecord::Base
|
1184
|
+
include Blorgh::Concerns::Models::Article
|
1185
|
+
end
|
1186
|
+
```
|
1187
|
+
|
1188
|
+
```ruby
|
1189
|
+
# Blorgh/lib/concerns/models/article
|
1190
|
+
|
1191
|
+
module Blorgh::Concerns::Models::Article
|
1192
|
+
extend ActiveSupport::Concern
|
1193
|
+
|
1194
|
+
# 'included do' causes the included code to be evaluated in the
|
1195
|
+
# context where it is included (article.rb), rather than being
|
1196
|
+
# executed in the module's context (blorgh/concerns/models/article).
|
1197
|
+
included do
|
1198
|
+
attr_accessor :author_name
|
1199
|
+
belongs_to :author, class_name: "User"
|
1200
|
+
|
1201
|
+
before_save :set_author
|
1202
|
+
|
1203
|
+
private
|
1204
|
+
def set_author
|
1205
|
+
self.author = User.find_or_create_by(name: author_name)
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
def summary
|
1210
|
+
"#{title}"
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
module ClassMethods
|
1214
|
+
def some_class_method
|
1215
|
+
'some class method string'
|
1216
|
+
end
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
```
|
1220
|
+
|
1221
|
+
### Overriding Views
|
1222
|
+
|
1223
|
+
When Rails looks for a view to render, it will first look in the `app/views`
|
1224
|
+
directory of the application. If it cannot find the view there, it will check in
|
1225
|
+
the `app/views` directories of all engines that have this directory.
|
1226
|
+
|
1227
|
+
When the application is asked to render the view for `Blorgh::ArticlesController`'s
|
1228
|
+
index action, it will first look for the path
|
1229
|
+
`app/views/blorgh/articles/index.html.erb` within the application. If it cannot
|
1230
|
+
find it, it will look inside the engine.
|
1231
|
+
|
1232
|
+
You can override this view in the application by simply creating a new file at
|
1233
|
+
`app/views/blorgh/articles/index.html.erb`. Then you can completely change what
|
1234
|
+
this view would normally output.
|
1235
|
+
|
1236
|
+
Try this now by creating a new file at `app/views/blorgh/articles/index.html.erb`
|
1237
|
+
and put this content in it:
|
1238
|
+
|
1239
|
+
```html+erb
|
1240
|
+
<h1>Articles</h1>
|
1241
|
+
<%= link_to "New Article", new_article_path %>
|
1242
|
+
<% @articles.each do |article| %>
|
1243
|
+
<h2><%= article.title %></h2>
|
1244
|
+
<small>By <%= article.author %></small>
|
1245
|
+
<%= simple_format(article.text) %>
|
1246
|
+
<hr>
|
1247
|
+
<% end %>
|
1248
|
+
```
|
1249
|
+
|
1250
|
+
### Routes
|
1251
|
+
|
1252
|
+
Routes inside an engine are isolated from the application by default. This is
|
1253
|
+
done by the `isolate_namespace` call inside the `Engine` class. This essentially
|
1254
|
+
means that the application and its engines can have identically named routes and
|
1255
|
+
they will not clash.
|
1256
|
+
|
1257
|
+
Routes inside an engine are drawn on the `Engine` class within
|
1258
|
+
`config/routes.rb`, like this:
|
1259
|
+
|
1260
|
+
```ruby
|
1261
|
+
Blorgh::Engine.routes.draw do
|
1262
|
+
resources :articles
|
1263
|
+
end
|
1264
|
+
```
|
1265
|
+
|
1266
|
+
By having isolated routes such as this, if you wish to link to an area of an
|
1267
|
+
engine from within an application, you will need to use the engine's routing
|
1268
|
+
proxy method. Calls to normal routing methods such as `articles_path` may end up
|
1269
|
+
going to undesired locations if both the application and the engine have such a
|
1270
|
+
helper defined.
|
1271
|
+
|
1272
|
+
For instance, the following example would go to the application's `articles_path`
|
1273
|
+
if that template was rendered from the application, or the engine's `articles_path`
|
1274
|
+
if it was rendered from the engine:
|
1275
|
+
|
1276
|
+
```erb
|
1277
|
+
<%= link_to "Blog articles", articles_path %>
|
1278
|
+
```
|
1279
|
+
|
1280
|
+
To make this route always use the engine's `articles_path` routing helper method,
|
1281
|
+
we must call the method on the routing proxy method that shares the same name as
|
1282
|
+
the engine.
|
1283
|
+
|
1284
|
+
```erb
|
1285
|
+
<%= link_to "Blog articles", blorgh.articles_path %>
|
1286
|
+
```
|
1287
|
+
|
1288
|
+
If you wish to reference the application inside the engine in a similar way, use
|
1289
|
+
the `main_app` helper:
|
1290
|
+
|
1291
|
+
```erb
|
1292
|
+
<%= link_to "Home", main_app.root_path %>
|
1293
|
+
```
|
1294
|
+
|
1295
|
+
If you were to use this inside an engine, it would **always** go to the
|
1296
|
+
application's root. If you were to leave off the `main_app` "routing proxy"
|
1297
|
+
method call, it could potentially go to the engine's or application's root,
|
1298
|
+
depending on where it was called from.
|
1299
|
+
|
1300
|
+
If a template rendered from within an engine attempts to use one of the
|
1301
|
+
application's routing helper methods, it may result in an undefined method call.
|
1302
|
+
If you encounter such an issue, ensure that you're not attempting to call the
|
1303
|
+
application's routing methods without the `main_app` prefix from within the
|
1304
|
+
engine.
|
1305
|
+
|
1306
|
+
### Assets
|
1307
|
+
|
1308
|
+
Assets within an engine work in an identical way to a full application. Because
|
1309
|
+
the engine class inherits from `Rails::Engine`, the application will know to
|
1310
|
+
look up assets in the engine's 'app/assets' and 'lib/assets' directories.
|
1311
|
+
|
1312
|
+
Like all of the other components of an engine, the assets should be namespaced.
|
1313
|
+
This means that if you have an asset called `style.css`, it should be placed at
|
1314
|
+
`app/assets/stylesheets/[engine name]/style.css`, rather than
|
1315
|
+
`app/assets/stylesheets/style.css`. If this asset isn't namespaced, there is a
|
1316
|
+
possibility that the host application could have an asset named identically, in
|
1317
|
+
which case the application's asset would take precedence and the engine's one
|
1318
|
+
would be ignored.
|
1319
|
+
|
1320
|
+
Imagine that you did have an asset located at
|
1321
|
+
`app/assets/stylesheets/blorgh/style.css` To include this asset inside an
|
1322
|
+
application, just use `stylesheet_link_tag` and reference the asset as if it
|
1323
|
+
were inside the engine:
|
1324
|
+
|
1325
|
+
```erb
|
1326
|
+
<%= stylesheet_link_tag "blorgh/style.css" %>
|
1327
|
+
```
|
1328
|
+
|
1329
|
+
You can also specify these assets as dependencies of other assets using Asset
|
1330
|
+
Pipeline require statements in processed files:
|
1331
|
+
|
1332
|
+
```
|
1333
|
+
/*
|
1334
|
+
*= require blorgh/style
|
1335
|
+
*/
|
1336
|
+
```
|
1337
|
+
|
1338
|
+
INFO. Remember that in order to use languages like Sass or CoffeeScript, you
|
1339
|
+
should add the relevant library to your engine's `.gemspec`.
|
1340
|
+
|
1341
|
+
### Separate Assets & Precompiling
|
1342
|
+
|
1343
|
+
There are some situations where your engine's assets are not required by the
|
1344
|
+
host application. For example, say that you've created an admin functionality
|
1345
|
+
that only exists for your engine. In this case, the host application doesn't
|
1346
|
+
need to require `admin.css` or `admin.js`. Only the gem's admin layout needs
|
1347
|
+
these assets. It doesn't make sense for the host app to include
|
1348
|
+
`"blorgh/admin.css"` in its stylesheets. In this situation, you should
|
1349
|
+
explicitly define these assets for precompilation. This tells sprockets to add
|
1350
|
+
your engine assets when `rake assets:precompile` is triggered.
|
1351
|
+
|
1352
|
+
You can define assets for precompilation in `engine.rb`:
|
1353
|
+
|
1354
|
+
```ruby
|
1355
|
+
initializer "blorgh.assets.precompile" do |app|
|
1356
|
+
app.config.assets.precompile += %w(admin.css admin.js)
|
1357
|
+
end
|
1358
|
+
```
|
1359
|
+
|
1360
|
+
For more information, read the [Asset Pipeline guide](asset_pipeline.html).
|
1361
|
+
|
1362
|
+
### Other Gem Dependencies
|
1363
|
+
|
1364
|
+
Gem dependencies inside an engine should be specified inside the `.gemspec` file
|
1365
|
+
at the root of the engine. The reason is that the engine may be installed as a
|
1366
|
+
gem. If dependencies were to be specified inside the `Gemfile`, these would not
|
1367
|
+
be recognized by a traditional gem install and so they would not be installed,
|
1368
|
+
causing the engine to malfunction.
|
1369
|
+
|
1370
|
+
To specify a dependency that should be installed with the engine during a
|
1371
|
+
traditional `gem install`, specify it inside the `Gem::Specification` block
|
1372
|
+
inside the `.gemspec` file in the engine:
|
1373
|
+
|
1374
|
+
```ruby
|
1375
|
+
s.add_dependency "moo"
|
1376
|
+
```
|
1377
|
+
|
1378
|
+
To specify a dependency that should only be installed as a development
|
1379
|
+
dependency of the application, specify it like this:
|
1380
|
+
|
1381
|
+
```ruby
|
1382
|
+
s.add_development_dependency "moo"
|
1383
|
+
```
|
1384
|
+
|
1385
|
+
Both kinds of dependencies will be installed when `bundle install` is run inside
|
1386
|
+
of the application. The development dependencies for the gem will only be used
|
1387
|
+
when the tests for the engine are running.
|
1388
|
+
|
1389
|
+
Note that if you want to immediately require dependencies when the engine is
|
1390
|
+
required, you should require them before the engine's initialization. For
|
1391
|
+
example:
|
1392
|
+
|
1393
|
+
```ruby
|
1394
|
+
require 'other_engine/engine'
|
1395
|
+
require 'yet_another_engine/engine'
|
1396
|
+
|
1397
|
+
module MyEngine
|
1398
|
+
class Engine < ::Rails::Engine
|
1399
|
+
end
|
1400
|
+
end
|
1401
|
+
```
|