parlement 0.10 → 0.11
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +11 -0
- data/MEMORY +9 -1
- data/README +5 -4
- data/app/controllers/account_controller.rb +10 -13
- data/app/controllers/application.rb +4 -5
- data/app/controllers/elt_controller.rb +9 -7
- data/app/controllers/person_controller.rb +1 -3
- data/app/controllers/subscriber_controller.rb +10 -10
- data/app/helpers/elt_helper.rb +2 -0
- data/app/models/elt.rb +28 -19
- data/app/models/mail.rb +26 -14
- data/app/models/mail_notify.rb +5 -4
- data/app/models/person.rb +11 -2
- data/app/views/account/_login.rhtml +3 -3
- data/app/views/account/_show.rhtml +12 -14
- data/app/views/elt/_choice.rhtml +3 -3
- data/app/views/elt/_elt.rhtml +4 -4
- data/app/views/elt/_list.rhtml +2 -2
- data/app/views/elt/_listByDate.rhtml +1 -1
- data/app/views/elt/_listByVote.rhtml +1 -1
- data/app/views/elt/new.rhtml +3 -3
- data/app/views/elt/show.rhtml +2 -2
- data/app/views/layouts/top.rhtml +6 -0
- data/app/views/mail_notify/publish.text.html.rhtml +1 -1
- data/app/views/person/_listElts.rhtml +5 -3
- data/app/views/person/show.rhtml +1 -2
- data/config/boot.rb +5 -4
- data/config/environment.rb +6 -4
- data/config/routes.rb +3 -2
- data/db/development_structure.sql +15 -4
- data/db/migrate/006_last_activity.rb +10 -0
- data/db/schema.rb +67 -49
- data/public/dispatch.fcgi +1 -0
- data/public/javascripts/controls.js +41 -23
- data/public/javascripts/dragdrop.js +317 -99
- data/public/javascripts/effects.js +301 -166
- data/public/javascripts/prototype.js +932 -402
- data/public/stylesheets/default.css +3 -2
- data/test/unit/elt_test.rb +13 -0
- data/test/unit/mail_test.rb +3 -1
- data/vendor/plugins/engines/CHANGELOG +203 -99
- data/vendor/plugins/engines/MIT-LICENSE +1 -1
- data/vendor/plugins/engines/README +32 -384
- data/vendor/plugins/engines/Rakefile +14 -0
- data/vendor/plugins/engines/UPGRADING +93 -0
- data/vendor/plugins/engines/about.yml +7 -0
- data/vendor/plugins/engines/generators/plugin_migration/USAGE +45 -0
- data/vendor/plugins/engines/generators/plugin_migration/plugin_migration_generator.rb +79 -0
- data/vendor/plugins/engines/generators/plugin_migration/templates/plugin_migration.erb +13 -0
- data/vendor/plugins/engines/init.rb +34 -47
- data/vendor/plugins/engines/install.rb +32 -0
- data/vendor/plugins/engines/lib/engines/{ruby_extensions.rb → deprecated_config_support.rb} +135 -113
- data/vendor/plugins/engines/lib/engines/plugin.rb +214 -0
- data/vendor/plugins/engines/lib/engines/plugin_list.rb +31 -0
- data/vendor/plugins/engines/lib/engines/plugin_migrator.rb +60 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/active_record.rb +19 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/dependencies.rb +143 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/migrations.rb +155 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/public_asset_helpers.rb +116 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/rails.rb +20 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/rails_initializer.rb +86 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/routing.rb +77 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions/templates.rb +140 -0
- data/vendor/plugins/engines/lib/engines/rails_extensions.rb +6 -0
- data/vendor/plugins/engines/lib/engines/testing.rb +88 -0
- data/vendor/plugins/engines/lib/engines.rb +281 -425
- data/vendor/plugins/engines/tasks/engines.rake +108 -137
- metadata +218 -250
- data/db/ROOT/perso.txt +0 -214
- data/public/images/indicator.gif +0 -0
- data/public/images/orange_by_darren_Hester_350o.jpg +0 -0
- data/public/images/smile.png +0 -0
- data/vendor/plugins/engines/generators/engine/USAGE +0 -26
- data/vendor/plugins/engines/generators/engine/engine_generator.rb +0 -199
- data/vendor/plugins/engines/generators/engine/templates/README +0 -85
- data/vendor/plugins/engines/generators/engine/templates/init_engine.erb +0 -15
- data/vendor/plugins/engines/generators/engine/templates/install.erb +0 -4
- data/vendor/plugins/engines/generators/engine/templates/lib/engine.erb +0 -6
- data/vendor/plugins/engines/generators/engine/templates/licenses/GPL +0 -18
- data/vendor/plugins/engines/generators/engine/templates/licenses/LGPL +0 -19
- data/vendor/plugins/engines/generators/engine/templates/licenses/MIT +0 -22
- data/vendor/plugins/engines/generators/engine/templates/licenses/None +0 -1
- data/vendor/plugins/engines/generators/engine/templates/public/javascripts/engine.js +0 -0
- data/vendor/plugins/engines/generators/engine/templates/public/stylesheets/engine.css +0 -0
- data/vendor/plugins/engines/generators/engine/templates/tasks/engine.rake +0 -0
- data/vendor/plugins/engines/generators/engine/templates/test/test_helper.erb +0 -17
- data/vendor/plugins/engines/lib/bundles/require_resource.rb +0 -124
- data/vendor/plugins/engines/lib/bundles.rb +0 -77
- data/vendor/plugins/engines/lib/engines/action_mailer_extensions.rb +0 -140
- data/vendor/plugins/engines/lib/engines/action_view_extensions.rb +0 -141
- data/vendor/plugins/engines/lib/engines/active_record_extensions.rb +0 -21
- data/vendor/plugins/engines/lib/engines/dependencies_extensions.rb +0 -129
- data/vendor/plugins/engines/lib/engines/migration_extensions.rb +0 -53
- data/vendor/plugins/engines/lib/engines/routing_extensions.rb +0 -28
- data/vendor/plugins/engines/lib/engines/testing_extensions.rb +0 -327
- data/vendor/plugins/engines/tasks/deprecated_engines.rake +0 -7
- data/vendor/plugins/engines/test/action_view_extensions_test.rb +0 -9
- data/vendor/plugins/engines/test/ruby_extensions_test.rb +0 -115
- data/vendor/plugins/guid/README.TXT +0 -29
- data/vendor/plugins/guid/init.rb +0 -30
- data/vendor/plugins/guid/lib/usesguid.rb +0 -37
- data/vendor/plugins/guid/lib/uuid22.rb +0 -43
- data/vendor/plugins/guid/lib/uuidtools.rb +0 -572
- data/vendor/plugins/responds_to_parent/MIT-LICENSE +0 -20
- data/vendor/plugins/responds_to_parent/README +0 -42
- data/vendor/plugins/responds_to_parent/Rakefile +0 -22
- data/vendor/plugins/responds_to_parent/init.rb +0 -1
- data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +0 -46
- data/vendor/plugins/responds_to_parent/test/responds_to_parent_test.rb +0 -115
@@ -0,0 +1,214 @@
|
|
1
|
+
# An instance of Plugin is created for each plugin loaded by Rails, and
|
2
|
+
# stored in the <tt>Rails.plugins</tt> PluginList
|
3
|
+
# (see Engines::RailsExtensions::RailsInitializer for more details).
|
4
|
+
#
|
5
|
+
# Once the engines plugin is loaded, other plugins can take advantage of
|
6
|
+
# their own instances by accessing either Engines.current, or the preferred mechanism
|
7
|
+
#
|
8
|
+
# Rails.plugins[:plugin_name]
|
9
|
+
#
|
10
|
+
# Useful properties of this object include Plugin#version, which plugin developers
|
11
|
+
# can set in their <tt>init.rb</tt> scripts:
|
12
|
+
#
|
13
|
+
# Rails.plugins[:my_plugin].version = "1.4.2"
|
14
|
+
#
|
15
|
+
# Plugin developers can also access the contents of their <tt>about.yml</tt> files
|
16
|
+
# via Plugin#about, which returns a Hash if the <tt>about.yml</tt> file exists for
|
17
|
+
# this plugin. Note that if <tt>about.yml</tt> contains a "version" key, it will
|
18
|
+
# automatically be loaded into the <tt>version</tt> attribute described above.
|
19
|
+
#
|
20
|
+
# If this plugin contains paths in directories other than <tt>app/controllers</tt>,
|
21
|
+
# <tt>app/helpers</tt>, <tt>app/models</tt> and <tt>components</tt>, authors can
|
22
|
+
# declare this by adding extra paths to #code_paths:
|
23
|
+
#
|
24
|
+
# Rails.plugin[:my_plugin].code_paths << "app/sweepers" << "vendor/my_lib"
|
25
|
+
#
|
26
|
+
# Other properties of the Plugin instance can also be set.
|
27
|
+
class Plugin
|
28
|
+
|
29
|
+
# The name of this plugin
|
30
|
+
attr_accessor :name
|
31
|
+
|
32
|
+
# The directory in which this plugin is located
|
33
|
+
attr_accessor :root
|
34
|
+
|
35
|
+
# The version of this plugin
|
36
|
+
attr_accessor :version
|
37
|
+
|
38
|
+
# The about.yml information as a Hash, if it exists
|
39
|
+
attr_accessor :about
|
40
|
+
|
41
|
+
# Plugins can add code paths to this attribute in init.rb if they
|
42
|
+
# need plugin directories to be added to the load path, i.e.
|
43
|
+
#
|
44
|
+
# plugin.code_paths << 'app/other_classes'
|
45
|
+
#
|
46
|
+
# Defaults to ["app/controllers", "app/helpers", "app/models", "components"]
|
47
|
+
# (see #default_code_paths). NOTE: if you want to set this, you must
|
48
|
+
# ensure that the engines plugin is loaded before any plugins which
|
49
|
+
# reference this since it's not available before the engines plugin has worked
|
50
|
+
# its magic.
|
51
|
+
attr_accessor :code_paths
|
52
|
+
|
53
|
+
# Plugins can add paths to this attribute in init.rb if they need
|
54
|
+
# controllers loaded from additional locations. See also #default_controller_paths, and
|
55
|
+
# the caveat surrounding the #code_paths accessor.
|
56
|
+
attr_accessor :controller_paths
|
57
|
+
|
58
|
+
# The directory in this plugin to mirror into the shared directory
|
59
|
+
# under +public+. See Engines.initialize_base_public_directory
|
60
|
+
# for more information.
|
61
|
+
#
|
62
|
+
# Defaults to "assets" (see default_public_directory).
|
63
|
+
attr_accessor :public_directory
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
# The default set of code paths which will be added to $LOAD_PATH
|
68
|
+
# and Dependencies.load_paths
|
69
|
+
def default_code_paths
|
70
|
+
# lib will actually be removed from the load paths when we call
|
71
|
+
# uniq! in #inject_into_load_paths, but it's important to keep it
|
72
|
+
# around (for the documentation tasks, for instance).
|
73
|
+
%w(app/controllers app/helpers app/models components lib)
|
74
|
+
end
|
75
|
+
|
76
|
+
# The default set of code paths which will be added to the routing system
|
77
|
+
def default_controller_paths
|
78
|
+
%w(app/controllers components)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Attempts to detect the directory to use for public files.
|
82
|
+
# If +assets+ exists in the plugin, this will be used. If +assets+ is missing
|
83
|
+
# but +public+ is found, +public+ will be used.
|
84
|
+
def default_public_directory
|
85
|
+
%w(assets public).select { |dir| File.directory?(File.join(root, dir)) }.first || "assets"
|
86
|
+
end
|
87
|
+
|
88
|
+
public
|
89
|
+
|
90
|
+
# Creates a new Plugin instance, and loads any other data from <tt>about.yml</tt>
|
91
|
+
def initialize(name, path)
|
92
|
+
@name = name
|
93
|
+
@root = path
|
94
|
+
|
95
|
+
@code_paths = default_code_paths
|
96
|
+
@controller_paths = default_controller_paths
|
97
|
+
@public_directory = default_public_directory
|
98
|
+
|
99
|
+
load_about_information
|
100
|
+
end
|
101
|
+
|
102
|
+
# Load the information from <tt>about.yml</tt>. This Hash is then accessible
|
103
|
+
# from #about.
|
104
|
+
#
|
105
|
+
# If <tt>about.yml</tt> includes a "version", this will be assigned
|
106
|
+
# automatically into #version.
|
107
|
+
def load_about_information
|
108
|
+
about_path = File.join(self.root, 'about.yml')
|
109
|
+
if File.exist?(about_path)
|
110
|
+
@about = YAML.load(File.open(about_path).read)
|
111
|
+
@about.stringify_keys!
|
112
|
+
@version = @about["version"]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Load the plugin. Since Rails takes care of evaluating <tt>init.rb</tt> and
|
117
|
+
# adding +lib+ to the <tt>$LOAD_PATH</tt>, we don't need to do that here (see
|
118
|
+
# Engines::RailsExtensions::RailsInitializer.load_plugins_with_engine_additions).
|
119
|
+
#
|
120
|
+
# Here we add controller/helper code to the appropriate load paths (see
|
121
|
+
# #inject_into_load_path) and mirror the plugin assets into the shared public
|
122
|
+
# directory (#mirror_public_assets).
|
123
|
+
def load
|
124
|
+
logger.debug "Plugin '#{name}': starting load."
|
125
|
+
|
126
|
+
inject_into_load_path
|
127
|
+
mirror_public_assets
|
128
|
+
|
129
|
+
logger.debug "Plugin '#{name}': loaded."
|
130
|
+
end
|
131
|
+
|
132
|
+
# Adds all directories in the +app+ and +lib+ directories within the engine
|
133
|
+
# to the three relevant load paths mechanism that Rails might use:
|
134
|
+
#
|
135
|
+
# * <tt>$LOAD_PATH</tt>
|
136
|
+
# * <tt>Dependencies.load_paths</tt>
|
137
|
+
# * <tt>ActionController::Routing.controller_paths</tt>
|
138
|
+
#
|
139
|
+
def inject_into_load_path
|
140
|
+
|
141
|
+
load_path_index = $LOAD_PATH.index(Engines.rails_final_load_path)
|
142
|
+
dependency_index = ::Dependencies.load_paths.index(Engines.rails_final_dependency_load_path)
|
143
|
+
|
144
|
+
# Add relevant paths under the engine root to the load path
|
145
|
+
code_paths.map { |p| File.join(root, p) }.each do |path|
|
146
|
+
if File.directory?(path)
|
147
|
+
# Add to the load paths
|
148
|
+
$LOAD_PATH.insert(load_path_index + 1, path)
|
149
|
+
# Add to the dependency system, for autoloading.
|
150
|
+
::Dependencies.load_paths.insert(dependency_index + 1, path)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Add controllers to the Routing system specifically. We actually add our paths
|
155
|
+
# to the configuration too, since routing is started AFTER plugins are. Plugins
|
156
|
+
# which are loaded by engines specifically (i.e. because of the '*' in
|
157
|
+
# +config.plugins+) will need their paths added directly to the routing system,
|
158
|
+
# since at that point it has already been configured.
|
159
|
+
controller_paths.map { |p| File.join(root, p) }.each do |path|
|
160
|
+
if File.directory?(path)
|
161
|
+
ActionController::Routing.controller_paths << path
|
162
|
+
Rails.configuration.controller_paths << path
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
$LOAD_PATH.uniq!
|
167
|
+
::Dependencies.load_paths.uniq!
|
168
|
+
ActionController::Routing.controller_paths.uniq!
|
169
|
+
Rails.configuration.controller_paths.uniq!
|
170
|
+
end
|
171
|
+
|
172
|
+
# Replicates the subdirectories under the plugins's +assets+ (or +public+) directory into
|
173
|
+
# the corresponding public directory. See also Plugin#public_directory for more.
|
174
|
+
def mirror_public_assets
|
175
|
+
|
176
|
+
begin
|
177
|
+
source = File.join(root, self.public_directory)
|
178
|
+
# if there is no public directory, just return after this file
|
179
|
+
return if !File.exist?(source)
|
180
|
+
|
181
|
+
logger.debug "Attempting to copy plugin plugin asset files from '#{source}' to '#{Engines.public_directory}'"
|
182
|
+
|
183
|
+
Engines.mirror_files_from(source, File.join(Engines.public_directory, name))
|
184
|
+
|
185
|
+
rescue Exception => e
|
186
|
+
logger.warn "WARNING: Couldn't create the public file structure for plugin '#{name}'; Error follows:"
|
187
|
+
logger.warn e
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# The path to this plugin's public files
|
192
|
+
def public_asset_directory
|
193
|
+
"#{File.basename(Engines.public_directory)}/#{name}"
|
194
|
+
end
|
195
|
+
|
196
|
+
# The directory containing this plugin's migrations (<tt>plugin/db/migrate</tt>)
|
197
|
+
def migration_directory
|
198
|
+
File.join(self.root, 'db', 'migrate')
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns the version number of the latest migration for this plugin. Returns
|
202
|
+
# nil if this plugin has no migrations.
|
203
|
+
def latest_migration
|
204
|
+
migrations = Dir[migration_directory+"/*.rb"]
|
205
|
+
return nil if migrations.empty?
|
206
|
+
migrations.map { |p| File.basename(p) }.sort.last.match(/0*(\d+)\_/)[1].to_i
|
207
|
+
end
|
208
|
+
|
209
|
+
# Migrate this plugin to the given version. See Engines::PluginMigrator for more
|
210
|
+
# information.
|
211
|
+
def migrate(version = nil)
|
212
|
+
Engines::PluginMigrator.migrate_plugin(self, version)
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# The PluginList class is an array, enhanced to allow access to loaded plugins
|
2
|
+
# by name, and iteration over loaded plugins in order of priority. This array is used
|
3
|
+
# by Engines::RailsExtensions::RailsInitializer to create the Rails.plugins array.
|
4
|
+
#
|
5
|
+
# Each loaded plugin has a corresponding Plugin instance within this array, and
|
6
|
+
# the order the plugins were loaded is reflected in the entries in this array.
|
7
|
+
#
|
8
|
+
# For more information, see the Rails module.
|
9
|
+
class PluginList < Array
|
10
|
+
# Finds plugins with the set with the given name (accepts Strings or Symbols), or
|
11
|
+
# index. So, Rails.plugins[0] returns the first-loaded Plugin, and Rails.plugins[:engines]
|
12
|
+
# returns the Plugin instance for the engines plugin itself.
|
13
|
+
def [](name_or_index)
|
14
|
+
if name_or_index.is_a?(Fixnum)
|
15
|
+
super
|
16
|
+
else
|
17
|
+
self.find { |plugin| plugin.name.to_s == name_or_index.to_s }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Go through each plugin, highest priority first (last loaded first). Effectively,
|
22
|
+
# this is like <tt>Rails.plugins.reverse</tt>, except when given a block, when it behaves
|
23
|
+
# like <tt>Rails.plugins.reverse.each</tt>.
|
24
|
+
def by_precedence(&block)
|
25
|
+
if block_given?
|
26
|
+
reverse.each { |x| yield x }
|
27
|
+
else
|
28
|
+
reverse
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# The PluginMigrator class contains the logic to run migrations from
|
2
|
+
# within plugin directories. The directory in which a plugin's migrations
|
3
|
+
# should be is determined by the Plugin#migration_directory method.
|
4
|
+
#
|
5
|
+
# To migrate a plugin, you can simple call the migrate method (Plugin#migrate)
|
6
|
+
# with the version number that plugin should be at. The plugin's migrations
|
7
|
+
# will then be used to migrate up (or down) to the given version.
|
8
|
+
#
|
9
|
+
# For more information, see Engines::RailsExtensions::Migrations
|
10
|
+
class Engines::PluginMigrator < ActiveRecord::Migrator
|
11
|
+
|
12
|
+
# We need to be able to set the 'current' engine being migrated.
|
13
|
+
cattr_accessor :current_plugin
|
14
|
+
|
15
|
+
# Runs the migrations from a plugin, up (or down) to the version given
|
16
|
+
def self.migrate_plugin(plugin, version)
|
17
|
+
self.current_plugin = plugin
|
18
|
+
migrate(plugin.migration_directory, version)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the name of the table used to store schema information about
|
22
|
+
# installed plugins.
|
23
|
+
#
|
24
|
+
# See Engines.schema_info_table for more details.
|
25
|
+
def self.schema_info_table_name
|
26
|
+
ActiveRecord::Base.wrapped_table_name Engines.schema_info_table
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the current version of the given plugin
|
30
|
+
def self.current_version(plugin=current_plugin)
|
31
|
+
result = ActiveRecord::Base.connection.select_one(<<-ESQL
|
32
|
+
SELECT version FROM #{schema_info_table_name}
|
33
|
+
WHERE plugin_name = '#{plugin.name}'
|
34
|
+
ESQL
|
35
|
+
)
|
36
|
+
if result
|
37
|
+
result["version"].to_i
|
38
|
+
else
|
39
|
+
# There probably isn't an entry for this engine in the migration info table.
|
40
|
+
# We need to create that entry, and set the version to 0
|
41
|
+
ActiveRecord::Base.connection.execute(<<-ESQL
|
42
|
+
INSERT INTO #{schema_info_table_name} (version, plugin_name)
|
43
|
+
VALUES (0,'#{plugin.name}')
|
44
|
+
ESQL
|
45
|
+
)
|
46
|
+
0
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sets the version of the plugin in Engines::PluginMigrator.current_plugin to
|
51
|
+
# the given version.
|
52
|
+
def set_schema_version(version)
|
53
|
+
ActiveRecord::Base.connection.update(<<-ESQL
|
54
|
+
UPDATE #{self.class.schema_info_table_name}
|
55
|
+
SET version = #{down? ? version.to_i - 1 : version.to_i}
|
56
|
+
WHERE plugin_name = '#{self.current_plugin.name}'
|
57
|
+
ESQL
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Here we add a single helpful method to ActiveRecord::Base. This method may be deprecated
|
2
|
+
# in the future, since support for the Module#config mechanism which required it has
|
3
|
+
# also been dropped.
|
4
|
+
module Engines::RailsExtensions::ActiveRecord
|
5
|
+
# NOTE: Currently the Migrations system will ALWAYS wrap given table names
|
6
|
+
# in the prefix/suffix, so any table name set via ActiveRecord::Base#set_table_name,
|
7
|
+
# for instance will always get wrapped in the process of migration. For this
|
8
|
+
# reason, whatever value you give to the config will be wrapped when set_table_name
|
9
|
+
# is used in the model.
|
10
|
+
#
|
11
|
+
# This method is useful for determining the actual name (including prefix and
|
12
|
+
# suffix) that Rails will use for a model, given a particular set_table_name
|
13
|
+
# parameter.
|
14
|
+
def wrapped_table_name(name)
|
15
|
+
table_name_prefix + name + table_name_suffix
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
::ActiveRecord::Base.extend(Engines::RailsExtensions::ActiveRecord)
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# One of the magic features that that engines plugin provides is the ability to
|
2
|
+
# override selected methods in controllers and helpers from your application.
|
3
|
+
# This is achieved by trapping requests to load those files, and then mixing in
|
4
|
+
# code from plugins (in the order the plugins were loaded) before finally loading
|
5
|
+
# any versions from the main +app+ directory.
|
6
|
+
#
|
7
|
+
# The behaviour of this extension is output to the log file for help when
|
8
|
+
# debugging.
|
9
|
+
#
|
10
|
+
# == Example
|
11
|
+
#
|
12
|
+
# A plugin contains the following controller in <tt>plugin/app/controllers/my_controller.rb</tt>:
|
13
|
+
#
|
14
|
+
# class MyController < ApplicationController
|
15
|
+
# def index
|
16
|
+
# @name = "HAL 9000"
|
17
|
+
# end
|
18
|
+
# def list
|
19
|
+
# @robots = Robot.find(:all)
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# In one application that uses this plugin, we decide that the name used in the
|
24
|
+
# index action should be "Robbie", not "HAL 9000". To override this single method,
|
25
|
+
# we create the corresponding controller in our application
|
26
|
+
# (<tt>RAILS_ROOT/app/controllers/my_controller.rb</tt>), and redefine the method:
|
27
|
+
#
|
28
|
+
# class MyController < ApplicationController
|
29
|
+
# def index
|
30
|
+
# @name = "Robbie"
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# The list method remains as it was defined in the plugin controller.
|
35
|
+
#
|
36
|
+
# The same basic principle applies to helpers, and also views and partials (although
|
37
|
+
# view overriding is performed in Engines::RailsExtensions::Templates; see that
|
38
|
+
# module for more information).
|
39
|
+
#
|
40
|
+
# === What about models?
|
41
|
+
#
|
42
|
+
# Unfortunately, it's not possible to provide this kind of magic for models.
|
43
|
+
# The only reason why it's possible for controllers and helpers is because
|
44
|
+
# they can be recognised by their filenames ("whatever_controller", "jazz_helper"),
|
45
|
+
# whereas models appear the same as any other typical Ruby library ("node",
|
46
|
+
# "user", "image", etc.).
|
47
|
+
#
|
48
|
+
# If mixing were allowed in models, it would mean code mixing for *every*
|
49
|
+
# file that was loaded via +require_or_load+, and this could result in
|
50
|
+
# problems where, for example, a Node model might start to include
|
51
|
+
# functionality from another file called "node" somewhere else in the
|
52
|
+
# <tt>$LOAD_PATH</tt>.
|
53
|
+
#
|
54
|
+
# One way to overcome this is to provide model functionality as a module in
|
55
|
+
# a plugin, which developers can then include into their own model
|
56
|
+
# implementations.
|
57
|
+
#
|
58
|
+
# Another option is to provide an abstract model (see the ActiveRecord::Base
|
59
|
+
# documentation) and have developers subclass this model in their own
|
60
|
+
# application if they must.
|
61
|
+
#
|
62
|
+
# ---
|
63
|
+
#
|
64
|
+
# The Engines::RailsExtensions::Dependencies module includes a method to
|
65
|
+
# override Dependencies.require_or_load, which is called to load code needed
|
66
|
+
# by Rails as it encounters constants that aren't defined.
|
67
|
+
#
|
68
|
+
# This method is enhanced with the code-mixing features described above.
|
69
|
+
#
|
70
|
+
module Engines::RailsExtensions::Dependencies
|
71
|
+
def self.included(base) #:nodoc:
|
72
|
+
base.class_eval { alias_method_chain :require_or_load, :engine_additions }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Attempt to load the given file from any plugins, as well as the application.
|
76
|
+
# This performs the 'code mixing' magic, allowing application controllers and
|
77
|
+
# helpers to override single methods from those in plugins.
|
78
|
+
# If the file can be found in any plugins, it will be loaded first from those
|
79
|
+
# locations. Finally, the application version is loaded, using Ruby's behaviour
|
80
|
+
# to replace existing methods with their new definitions.
|
81
|
+
#
|
82
|
+
# If <tt>Engines.disable_code_mixing == true</tt>, the first controller/helper on the
|
83
|
+
# <tt>$LOAD_PATH</tt> will be used (plugins' +app+ directories are always lower on the
|
84
|
+
# <tt>$LOAD_PATH</tt> than the main +app+ directory).
|
85
|
+
#
|
86
|
+
# If <tt>Engines.disable_application_code_loading == true</tt>, controllers will
|
87
|
+
# not be loaded from the main +app+ directory *if* they are present in any
|
88
|
+
# plugins.
|
89
|
+
#
|
90
|
+
# Returns true if the file could be loaded (from anywhere); false otherwise -
|
91
|
+
# mirroring the behaviour of +require_or_load+ from Rails (which mirrors
|
92
|
+
# that of Ruby's own +require+, I believe).
|
93
|
+
def require_or_load_with_engine_additions(file_name, const_path=nil)
|
94
|
+
return require_or_load_without_engine_additions(file_name, const_path) if Engines.disable_code_mixing
|
95
|
+
|
96
|
+
file_loaded = false
|
97
|
+
|
98
|
+
# try and load the plugin code first
|
99
|
+
# can't use model, as there's nothing in the name to indicate that the file is a 'model' file
|
100
|
+
# rather than a library or anything else.
|
101
|
+
['controller', 'helper'].each do |file_type|
|
102
|
+
# if we recognise this type
|
103
|
+
# (this regexp splits out the module/filename from any instances of app/#{type}, so that
|
104
|
+
# modules are still respected.)
|
105
|
+
if file_name =~ /^(.*app\/#{file_type}s\/)?(.*_#{file_type})(\.rb)?$/
|
106
|
+
base_name = $2
|
107
|
+
# ... go through the plugins from first started to last, so that
|
108
|
+
# code with a high precedence (started later) will override lower precedence
|
109
|
+
# implementations
|
110
|
+
Rails.plugins.each do |plugin|
|
111
|
+
plugin_file_name = File.expand_path(File.join(plugin.root, 'app', "#{file_type}s", base_name))
|
112
|
+
logger.debug("checking plugin '#{plugin.name}' for '#{base_name}'")
|
113
|
+
if File.file?("#{plugin_file_name}.rb")
|
114
|
+
logger.debug("==> loading from plugin '#{plugin.name}'")
|
115
|
+
file_loaded = true if require_or_load_without_engine_additions(plugin_file_name, const_path)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# finally, load any application-specific controller classes using the 'proper'
|
120
|
+
# rails load mechanism, EXCEPT when we're testing engines and could load this file
|
121
|
+
# from an engine
|
122
|
+
if Engines.disable_application_code_loading
|
123
|
+
logger.debug("loading from application disabled.")
|
124
|
+
else
|
125
|
+
# Ensure we are only loading from the /app directory at this point
|
126
|
+
app_file_name = File.join(RAILS_ROOT, 'app', "#{file_type}s", "#{base_name}")
|
127
|
+
if File.file?("#{app_file_name}.rb")
|
128
|
+
logger.debug("loading from application: #{base_name}")
|
129
|
+
file_loaded = true if require_or_load_without_engine_additions(app_file_name, const_path)
|
130
|
+
else
|
131
|
+
logger.debug("(file not found in application)")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# if we managed to load a file, return true. If not, default to the original method.
|
138
|
+
# Note that this relies on the RHS of a boolean || not to be evaluated if the LHS is true.
|
139
|
+
file_loaded || require_or_load_without_engine_additions(file_name, const_path)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
::Dependencies.send(:include, Engines::RailsExtensions::Dependencies)
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# Contains the enhancements to Rails' migrations system to support the
|
2
|
+
# Engines::PluginMigrator. See Engines::RailsExtensions::Migrations for more
|
3
|
+
# information.
|
4
|
+
|
5
|
+
require "engines/plugin_migrator"
|
6
|
+
|
7
|
+
# = Plugins and Migrations: Background
|
8
|
+
#
|
9
|
+
# Rails uses migrations to describe changes to the databases as your application
|
10
|
+
# evolves. Each change to your application - adding and removing models, most
|
11
|
+
# commonly - might require tweaks to your schema in the form of new tables, or new
|
12
|
+
# columns on existing tables, or possibly the removal of tables or columns. Migrations
|
13
|
+
# can even include arbitrary code to *transform* data as the underlying schema
|
14
|
+
# changes.
|
15
|
+
#
|
16
|
+
# The point is that at any particular stage in your application's development,
|
17
|
+
# migrations serve to transform the database into a state where it is compatible
|
18
|
+
# and appropriate at that time.
|
19
|
+
#
|
20
|
+
# == What about plugins?
|
21
|
+
#
|
22
|
+
# If you want to share models using plugins, chances are that you might also
|
23
|
+
# want to include the corresponding migrations to create tables for those models.
|
24
|
+
# With the engines plugin installed, plugins can carry migration data easily:
|
25
|
+
#
|
26
|
+
# vendor/
|
27
|
+
# |
|
28
|
+
# plugins/
|
29
|
+
# |
|
30
|
+
# my_plugin/
|
31
|
+
# |- init.rb
|
32
|
+
# |- lib/
|
33
|
+
# |- db/
|
34
|
+
# |-migrate/
|
35
|
+
# |- 001_do_something.rb
|
36
|
+
# |- 002_and_something_else.rb
|
37
|
+
# |- ...
|
38
|
+
#
|
39
|
+
# When you install a plugin which contains migrations, you are undertaking a
|
40
|
+
# further step in the development of your application, the same as the addition
|
41
|
+
# of any other code. With this in mind, you may want to 'roll back' the
|
42
|
+
# installation of this plugin at some point, and the database should be able
|
43
|
+
# to migrate back to the point without this plugin in it too.
|
44
|
+
#
|
45
|
+
# == An example
|
46
|
+
#
|
47
|
+
# For example, our current application is at version 14 (according to the
|
48
|
+
# +schema_info+ table), when we decide that we want to add a tagging plugin. The
|
49
|
+
# tagging plugin chosen includes migrations to create the tables it requires
|
50
|
+
# (say, _tags_ and _taggings_, for instance), along with the models and helpers
|
51
|
+
# one might expect.
|
52
|
+
#
|
53
|
+
# After installing this plugin, these tables should be created in our database.
|
54
|
+
# Rather than running the migrations directly from the plugin, they should be
|
55
|
+
# integrated into our main migration stream in order to accurately reflect the
|
56
|
+
# state of our application's database *at this moment in time*.
|
57
|
+
#
|
58
|
+
# $ script/generate plugin_migration
|
59
|
+
# exists db/migrate
|
60
|
+
# create db/migrate/015_migrate_tagging_plugin_to_version_3.rb
|
61
|
+
#
|
62
|
+
# This migration will take our application to version 15, and contains the following,
|
63
|
+
# typical migration code:
|
64
|
+
#
|
65
|
+
# class MigrateTaggingPluginToVersion3 < ActiveRecord::Migration
|
66
|
+
# def self.up
|
67
|
+
# Rails.plugins[:tagging].migrate(3)
|
68
|
+
# end
|
69
|
+
# def self.down
|
70
|
+
# Rails.plugins[:tagging].migrate(0)
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# When we migrate our application up, using <tt>rake db:migrate</tt> as normal,
|
75
|
+
# the plugin will be migrated up to its latest version (3 in this example). If we
|
76
|
+
# ever decide to migrate the application back to the state it was in at version 14,
|
77
|
+
# the plugin migrations will be taken back down to version 0 (which, typically,
|
78
|
+
# would remove all tables the plugin migrations define).
|
79
|
+
#
|
80
|
+
# == Upgrading plugins
|
81
|
+
#
|
82
|
+
# It might happen that later in an application's life, we update to a new version of
|
83
|
+
# the tagging plugin which requires some changes to our database. The tagging plugin
|
84
|
+
# provides these changes in the form of its own migrations.
|
85
|
+
#
|
86
|
+
# In this case, we just need to re-run the plugin_migration generator to create a
|
87
|
+
# new migration from the current revision to the newest one:
|
88
|
+
#
|
89
|
+
# $ script/generate plugin_migration
|
90
|
+
# exists db/migrate
|
91
|
+
# create db/migrate/023_migrate_tagging_plugin_to_version_5.rb
|
92
|
+
#
|
93
|
+
# The contents of this migration are:
|
94
|
+
#
|
95
|
+
# class MigrateTaggingPluginToVersion3 < ActiveRecord::Migration
|
96
|
+
# def self.up
|
97
|
+
# Rails.plugins[:tagging].migrate(5)
|
98
|
+
# end
|
99
|
+
# def self.down
|
100
|
+
# Rails.plugins[:tagging].migrate(3)
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# Notice that if we were to migrate down to revision 22 or lower, the tagging plugin
|
105
|
+
# will be migrated back down to version 3 - the version we were previously at.
|
106
|
+
#
|
107
|
+
#
|
108
|
+
# = Creating migrations in plugins
|
109
|
+
#
|
110
|
+
# In order to use the plugin migration functionality that engines provides, a plugin
|
111
|
+
# only needs to provide regular migrations in a <tt>db/migrate</tt> folder within it.
|
112
|
+
#
|
113
|
+
# = Explicitly migrating plugins
|
114
|
+
#
|
115
|
+
# It's possible to migrate plugins within your own migrations, or any other code.
|
116
|
+
# Simply get the Plugin instance, and its Plugin#migrate method with the version
|
117
|
+
# you wish to end up at:
|
118
|
+
#
|
119
|
+
# Rails.plugins[:whatever].migrate(version)
|
120
|
+
#
|
121
|
+
# ---
|
122
|
+
#
|
123
|
+
# The Engines::RailsExtensions::Migrations module defines extensions for Rails'
|
124
|
+
# migration systems. Specifically:
|
125
|
+
#
|
126
|
+
# * Adding a hook to initialize_schema_information to create the plugin schema
|
127
|
+
# info table.
|
128
|
+
#
|
129
|
+
module Engines::RailsExtensions::Migrations
|
130
|
+
def self.included(base) # :nodoc:
|
131
|
+
base.class_eval { alias_method_chain :initialize_schema_information, :engine_additions }
|
132
|
+
end
|
133
|
+
|
134
|
+
# Create the schema tables, and ensure that the plugin schema table
|
135
|
+
# is also initialized. The plugin schema info table is defined by
|
136
|
+
# Engines::PluginMigrator.schema_info_table_name.
|
137
|
+
def initialize_schema_information_with_engine_additions
|
138
|
+
initialize_schema_information_without_engine_additions
|
139
|
+
|
140
|
+
# create the plugin schema stuff.
|
141
|
+
begin
|
142
|
+
execute <<-ESQL
|
143
|
+
CREATE TABLE #{Engines::PluginMigrator.schema_info_table_name}
|
144
|
+
(plugin_name #{type_to_sql(:string)}, version #{type_to_sql(:integer)})
|
145
|
+
ESQL
|
146
|
+
rescue ActiveRecord::StatementInvalid
|
147
|
+
# Schema has been initialized
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
::ActiveRecord::ConnectionAdapters::SchemaStatements.send(:include, Engines::RailsExtensions::Migrations)
|
153
|
+
|
154
|
+
# Set ActiveRecord to ignore the plugin schema table by default
|
155
|
+
::ActiveRecord::SchemaDumper.ignore_tables << Engines.schema_info_table
|