engineer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009-2010 Phil Smith
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,336 @@
1
+ = Engineer
2
+
3
+ It makes engines, get it?
4
+
5
+ == Explanation
6
+
7
+ Engines are rails apps which can be run from inside other rails apps.
8
+
9
+ Engineer is a small gem that lets a sufficiently normal rails application quickly become an
10
+ engine. It provides necessary bits of engine anatomy, and also gives the engine author a way to
11
+ publish changes (such as migrations) to users as the engine evolves. This has been a sticking
12
+ point in the past, and is one of the few remaining areas of engine support not yet covered by
13
+ rails itself.
14
+
15
+ Engineer targets rails 3 engines hosted inside rails 3 applications. If you are looking for rails
16
+ 2.3 support, try the engines plugin (http://rails-engines.org).
17
+
18
+ == Getting Started
19
+
20
+ Let's say you have a new rails app named +my_engine+ that you wish to package as an engine. Drop
21
+ this in your +Gemfile+ and do the bundler thing:
22
+
23
+ <tt>gem "engineer"</tt>
24
+
25
+ A new generator named <tt>engineer:install</tt> will be available; run it.
26
+ $ rails g engineer:install
27
+ exist lib
28
+ create lib/my_engine/engine.rb
29
+ create lib/my_engine.rb
30
+ create lib/generators/my_engine/install/install_generator.rb
31
+ create lib/generators/my_engine/install/templates/my_engine.rake
32
+ create lib/generators/my_engine/install/USAGE
33
+ create app/controllers/my_engine
34
+ create app/controllers/my_engine/application_controller.rb
35
+ remove app/controllers/application_controller.rb
36
+ append Rakefile
37
+ gsub config/routes.rb
38
+
39
+ The two major take-aways from this are
40
+ 1. <tt>application_controller.rb</tt> has moved under +my_engine+.
41
+ 2. Your +Rakefile+ has grown a bit.
42
+
43
+ The Gory Details below explain more deeply, but for now let's just look at the new +Rakefile+
44
+ content:
45
+ $ cat Rakefile
46
+ # ...
47
+
48
+ Engineer::Tasks.new do |gem|
49
+ gem.name = "my_engine"
50
+ gem.summary = %Q{TODO: one-line summary of your engine}
51
+ gem.description = %Q{TODO: longer description of your engine}
52
+ gem.email = "TODO"
53
+ gem.homepage = "TODO"
54
+ gem.authors = ["TODO"]
55
+ gem.require_path = 'lib'
56
+ gem.files = FileList[
57
+ "[A-Z]*",
58
+ "{app,config,lib,public,spec,test,vendor}/**/*",
59
+ "db/**/*.rb"
60
+ ]
61
+
62
+ # Include Bundler dependencies
63
+ Bundler.definition.dependencies.each do |dependency|
64
+ next if dependency.name == "engineer"
65
+
66
+ if (dependency.groups & [:default, :production, :staging]).any?
67
+ gem.add_dependency dependency.name, *dependency.requirement.as_list
68
+ else
69
+ gem.add_development_dependency dependency.name, *dependency.requirement.as_list
70
+ end
71
+ end
72
+
73
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
74
+ end
75
+
76
+ If you've used jeweler (http://github.com/technicalpickles/jeweler) before, this should look
77
+ eerily familiar. Engineer engines are shipped as gems, so engineer throws a (very light) wrapper
78
+ around jeweler for wrangling the gem-related tasks. Jeweler in turn keeps your gem's metadata in
79
+ the +Rakefile+, so here it is.
80
+
81
+ <b>Unlike jeweler, dependencies should still be declared in +Gemfile+, and not here.</b>
82
+
83
+ As you can see, your bundler dependencies will be included in the generated gemspec.
84
+
85
+ Let's make a gem:
86
+ $ rake build
87
+ (in /Users/phil/Public/code/my_engine)
88
+ Expected VERSION or VERSION.yml to exist. See version:write to create an initial one.
89
+
90
+ Whoops. Our gem needs to know what version it is, and we haven't told it. Start off at 0.0.0 by
91
+ $ rake version:write
92
+ (in /Users/phil/Public/code/my_engine)
93
+ Updated version: 0.0.0
94
+
95
+ $ rake build
96
+ (in /Users/phil/Public/code/my_engine)
97
+ Generated: my_engine.gemspec
98
+ my_engine.gemspec is valid.
99
+ rake aborted!
100
+ "FIXME" or "TODO" is not an author
101
+
102
+ (See full trace by running task with --trace)
103
+
104
+ Doh. Remember all those TODOs (summary, author, etc) in the Rakefile metadata? Go fill those out.
105
+
106
+ Once more:
107
+ $ rake build
108
+ (in /Users/phil/Public/code/my_engine)
109
+ Generated: my_engine.gemspec
110
+ my_engine.gemspec is valid.
111
+ WARNING: no rubyforge_project specified
112
+ Successfully built RubyGem
113
+ Name: my_engine
114
+ Version: 0.0.0
115
+ File: my_engine-0.0.0.gem
116
+
117
+ There we go, there's an engine gem sitting in <tt>pkg/</tt>, go nuts. See jeweler's documentation
118
+ for managing the version, pushing to gemcutter and other goodies.
119
+
120
+ == Installing Engine Gems
121
+
122
+ How about the other side of the fence? To install an engine into a host application, the host
123
+ author follows a similar workflow.
124
+
125
+ First, add a line to the +Gemfile+ and call bundler:
126
+
127
+ <tt>gem "my_engine"</tt>
128
+
129
+ A new generator will be available, named <tt>my_engine:install</tt>; run it.
130
+ $ rails g my_engine:install
131
+ exist lib/tasks
132
+ create lib/tasks/my_engine.rake
133
+ rake my_engine:assets my_engine:db:schema my_engine:db:migrate
134
+ rm -rf /.../host/public/my_engine
135
+ ln -s /.../gems/my_engine-0.0.0/public /.../host/public/my_engine
136
+ mkdir -p db/migrate
137
+ cp /tmp/20100428232715_my_engine_schema_after_create_comments.rb /.../host/db/migrate/20100428232715_my_engine_schema_after_create_comments.rb
138
+
139
+ This includes the engine's static assets in a subdirectory under the host's +public+. Voodoo in
140
+ the engine takes care of looking there for assets (see Gory Details below.) If your OS is
141
+ allergic to symlinks, the files are copied instead.
142
+
143
+ The engine's schema is also added as a new database migration without running it. The host author
144
+ is free to take a peek at it before deciding to <tt>rake db:migrate</tt> for real.
145
+
146
+ Run your pending migrations and fire up <tt>rails s</tt>, you're good to go.
147
+
148
+ == Managing Engine Gems
149
+
150
+ After installation, some new rake tasks are available:
151
+ $ rake -T my_engine
152
+ (in /Users/phil/Public/code/host)
153
+ rake my_engine:assets[copy] # Link (or copy) my_engine's static assets
154
+ rake my_engine:db:migrate # Import my_engine's new db migrations
155
+ rake my_engine:db:schema # Import my_engine's schema as a db migration
156
+ rake my_engine:db:seed # Load my_engine's seed data
157
+ rake my_engine:update # Import my_engine's assets and new db migrations
158
+
159
+ There are catch-all tasks as well:
160
+ $ rake -T engines
161
+ (in /Users/phil/Public/code/host)
162
+ rake engines:assets[copy] # Link (or copy) static assets from all engines
163
+ rake engines:db:migrate # Import new migrations from all engines
164
+ rake engines:db:seed # Load seed data from all engines
165
+ rake engines:update # Import assets and new db migrations from all engines
166
+
167
+ These let the host author manage the newly-installed (or updated!) engine. If the host
168
+ application revs the version of the engine gem, any new engine db migrations can be imported into
169
+ the host app with:
170
+ $ rake my_engine:db:migrate
171
+ (in /Users/phil/Public/code/host)
172
+ mkdir -p db/migrate
173
+ cp /tmp/20100428232715_my_engine_create_tags.rb /.../host/db/migrate/20100428232715_my_engine_create_tags.rb
174
+ cp /tmp/20100428232716_my_engine_create_taggings.rb /.../host/db/migrate/20100428232716_my_engine_create_taggings.rb
175
+
176
+ As before, this doesn't actually run any engine migrations but instead copies new ones (with mild
177
+ munging) to <tt>db/migrate</tt>.
178
+
179
+ The engine's static assets (stylesheets, images and so on) can be updated with:
180
+ $ rake my_engine:assets
181
+ (in /Users/phil/Public/code/host)
182
+ rm -rf /Users/phil/Public/code/host/public/my_engine
183
+ ln -s /.../gems/my_engine-0.0.1/public /.../host/public/my_engine
184
+
185
+ Even when using soft-links, updating the assets is important: you need the symlink pointing into
186
+ the correct gem version.
187
+
188
+ One can do both in a single shot with <tt>rake my_engine:update</tt>. Or with
189
+ <tt>rake engines:update</tt> to hit all engines at once.
190
+
191
+ == Gory Details
192
+
193
+ Rails' engine support has become robust with the release of rails 3. There are still a few pain
194
+ points which engineer tries to address. It does so by making some decisions for you, the engine
195
+ author. In the spirit of openness, the introduced voodoo is not buried inside the engineer gem
196
+ but copied into the engine app on installation. It's your gem, feel free to tailor it to your
197
+ needs.
198
+
199
+ Some intrepid adventurers will ask for an explanation of the generated engine's internals; here is
200
+ an overview.
201
+
202
+ === Database Migrations
203
+
204
+ Engine database migrations are a tricky problem. Luckily for me, some very clever people already
205
+ hammered out a workable idea, which engineer implements.
206
+
207
+ The migrations are packaged in the gem along with the rest of the engine. When the host author
208
+ updates the gem, she runs the <tt><engine_name>:db:migrate</tt> rake task (directly, or indirectly
209
+ through <tt><engine_name>:update</tt>.) This copies the engine migrations into the host
210
+ application, changing a few things on the way.
211
+
212
+ Specifically:
213
+ 1. The migration numbers are reassigned.
214
+ 2. The engine name is inserted into the new migration name.
215
+
216
+ The numbers are reassigned so that the copied migrations preserve their relative order, and yet
217
+ occur after all previous migrations in the host application. This guarantees that the host
218
+ application has a linear schema history, by making that history explicit. Put another way, the
219
+ final host migrations should reflect the evolution of the _host_ schema, not the _engine_ schema
220
+ from which it was derived.
221
+
222
+ The engine name is inserted to avoid name collisions. It allows the engine to see what migrations
223
+ have already been copied into the host app; it will not attempt to copy them again. Changing the
224
+ migration name implies changing the contained migration class name as well: the rake task will
225
+ normally take care of it.
226
+
227
+ Interested readers could also check out
228
+ * The original discussion: https://rails.lighthouseapp.com/projects/8994/tickets/2058
229
+ * James Adam's related blog post: http://interblah.net/plugin-migrations
230
+
231
+ === Schemas
232
+
233
+ It is recommended practice to avoid running a long string of migrations when setting up a new
234
+ database, since the migration process can become slow and brittle. Analogously, engines should be
235
+ able to (and can) create their schemas in the host database without running a long string of
236
+ migrations.
237
+
238
+ It is a very real use case for a host application author to add a new engine after the host schema
239
+ has been created and deployed to production. Deployment tools like cap and vlad know how to run
240
+ pending migrations at the right time, but they don't understand (out of the box) how to run an
241
+ engine-specific <tt>db:schema:load</tt>. It would be really great if they didn't have to.
242
+
243
+ Engineer engines satisfy these two requirements by importing the engine schema into the host
244
+ application as a migration. A naming convention is used to identify engine migrations that are
245
+ implied by the schema: the schema migration will be named something like
246
+ +my_engine_schema_after_create_posts+. This indicates that all engine migrations before (and
247
+ including) +create_posts+ should be considered to be already run. If there are no engine
248
+ migrations to skip (because the engine author removed them) then the name +my_engine_schema+ is
249
+ used instead.
250
+
251
+ No effort is made to make this schema migration reversible.
252
+
253
+ === Seeding
254
+
255
+ Similar to loading a schema, seeding initial database rows is another task that should only be run
256
+ once per database.
257
+
258
+ Engineer engines provide two ways to load the engine's <tt>db/seeds.rb</tt>. The first is a rake
259
+ task, which the host-level <tt>rake db:seed</tt> depends on. This is meant for new databases
260
+ being set up after the engine's schema has been incorporated into the host's. All seed rows will
261
+ be loaded together at the usual time, just as all the tables were created together.
262
+
263
+ The other way seed data can be loaded is in a migration generated on engine installation. This is
264
+ appropriate for installing a new engine into an established application, such as in a production
265
+ deploy.
266
+
267
+ === Static Assets and Asset Helpers
268
+
269
+ Separation of assets is another issue facing engines. While it is certainly useful to harness the
270
+ host's layouts and styling, engines will inescapably need their own stylesheets, scripts, etc.
271
+
272
+ Rails provides conventions about where those assets live and what they are named, and backs those
273
+ conventions up with helpers that "just work." The problem is (for example), the host and engine
274
+ master stylesheets can't both live at <tt>public/stylesheets/application.css</tt>. So we need two
275
+ separate places to keep assets, and a way of hiding this separation when it is convenient.
276
+
277
+ Many http servers serve static assets more efficiently than dynamic ones generated from frameworks
278
+ like rails. Any serious solution to the problem will need to respect this optimization. So,
279
+ assuming the web server wants to be dumb, the separate asset locations will be apparent in the
280
+ URIs. The browser only fetches the URIs the application gives it, so the application must know to
281
+ render engine asset URIs for engine assets, and host asset URIs otherwise. These URIs are created
282
+ by asset tag helpers such as +image_tag+ and +stylesheet_link_tag+.
283
+
284
+ An engine author could visit each tag and stick (for example) <tt>my_engine/</tt> in front of the
285
+ asset names, but that stinks. It also breaks the engine when run as a normal application.
286
+ Instead, the asset helpers called by the engine must create engine asset URIs only when the engine
287
+ is run as such.
288
+
289
+ Enter +asset_path+. This is an +action_controller+ configuration hook provided by rails to
290
+ control how asset URIs are generated. It can be set on a controller class, and it is inherited by
291
+ subclasses. By default, it provides rails' cache-busting ability (the <tt>?123line-noise456</tt>
292
+ on the end of your stylesheet URIs.) Engineer hijacks it to provide asset separation.
293
+
294
+ Recall that engineer's install generator moves <tt>application_controller.rb</tt> into an
295
+ engine-specific namespace: this is why. On startup, a <tt>MyEngine::Engine</tt> initializer sets
296
+ an +asset_path+ on <tt>MyEngine::ApplicationController</tt>. Since this controller is no longer
297
+ the global +ApplicationController+, we're ensured this +asset_path+ will affect not only all the
298
+ engine controllers, but only them. The initializers in <tt>MyEngine::Engine</tt> are only run
299
+ when the application is started as an engine. Thus when +my_engine+ is fired up as a normal
300
+ application, the custom +asset_path+ is not used.
301
+
302
+ Almost there. +asset_path+ can take as value either a string template (such as
303
+ <tt>"/my_engine%s"</tt>) or a lambda. If the engine and host authors were not interested in
304
+ sharing layouts and other views, <tt>"/my_engine%s"</tt> would be enough. There, all asset tags
305
+ rendered by any view from an engine controller will target the engine's assets. This goes bad
306
+ when an engine view wants to render with a host layout that includes a stylesheet: the stylesheet
307
+ URI would point into the engine, not the host.
308
+
309
+ A little more (and arguably too much) leg work can save us. The flaw is that we want the
310
+ customized URI not when requesting an engine _controller_, but when rendering an engine _view_.
311
+ If you have one around, open up <tt>lib/my_engine/engine.rb</tt>. There is another initializer in
312
+ there that duck punches <tt>ActionView::Template#render</tt>. When a template is rendered, its
313
+ +identifier+ is captured. For templates loaded from files, this is a file system path descending
314
+ from a <tt>config.paths.app.views</tt> of either the host or the engine. Thus we can distinguish
315
+ host templates from engine templates. Engine assets are generated for engine views and non-file
316
+ system views.
317
+
318
+ == Running the Tests
319
+
320
+ The (sparse) tests are written with cucumber (http://cukes.info) and can be run with just "rake".
321
+ You may need to install jeweler first.
322
+
323
+ == Thanks
324
+
325
+ This tool would not exist if the road had not already been well-paved by many others.
326
+ Specifically, James Adam's herculean effort in the rails 2.3 engines plugin has illuminated many
327
+ issues and solutions. Thanks also to the rails core team for incorporating many engine-centric
328
+ ideas into the framework, vastly simplifying what this tool needs to do. And also for just making
329
+ an awesome framework.
330
+
331
+ Finally, thanks to SEOmoz (http://seomoz.org) for letting me build this at my desk in the
332
+ afternoons instead of on the couch in the middle of the night ^_^.
333
+
334
+ == Copyright
335
+
336
+ Copyright (c) 2009-2010 Philip Smith. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "engineer"
8
+ gem.summary = %Q{Turn rails 3 applications into engines}
9
+ gem.description = "Turn your rails 3 app into an embeddable engine gem, with answers for db "\
10
+ "migrations, static assets and more."
11
+ gem.email = "phil.h.smith@gmail.com"
12
+ gem.homepage = "http://github.com/phs/engineer"
13
+ gem.authors = ["Phil Smith"]
14
+ gem.files = FileList["[A-Z]*", "{features,lib,spec}/**/*"]
15
+ gem.add_dependency "jeweler", ">= 1.4.0"
16
+ gem.add_development_dependency "rspec", ">= 1.2.9"
17
+ gem.add_development_dependency "cucumber", ">= 0.6.4"
18
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'spec/rake/spectask'
26
+ Spec::Rake::SpecTask.new(:spec) do |spec|
27
+ spec.libs << 'lib' << 'spec'
28
+ spec.spec_files = FileList['spec/**/*_spec.rb']
29
+ end
30
+
31
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.pattern = 'spec/**/*_spec.rb'
34
+ spec.rcov = true
35
+ end
36
+
37
+ task :spec => :check_dependencies
38
+
39
+ task :default => [:spec, :cucumber]
40
+
41
+ require 'rake/rdoctask'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "engineer #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
50
+
51
+ require 'cucumber/rake/task'
52
+ Cucumber::Rake::Task.new do |t|
53
+ t.cucumber_opts = %w{--format pretty}
54
+ end
55
+
56
+ desc "Remove build products"
57
+ task :clean do
58
+ rm_rf 'pkg'
59
+ end
60
+
61
+ # Some steps expect the gem to be built, so it can be added to rails projects created in tests.
62
+ task :cucumber => [:clean, :build]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,55 @@
1
+ Feature: Rake Tasks added to Engines
2
+ As an engine author
3
+ I want to pack my app as a gem the same way I do with jeweler
4
+ Since I probably already know how to use jeweler
5
+
6
+ Background:
7
+ Given I have a new rails app named my_engine, with the engineer gem
8
+ And I rails g engineer:install
9
+
10
+ Scenario: Creating initial VERSION file
11
+ When I try to rake version:bump:patch
12
+ Then I should see output:
13
+ """
14
+ Expected VERSION or VERSION.yml to exist. See version:write to create an initial one.
15
+ """
16
+
17
+ When I rake version:write
18
+ Then VERSION should contain:
19
+ """
20
+ 0.0.0
21
+ """
22
+
23
+ Scenario: Bumping versions
24
+ Given I rake version:write
25
+ When I rake version:bump:patch
26
+ Then I should see output:
27
+ """
28
+ Current version: 0.0.0
29
+ Updated version: 0.0.1
30
+ """
31
+
32
+ When I rake version:bump:major
33
+ Then I should see output:
34
+ """
35
+ Current version: 0.0.1
36
+ Updated version: 1.0.0
37
+ """
38
+
39
+ Scenario: Building a gem
40
+ Given I rake version:write
41
+ And I fill out my Rakefile gemspec
42
+ When I rake build
43
+ Then I should see a my_engine.gemspec file
44
+ And I should see a pkg/my_engine-0.0.0.gem file
45
+
46
+ And my_engine.gemspec should contain:
47
+ """
48
+ s.add_runtime_dependency(%q<rails>, ["${VERSION}"])
49
+ s.add_runtime_dependency(%q<sqlite3-ruby>, ["${VERSION}"])
50
+ """
51
+
52
+ And my_engine.gemspec should not contain:
53
+ """
54
+ s.add_runtime_dependency(%q<engineer>, ["${VERSION}"])
55
+ """
@@ -0,0 +1,47 @@
1
+ Feature: Engineer Installation
2
+ As an engine author
3
+ I want to install engineer painlessly
4
+ So that I can turn my rails app into an embeddable engine
5
+
6
+ Background:
7
+ Given I have a new rails app named my_engine, with the engineer gem
8
+
9
+ Scenario: Install engineer, see my new rake tasks
10
+ When I rails g engineer:install
11
+ Then I should see output:
12
+ """
13
+ exist lib
14
+ create lib/my_engine/engine.rb
15
+ create lib/my_engine.rb
16
+ create lib/generators/my_engine/install/install_generator.rb
17
+ create lib/generators/my_engine/install/templates/my_engine.rake
18
+ create lib/generators/my_engine/install/USAGE
19
+ create app/controllers/my_engine
20
+ create app/controllers/my_engine/application_controller.rb
21
+ remove app/controllers/application_controller.rb
22
+ append Rakefile
23
+ gsub config/routes.rb
24
+ """
25
+
26
+ And config/routes.rb should contain:
27
+ """
28
+ Rails.application.routes.draw
29
+ """
30
+
31
+ And app/controllers/my_engine/application_controller.rb should contain:
32
+ """
33
+ class MyEngine::ApplicationController
34
+ """
35
+
36
+ When I rake -T
37
+ Then I should see output:
38
+ """
39
+ rake build # Build gem
40
+ """
41
+ And I should see output:
42
+ """
43
+ rake version:bump:major # Bump the gemspec by a major version.
44
+ rake version:bump:minor # Bump the gemspec by a minor version.
45
+ rake version:bump:patch # Bump the gemspec by a patch version.
46
+ rake version:write # Writes out an explicit version.
47
+ """
@@ -0,0 +1,54 @@
1
+ Feature: Engine Installation into a Host App
2
+ As an host application author
3
+ I want to install an engine painlessly
4
+ So that I can use it in my app
5
+
6
+ Background:
7
+ Given I have a finished engine application named my_engine
8
+ And I have a new rails app named host, with the my_engine gem
9
+
10
+ Scenario: Install my_engine
11
+ When I rails g --help
12
+ Then I should see output:
13
+ """
14
+ my_engine:install
15
+ """
16
+
17
+ When I rails g my_engine:install
18
+ Then I should see output:
19
+ """
20
+ exist lib/tasks
21
+ create lib/tasks/my_engine.rake
22
+ rake my_engine:install
23
+ """
24
+
25
+ And I should see output:
26
+ """
27
+ rm -rf /${HOST_PATH}/host/public/my_engine
28
+ ln -s /${GEM_REPO}/gems/my_engine-0.0.0/public /${HOST_PATH}/host/public/my_engine
29
+ """
30
+
31
+ When I rake -T
32
+ Then I should see output:
33
+ """
34
+ rake my_engine:assets[copy] # Link (or copy) my_engine's static assets
35
+ rake my_engine:db:migrate # Import my_engine's new db migrations
36
+ rake my_engine:db:schema # Import my_engine's schema as a db migration
37
+ rake my_engine:db:seed # Load my_engine's seed data
38
+ rake my_engine:update # Import my_engine's assets and new db migrations
39
+ """
40
+
41
+ And I should see output:
42
+ """
43
+ rake engines:assets[copy] # Link (or copy) static assets from all engines
44
+ rake engines:db:migrate # Import new migrations from all engines
45
+ rake engines:db:seed # Load seed data from all engines
46
+ rake engines:update # Import assets and new db migrations from all engines
47
+ """
48
+
49
+ When I rake my_engine:assets[true]
50
+ Then I should see output:
51
+ """
52
+ rm -rf /${HOST_PATH}/host/public/my_engine
53
+ cp -r /${GEM_REPO}/gems/my_engine-0.0.0/public /${HOST_PATH}/host/public/my_engine
54
+ """
@@ -0,0 +1,153 @@
1
+ # Inspiration shamelessly stolen from
2
+ # http://gravityblast.com/2009/08/11/testing-rails-generators-with-cucumber/
3
+
4
+ require "rubygems"
5
+ gem "railties", ">= 3.0.0.beta2"
6
+
7
+ require "fileutils"
8
+ require "tempfile"
9
+
10
+ class String
11
+ def strip_ansi
12
+ gsub /\e\[\d+m/, ''
13
+ end
14
+ end
15
+
16
+ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
17
+ ENGINEER_GEM_FILE = Dir["#{PROJECT_ROOT}/pkg/engineer*gem"].first
18
+
19
+ if ENV['VERBOSE'] == 'true'
20
+ include FileUtils::Verbose
21
+ else
22
+ include FileUtils
23
+ end
24
+
25
+ module Helpers
26
+ def generate_rails_app(name = 'rails_app', gem_files = [])
27
+ @current_app = name
28
+
29
+ unless File.exists?(in_workspace "apps", name)
30
+ in_workspace "apps" do
31
+ run "rails #{name}"
32
+ end
33
+
34
+ in_workspace "apps", @current_app do
35
+ gem_files.each do |gem_file|
36
+ gem_name, version = File.basename(gem_file).match(/(.*)-(\d+\.\d+\.\d+)\.gem/)[1..2]
37
+ install_gem gem_file
38
+ File.open('Gemfile', 'a') do |gemfile|
39
+ gemfile << "gem '#{gem_name}', '#{version}'\n"
40
+ end
41
+ end
42
+
43
+ run "#{gem_home} bundle install"
44
+ end
45
+ end
46
+
47
+ cp_r in_workspace("apps", @current_app), in_current_app
48
+ end
49
+
50
+ def generate(generator, options = {})
51
+ in_current_app do
52
+ run "#{gem_home} bundle exec rails g #{generator}", options
53
+ end
54
+ end
55
+
56
+ def rake(rake_task, options = {})
57
+ in_current_app do
58
+ run "#{gem_home} bundle exec rake #{rake_task}", options
59
+ end
60
+ end
61
+
62
+ def fill_out_the_rakefile_gemspec
63
+ in_current_app do
64
+ rakefile = File.read('Rakefile')
65
+ rakefile.gsub!(
66
+ '%Q{TODO: one-line summary of your engine}', '%Q{My awesome engine}')
67
+ rakefile.gsub!(
68
+ '%Q{TODO: longer description of your engine}', '%Q{My awesome, beautiful engine}')
69
+ rakefile.gsub!('gem.email = "TODO"', 'gem.email = "me@example.com"')
70
+ rakefile.gsub!('gem.homepage = "TODO"', 'gem.homepage = "http://example.com/"')
71
+ rakefile.gsub!('gem.authors = ["TODO"]', 'gem.authors = ["Me"]')
72
+ File.open('Rakefile', 'w') { |f| f << rakefile }
73
+ end
74
+ end
75
+
76
+ def strip_wildcards(content)
77
+ Regexp.quote(content).gsub /\\\$\\\{[A-Z_]+\\\}/, '.*' # escaped ${WHAT_EVER} becomes .*
78
+ end
79
+
80
+ def latest_output
81
+ @latest_output
82
+ end
83
+
84
+ private
85
+
86
+ def install_gem(gem_file)
87
+ @installed_gems ||= {}
88
+ @installed_gems[gem_file] ||= begin
89
+ run "#{gem_home} gem install --no-rdoc --no-ri #{gem_file}"
90
+ true
91
+ end
92
+ end
93
+
94
+ def run(cmd, options = {})
95
+ log cmd
96
+ (`#{cmd} 2>&1`).tap do |output|
97
+ @latest_output = output
98
+ log output
99
+ fail unless $?.to_i == 0 or options[:may_fail]
100
+ end
101
+ end
102
+
103
+ def in_workspace(*path)
104
+ absolute_path = File.join(WORKSPACE, *path)
105
+
106
+ if block_given?
107
+ Dir.chdir(absolute_path) do
108
+ log "cd #{absolute_path}"
109
+ yield
110
+ end
111
+ else
112
+ absolute_path
113
+ end
114
+ end
115
+
116
+ def in_current_scenario(*path, &block)
117
+ in_workspace "current_scenario", *path, &block
118
+ end
119
+
120
+ def in_current_app(*path, &block)
121
+ in_current_scenario @current_app, *path, &block
122
+ end
123
+
124
+ def gem_home
125
+ @gem_home ||= begin
126
+ repo_path = in_workspace 'gemrepo'
127
+ mkdir_p repo_path
128
+ "GEM_HOME='#{repo_path}'"
129
+ end
130
+ end
131
+
132
+ def log(message)
133
+ puts message if ENV['VERBOSE'] == 'true'
134
+ end
135
+
136
+ end
137
+
138
+ # TODO join from Dir::tmpdir after https://rails.lighthouseapp.com/projects/8994/tickets/4442
139
+ WORKSPACE = File.join("/tmp", "engineer-cucumber-#{$$}").tap do |tmpdir|
140
+ mkdir_p tmpdir
141
+ mkdir_p File.join(tmpdir, 'apps')
142
+ at_exit { rm_rf tmpdir }
143
+ end
144
+
145
+ Before do
146
+ mkdir_p in_current_scenario
147
+ end
148
+
149
+ After do
150
+ rm_rf in_current_scenario
151
+ end
152
+
153
+ World(Helpers)
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/env')
2
+
3
+ Given %r{I have a new rails app named (.*), with the (.*) gems?} do |name, gems|
4
+ gem_files = gems.split(',').collect do |gem_name|
5
+ gem_name.strip!
6
+ if gem_name == 'engineer'
7
+ ENGINEER_GEM_FILE
8
+ else
9
+ Dir["#{in_current_scenario(gem_name)}/pkg/#{gem_name}-*.gem"].first
10
+ end
11
+ end
12
+
13
+ generate_rails_app name, gem_files
14
+ end
15
+
16
+ When "I rails g $generator" do |generator|
17
+ generate generator
18
+ end
19
+
20
+ When "I try to rails g $generator" do |generator|
21
+ generate generator, :may_fail => true
22
+ end
23
+
24
+ When "I rake $rake_task" do |rake_task|
25
+ rake rake_task
26
+ end
27
+
28
+ When "I try to rake $rake_task" do |rake_task|
29
+ rake rake_task, :may_fail => true
30
+ end
31
+
32
+ When "I fill out my Rakefile gemspec" do
33
+ fill_out_the_rakefile_gemspec
34
+ end
35
+
36
+ Then "I should see output:" do |command_output|
37
+ latest_output.strip_ansi.strip.should match strip_wildcards(command_output.strip)
38
+ end
39
+
40
+ Then "$file should contain:" do |file, content|
41
+ File.read(in_current_app file).should match strip_wildcards(content)
42
+ end
43
+
44
+ Then "$file should not contain:" do |file, content|
45
+ File.read(in_current_app file).should_not match strip_wildcards(content)
46
+ end
47
+
48
+ Then "I should see a $file file" do |file|
49
+ File.exists?(in_current_app file).should be_true
50
+ end
51
+
52
+ Given "I have a finished engine application named $engine" do |engine|
53
+ Given "I have a new rails app named #{engine}, with the engineer gem"
54
+ And "I rails g engineer:install"
55
+ And "I rake version:write"
56
+ And "I fill out my Rakefile gemspec"
57
+ And "I rake build"
58
+ end
@@ -0,0 +1,4 @@
1
+ class Engineer
2
+ class Tasks < ::Jeweler::Tasks
3
+ end
4
+ end
data/lib/engineer.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'jeweler'
2
+
3
+ class Engineer < Jeweler
4
+ autoload :Tasks, 'engineer/tasks'
5
+ end
@@ -0,0 +1,10 @@
1
+ Description:
2
+ Transform the application into a gem-based engine.
3
+
4
+ The major steps are:
5
+ * creating an Engine subclass under lib/
6
+ * moving ApplicationController into a submodule
7
+ * creating an installation generator for host apps
8
+ * adding jeweler-style gem tasks to the Rakefile
9
+
10
+ Please see http://github.com/phs/engineer for details
@@ -0,0 +1,101 @@
1
+ class Engineer
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ def self.source_root
6
+ @source_root ||= File.expand_path('../templates', __FILE__)
7
+ end
8
+
9
+ def create_engine_files
10
+ directory 'lib', 'lib'
11
+ end
12
+
13
+ def namespace_application_controller
14
+ unnested_path = Rails.application.paths.app.controllers.to_a.detect do |p|
15
+ File.exists? File.join(p, 'application_controller.rb')
16
+ end
17
+
18
+ return unless unnested_path
19
+
20
+ nested_path = File.join(unnested_path, app_name)
21
+ empty_directory nested_path
22
+
23
+ create_file File.join(nested_path, 'application_controller.rb') do
24
+ content = File.read File.join(unnested_path, 'application_controller.rb')
25
+ content.gsub! "class ApplicationController", "class #{app_module}::ApplicationController"
26
+ content
27
+ end
28
+
29
+ remove_file File.join(unnested_path, 'application_controller.rb')
30
+
31
+ controller_files = Rails.application.paths.app.controllers.to_a.collect do |p|
32
+ Dir[File.join(p, "**", "*_controller.rb")]
33
+ end.flatten
34
+
35
+ controller_files.reject { |f| f =~ /application_controller\.rb$/ }.each do |file|
36
+ gsub_file file, "< ApplicationController", "< #{app_module}::ApplicationController"
37
+ end
38
+ end
39
+
40
+ def append_gemspec_to_Rakefile
41
+ in_root do
42
+ unless IO.read('Rakefile') =~ /Engineer::Tasks.new/
43
+ append_file 'Rakefile' do
44
+ <<-RAKE
45
+
46
+ Engineer::Tasks.new do |gem|
47
+ gem.name = "#{app_name}"
48
+ gem.summary = %Q{TODO: one-line summary of your engine}
49
+ gem.description = %Q{TODO: longer description of your engine}
50
+ gem.email = "TODO"
51
+ gem.homepage = "TODO"
52
+ gem.authors = ["TODO"]
53
+ gem.require_path = 'lib'
54
+ gem.files = FileList[
55
+ "[A-Z]*",
56
+ "{app,config,lib,public,spec,test,vendor}/**/*",
57
+ "db/**/*.rb"
58
+ ]
59
+
60
+ # Include Bundler dependencies
61
+ Bundler.definition.dependencies.each do |dependency|
62
+ next if dependency.name == "engineer"
63
+
64
+ if (dependency.groups & [:default, :production, :staging]).any?
65
+ gem.add_dependency dependency.name, *dependency.requirement.as_list
66
+ else
67
+ gem.add_development_dependency dependency.name, *dependency.requirement.as_list
68
+ end
69
+ end
70
+
71
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
72
+ end
73
+ RAKE
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ # TODO Remove after https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4400
80
+ def tweak_route_declaration
81
+ gsub_file(File.join("config", "routes.rb"),
82
+ /#{Rails.application.class}.routes.draw/, "Rails.application.routes.draw")
83
+ end
84
+
85
+ protected
86
+
87
+ def app_name
88
+ app_module.underscore
89
+ end
90
+
91
+ def app_module
92
+ Rails.application.class.name.split('::').first
93
+ end
94
+
95
+ def engineer_version
96
+ Gem.loaded_specs["engineer"].version
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,41 @@
1
+ require "<%= app_name %><%#%>"
2
+ require "rails"
3
+
4
+ module <%= app_module %><%#%>
5
+ class Engine < Rails::Engine
6
+ ASSET_PREFIX = "<%= app_name %><%#%>"
7
+ ENGINEER_VERSION = "<%= engineer_version %><%#%>"
8
+
9
+ initializer "<%= app_name %><%#%>.action_view.identifier_collection" do
10
+ require 'action_view/template'
11
+ class ActionView::Template
12
+ def render_with_identifier_collection(view, locals, &block)
13
+ (Thread.current[:view_identifiers] ||= []).push identifier
14
+ render_without_identifier_collection(view, locals, &block)
15
+ ensure
16
+ Thread.current[:view_identifiers].pop
17
+ end
18
+
19
+ unless instance_methods.include? "render_without_identifier_collection"
20
+ alias_method_chain :render, :identifier_collection
21
+ end
22
+ end
23
+ end
24
+
25
+ def engine_view?(identifier)
26
+ @engine_views ||= Hash.new do |h, identifier|
27
+ h[identifier] = !Rails.application.paths.app.views.any? do |path|
28
+ identifier =~ /^#{Regexp.escape(path)}/
29
+ end
30
+ end
31
+ @engine_views[identifier]
32
+ end
33
+
34
+ initializer "<%= app_name %><%#%>.asset_path" do
35
+ <%= app_module %><%#%>::ApplicationController.config.asset_path = lambda do |source|
36
+ view_identifier = (Thread.current[:view_identifiers] ||= []).last
37
+ engine_view?(view_identifier) ? "/#{ASSET_PREFIX}#{source}" : source
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module <%= app_module %>
2
+ require "<%= app_name %>/engine" if defined?(Rails)
3
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Install <%= app_name %>
3
+
4
+ Example:
5
+ rails generate <%= app_name %>:install
@@ -0,0 +1,19 @@
1
+ module <%= app_module %><%#%>
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ def self.source_root
6
+ @source_root ||= File.expand_path('../templates', __FILE__)
7
+ end
8
+
9
+ def install_rake_tasks
10
+ directory '.', File.join("lib", "tasks")
11
+ end
12
+
13
+ def rake_engine_install
14
+ rake "<%= app_name %><%#%>:install"
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,167 @@
1
+ namespace "<%= app_name %><%#%>" do
2
+
3
+ # Migrate after schema in case there is no engine schema: at least we'll get the migrations.
4
+ task :install => %w{assets db:schema db:migrate db:migrate:seed}
5
+
6
+ desc "Import <%= app_name %><%#%>'s assets and new db migrations"
7
+ task :update => %w{assets db:migrate}
8
+
9
+ namespace :db do #<%# TODO: not everyone uses active record. %>
10
+
11
+ def host_migration(new_migration_name, &block)
12
+ require 'rails/generators/active_record'
13
+ db_migrate = File.join Rails.root, *%w{db migrate}
14
+ mkdir_p db_migrate unless File.exists? db_migrate
15
+
16
+ # TODO Kill after https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/4412
17
+ sleep 1 # wait for timestamp to change.
18
+
19
+ migration_number = ActiveRecord::Generators::Base.next_migration_number(db_migrate)
20
+ new_migration = File.join db_migrate, "#{migration_number}_#{new_migration_name}.rb"
21
+ tmp_migration = File.join Dir::tmpdir, "#{migration_number}_#{new_migration_name}.rb"
22
+
23
+ File.open(tmp_migration, "w") do |f|
24
+ f << block.call
25
+ end
26
+
27
+ cp tmp_migration, new_migration
28
+ end
29
+
30
+ def import_migration(original_file, new_migration_name, &block)
31
+ host_migration new_migration_name do
32
+ block.call File.read original_file
33
+ end
34
+ end
35
+
36
+ desc "Import <%= app_name %><%#%>'s new db migrations"
37
+ task :migrate do
38
+ require 'rails/generators/active_record'
39
+ db_migrate = File.join Rails.root, *%w{db migrate}
40
+
41
+ # Consider the set of engine migrations, in chronological order.
42
+ migrations = Dir[File.join <%= app_module %><%#%>::Engine.root, *%w{db migrate [0-9]*_*.rb}]
43
+ migrations.sort!
44
+
45
+ # See if the host already has a schema migration.
46
+ last_migration_in_schema = migrations.reverse.detect do |migration|
47
+ name = File.basename(migration).match(/\d+_(.*)\.rb/)[1]
48
+ Dir[File.join(db_migrate, "[0-9]*_<%= app_name %><%#%>_schema_after_#{name}.rb")].any?
49
+ end
50
+
51
+ # If so, do not import any migrations implied by the schema
52
+ # (recall slice! *removes* the indicated range, leaving the rest.)
53
+ migrations.slice! 0..migrations.index(last_migration_in_schema) if last_migration_in_schema
54
+
55
+ # Of the remainder
56
+ migrations.each do |old_migration|
57
+ old_name = File.basename(old_migration).match(/\d+_(.*)\.rb/)[1]
58
+ new_name = "<%= app_name %><%#%>_#{old_name}"
59
+
60
+ # Skip this single migration if we already have it
61
+ next if Dir[File.join(db_migrate, "[0-9]*_#{new_name}.rb")].any?
62
+
63
+ import_migration old_migration, new_name do |migration_source|
64
+ migration_source.gsub "class #{old_name.camelize}", "class #{new_name.camelize}"
65
+ end
66
+ end
67
+ end
68
+
69
+ desc "Import <%= app_name %><%#%>'s schema as a db migration"
70
+ task :schema do
71
+ schema = File.join <%= app_module %><%#%>::Engine.root, *%w{db schema.rb}
72
+ if File.exist? schema
73
+ migrations = Dir[File.join <%= app_module %><%#%>::Engine.root, *%w{db migrate [0-9]*_*.rb}]
74
+ latest_migration = migrations.sort.last
75
+
76
+ migration_name = if latest_migration
77
+ latest_migration_name = File.basename(latest_migration).match(/^\d+_(.+)\.rb$/)[1]
78
+ "<%= app_name %><%#%>_schema_after_#{latest_migration_name}"
79
+ else
80
+ "<%= app_name %><%#%>_schema"
81
+ end
82
+
83
+ import_migration schema, migration_name do |schema_source|
84
+ # Strip schema declaration
85
+ schema_source.gsub! /\A.*^ActiveRecord::Schema.define\(:version => \d+\) do/m, ''
86
+ schema_source.strip!.gsub!(/^end\Z/m, '').strip!
87
+
88
+ # Indent everything 2 and strip trailing white space
89
+ schema_source.gsub!(/^/, ' ').gsub!(/[\t ]+$/, '')
90
+
91
+ # Wrap with migration class declaration
92
+ <<-EOF
93
+ class #{migration_name.camelize} < ActiveRecord::Migration
94
+ def self.up
95
+ #{schema_source}
96
+ end
97
+ end
98
+ EOF
99
+ end
100
+ end
101
+ end
102
+
103
+ task "migrate:seed" do
104
+ migration_name = "<%= app_name %><%#%>_seed"
105
+ if File.exist? File.join(<%= app_module %><%#%>::Engine.root, 'db', 'seeds.rb')
106
+ host_migration migration_name do
107
+ <<-EOF
108
+ class #{migration_name.camelize} < ActiveRecord::Migration
109
+ def self.up
110
+ load File.join(<%= app_module %><%#%>::Engine.root, 'db', 'seeds.rb')
111
+ end
112
+ end
113
+ EOF
114
+ end
115
+ end
116
+ end
117
+
118
+ desc "Load <%= app_name %><%#%>'s seed data"
119
+ task :seed => :environment do
120
+ seed_file = File.join(<%= app_module %><%#%>::Engine.root, 'db', 'seeds.rb')
121
+ load(seed_file) if File.exist?(seed_file)
122
+ end
123
+
124
+ end
125
+
126
+ desc "Link (or copy) <%= app_name %><%#%>'s static assets"
127
+ task :assets, [:copy] => :environment do |t, args|
128
+ engine_asset_path = File.join(
129
+ Rails.application.paths.public.to_a.first,
130
+ <%= app_module %><%#%>::Engine::ASSET_PREFIX)
131
+
132
+ rm_rf engine_asset_path
133
+ host_asset_path = <%= app_module %><%#%>::Engine.paths.public.to_a.first
134
+
135
+ link = lambda do
136
+ begin
137
+ ln_s host_asset_path, engine_asset_path
138
+ true
139
+ rescue NotImplementedError
140
+ false
141
+ end
142
+ end
143
+
144
+ copy = lambda { cp_r host_asset_path, engine_asset_path }
145
+
146
+ not args[:copy] and link.call or copy.call
147
+ end
148
+
149
+ end
150
+
151
+ namespace :engines do
152
+
153
+ desc "Load seed data from all engines"
154
+ task "db:seed" => "<%= app_name %><%#%>:db:seed"
155
+
156
+ desc "Import new migrations from all engines"
157
+ task "db:migrate" => "<%= app_name %><%#%>:db:migrate"
158
+
159
+ desc "Link (or copy) static assets from all engines"
160
+ task :assets, [:copy] => "<%= app_name %><%#%>:assets"
161
+
162
+ desc "Import assets and new db migrations from all engines"
163
+ task :update => "<%= app_name %><%#%>:update"
164
+
165
+ end
166
+
167
+ task "db:seed" => "engines:db:seed"
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,13 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ gem 'jeweler', '>= 1.4.0'
6
+
7
+ require 'engineer'
8
+ require 'spec'
9
+ require 'spec/autorun'
10
+
11
+ Spec::Runner.configure do |config|
12
+
13
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: engineer
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Phil Smith
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-28 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: jeweler
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 4
30
+ - 0
31
+ version: 1.4.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rspec
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 1
43
+ - 2
44
+ - 9
45
+ version: 1.2.9
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: cucumber
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ - 6
58
+ - 4
59
+ version: 0.6.4
60
+ type: :development
61
+ version_requirements: *id003
62
+ description: Turn your rails 3 app into an embeddable engine gem, with answers for db migrations, static assets and more.
63
+ email: phil.h.smith@gmail.com
64
+ executables: []
65
+
66
+ extensions: []
67
+
68
+ extra_rdoc_files:
69
+ - LICENSE
70
+ - README.rdoc
71
+ files:
72
+ - LICENSE
73
+ - README.rdoc
74
+ - Rakefile
75
+ - VERSION
76
+ - features/engine_rake_tasks.feature
77
+ - features/engineer_install_generator.feature
78
+ - features/host_install_generator.feature
79
+ - features/support/env.rb
80
+ - features/support/steps.rb
81
+ - lib/engineer.rb
82
+ - lib/engineer/tasks.rb
83
+ - lib/generators/engineer/install/USAGE
84
+ - lib/generators/engineer/install/install_generator.rb
85
+ - lib/generators/engineer/install/templates/lib/%app_name%.rb.tt
86
+ - lib/generators/engineer/install/templates/lib/%app_name%/engine.rb.tt
87
+ - lib/generators/engineer/install/templates/lib/generators/%app_name%/install/USAGE.tt
88
+ - lib/generators/engineer/install/templates/lib/generators/%app_name%/install/install_generator.rb.tt
89
+ - lib/generators/engineer/install/templates/lib/generators/%app_name%/install/templates/%app_name%.rake.tt
90
+ - spec/spec.opts
91
+ - spec/spec_helper.rb
92
+ has_rdoc: true
93
+ homepage: http://github.com/phs/engineer
94
+ licenses: []
95
+
96
+ post_install_message:
97
+ rdoc_options:
98
+ - --charset=UTF-8
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ segments:
113
+ - 0
114
+ version: "0"
115
+ requirements: []
116
+
117
+ rubyforge_project:
118
+ rubygems_version: 1.3.6
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Turn rails 3 applications into engines
122
+ test_files:
123
+ - spec/spec_helper.rb