GitHub Sale: sign up for any paid plan this week and pay nothing until January 1, 2009!  [ hide ]

public
Description: Desert is a component framework for Rails that allows your plugins have a Rails app like directory structure, routes, migrations, and dependencies.
Homepage: http://desert.rubyforge.org
Clone URL: git://github.com/pivotal/desert.git
desert / README.rdoc
100644 296 lines (210 sloc) 8.858 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
= Desert - It doesn't get any DRYer than this
 
Desert is a Rails plugin framework that makes it easy to share models, views,
controllers, helpers, routes, and migrations across your applications.
 
With Desert, reusability doesn't come at the cost of extensibility: it's trivial to extend
the functionality of a plugin - both in your application _and_ in other plugins.
 
Classes are automatically mixed in with your own or other plugins' classes.
This allows you to make full featured composable components.
 
Desert is a replacement for Appable Plugins (http://wiki.pluginaweek.org/Appable_plugins).
 
== Anatomy of a desert plugin
 
  |-- app
  | |-- controllers
  | | |-- application.rb
  | | `-- blogs_controller.rb
  | |-- helpers
  | | |-- application_helper.rb
  | | `-- blogs_helper.rb
  | |-- models
  | | `-- user.rb
  | `-- views
  | |-- blogs
  | |-- layouts
  | | `-- users.html.erb
  | `-- users
  | |-- index.html.erb
  | `-- show.html.erb
  |-- db
  | `-- migrate
  | `-- 001_migrate_users_to_001.rb
  |-- lib
  | `-- current_user.rb
  |-- spec
  | |-- controllers
  | | `-- blogs_controller_spec.rb
  | |-- fixtures
  | |-- models
  | |-- spec_helper.rb
  | `-- views
  | `-- blogs
  `-- vendor
      `-- plugins
          `-- user
              |-- app
              | |-- controllers
              | | `-- users_controller.rb
              | |-- helpers
              | | `-- users_helper.rb
              | |-- models
              | | `-- user.rb
              | `-- views
              | `-- users
              | |-- edit.html.erb
              | |-- index.html.erb
              | |-- new.html.erb
              | `-- show.html.erb
              |-- config
              | `-- routes.rb
              |-- db
              | `-- migrate
              | `-- 001_create_users.rb
              |-- init.rb
              |-- lib
              | `-- current_user.rb
              |-- spec
              | |-- controllers
              | | `-- user_controller_spec.rb
              | |-- fixtures
              | | `-- users.yml
              | |-- models
              | | `-- user.rb
              | |-- spec_helper.rb
              | `-- views
              | `-- users
              `-- tasks
 
== Installation and Usage
 
* Install the gem
 
    sudo gem install desert
 
* Require 'desert' between 'boot' and Rails::Initializer.run in environment.rb
 
    # File: config/environment.rb
 
    require File.join(File.dirname(__FILE__), 'boot')
 
    require 'desert'
 
    Rails::Initializer.run do |config|
    end
 
NOTE: you may have to require rubygems before requiring desert.
 
* Generate your desert plugin
 
    script/generate desert_plugin my_plugin_app
 
== Manage Plugin Dependencies
 
By default, Rails loads plugins in alphabetical order, making it tedious to manage dependencies.
Desert will automatically load plugins in the proper order when you declare their dependencies like this:
 
    # File: vendor/plugins/blogs/init.rb
 
    require_plugin 'user'
    require_plugin 'will_paginate'
 
Here <tt>user</tt> and <tt>will_paginate</tt> will always be loaded before <tt>blogs<tt>. Note that any plugin can be declared as a dependency.
 
== Share Routes
 
When you share controllers, you'll want to share their routes too.
If you look in your RAILS_ROOT/config/routes.rb file you will notice that the generator added a new line to the top:
 
    map.routes_from_plugin(:my_plugin_app)
 
In the <tt>user</tt> plugin:
 
    # File: vendor/plugins/user/config/routes.rb
 
    resource :users
 
In the <tt>blogs</tt> plugin:
 
    # File: vendor/plugins/blogs/config/routes.rb
 
    resource :blogs
 
In the application:
 
    # File: config/routes.rb
 
    ActionController::Routing::Routes.draw do |map|
      map.routes_from_plugin :blogs
      map.routes_from_plugin :user
    end
 
Here the application adds the <tt>users</tt> resource from the <tt>user</tt> plugin and the <tt>blogs</tt> resource from the <tt>blogs</tt> plugin.
Notice that there is no need to call methods on map in the plugin route files, because they are instance eval'd in the map object.
 
All standard routing methods are available in your plugin's routes file, such as:
 
    namespace :admin do |admin|
      admin.resources :posts
    end
 
Desert uses a separate table to manage migration version to maintain backwards compatibility with Rails 1.x.
Your plugin app's migration live in your_plugin/db/migrate. To run migrations, follow these steps:
 
* Create a new migration in your main app
 
    script/generate migration migrate_my_plugin_to_045
 
* Add the custom `migrate_plugin` method
 
    class MigrateMyPluginTo045 < ActiveRecord::Migration
      def self.up
        migrate_plugin(:my_plugin, 20080530223548)
      end
 
      def self.down
        migrate_plugin(:my_plugin, 0)
      end
    end
 
* Run your migrations normally
 
    rake db:migrate
 
    connect "/signup", :controller => "users", :action => "signup"
 
== Share Migrations
 
Sharing models means sharing schema fragments, and that means sharing migrations:
 
In the <tt>user</tt> plugin:
 
  vendor/plugins/user/db/migrate/
    001_create_user_table.rb
 
In the <tt>blogs</tt> plugin:
 
    vendor/plugins/blogs/db/migrate/
      001_create_user_table.rb
      002_add_became_a_blogger_at_to_user.rb
 
Here the <tt>blogs</tt> plugin needs to add a column to the <tt>users</tt> table. No problem!
It just includes a migration in its <tt>db/migrate</tt> directory, just like a regular Rails application.
When the application developer installs the plugin, he migrates the plugin in his own migration:
 
<tt>application_root/db/migrate/009_install_user_and_blogs_plugins.rb</tt>
 
    class InstallUserAndBlogsPlugins < ActiveRecord::Migration
      def self.up
        migrate_plugin 'user', 1
        migrate_plugin :blogs, 2
      end
 
      def self.down
        migrate_plugin 'user', 0
        migrate_plugin :blogs, 0
      end
    end
 
Here the application migrates the <tt>user</tt> plugin to version 1 and the <tt>blogs</tt> plugin to version 2.
If a subsequent version of the plugin introduces new migrations, the application developer has full control over when to apply them to his schema.
 
== Share Views
 
To share views, just create templates and partials in the plugin's <tt>app/views</tt> directory, just as you would with a Rails application.
 
<tt>application_root/app/views/blogs/index.html.erb</tt>
 
    <%= @blog.posts.each do |post| %>
      ...
    <% end %>
 
== Customize / extend behavior in each installation
 
Say you want to create a plugin named acts_as_spiffy.
Desert allows Spiffy to have a set of features that can be reused and extended in several projects.
 
The Spiffy project has a:
 
* SpiffyController
* Spiffy model
* SpiffyHelper
* spiffy.html.erb
* SpiffyLib library class
 
The Spiffy plugin acts as its own mini Rails application. Here is the directory structure:
 
  RAILS_ROOT/vendor/plugins/spiffy/app/controllers/spiffy_controller.rb
  RAILS_ROOT/vendor/plugins/spiffy/app/models/spiffy.rb
  RAILS_ROOT/vendor/plugins/spiffy/app/helpers/spiffy_helper.rb
  RAILS_ROOT/vendor/plugins/spiffy/app/views/spiffy/spiffy.rhtml
  RAILS_ROOT/vendor/plugins/spiffy/lib/spiffy_lib.rb
 
Now, say there is a Spiffy Store rails application that uses acts_as_spiffy. The Rails app can open up any of the Spiffy classes and override any of the methods.
 
Say spiffy.rb in the Spiffy plugin is defined as:
 
  class Spiffy < ActiveRecord::Base
    def why?
      "I just am Spiffy"
    end
  end
 
The Spiffy#why method can be overridden in RAILS_ROOT/app/models/spiffy.rb
 
  class Spiffy < ActiveRecord::Base
    def why?
      "I sell Spiffy stuff"
    end
  end
 
== Running plugin tests
 
You can run your plugin tests/specs like so:
 
  rake desert:testspec:plugins PLUGIN=spiffy
 
Leaving off the PLUGIN environment variable will cause it to run all the
test/specs for all installed plugins, which may not be what you want.
 
== Running Desert Specs
 
To run specs, you need to:
 
* Make sure you have the necessary gems installed (rr, rspec)
* Install git http://git.or.cz/
* Create a database named desert_test
* Update the database.yml file in <tt>spec/rails_root/config/database.yml</tt>
* Install the dependencies
* Run the specs
 
Desert is a library that heavily monkey patches Rails. To ensure that Desert works with
multiple versions of Rails, its tests are run against the supported versions of Rails.
 
To set up the different supported versions of Rails, run
 
  rake install_dependencies
 
This will clone the Rails git repo and export the supported versions of rails into the
respective directories.
 
  rake update_dependencies
 
will update the clones repo on your machine.