parlement 0.10 → 0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/CHANGES +11 -0
  2. data/MEMORY +9 -1
  3. data/README +5 -4
  4. data/app/controllers/account_controller.rb +10 -13
  5. data/app/controllers/application.rb +4 -5
  6. data/app/controllers/elt_controller.rb +9 -7
  7. data/app/controllers/person_controller.rb +1 -3
  8. data/app/controllers/subscriber_controller.rb +10 -10
  9. data/app/helpers/elt_helper.rb +2 -0
  10. data/app/models/elt.rb +28 -19
  11. data/app/models/mail.rb +26 -14
  12. data/app/models/mail_notify.rb +5 -4
  13. data/app/models/person.rb +11 -2
  14. data/app/views/account/_login.rhtml +3 -3
  15. data/app/views/account/_show.rhtml +12 -14
  16. data/app/views/elt/_choice.rhtml +3 -3
  17. data/app/views/elt/_elt.rhtml +4 -4
  18. data/app/views/elt/_list.rhtml +2 -2
  19. data/app/views/elt/_listByDate.rhtml +1 -1
  20. data/app/views/elt/_listByVote.rhtml +1 -1
  21. data/app/views/elt/new.rhtml +3 -3
  22. data/app/views/elt/show.rhtml +2 -2
  23. data/app/views/layouts/top.rhtml +6 -0
  24. data/app/views/mail_notify/publish.text.html.rhtml +1 -1
  25. data/app/views/person/_listElts.rhtml +5 -3
  26. data/app/views/person/show.rhtml +1 -2
  27. data/config/boot.rb +5 -4
  28. data/config/environment.rb +6 -4
  29. data/config/routes.rb +3 -2
  30. data/db/development_structure.sql +15 -4
  31. data/db/migrate/006_last_activity.rb +10 -0
  32. data/db/schema.rb +67 -49
  33. data/public/dispatch.fcgi +1 -0
  34. data/public/javascripts/controls.js +41 -23
  35. data/public/javascripts/dragdrop.js +317 -99
  36. data/public/javascripts/effects.js +301 -166
  37. data/public/javascripts/prototype.js +932 -402
  38. data/public/stylesheets/default.css +3 -2
  39. data/test/unit/elt_test.rb +13 -0
  40. data/test/unit/mail_test.rb +3 -1
  41. data/vendor/plugins/engines/CHANGELOG +203 -99
  42. data/vendor/plugins/engines/MIT-LICENSE +1 -1
  43. data/vendor/plugins/engines/README +32 -384
  44. data/vendor/plugins/engines/Rakefile +14 -0
  45. data/vendor/plugins/engines/UPGRADING +93 -0
  46. data/vendor/plugins/engines/about.yml +7 -0
  47. data/vendor/plugins/engines/generators/plugin_migration/USAGE +45 -0
  48. data/vendor/plugins/engines/generators/plugin_migration/plugin_migration_generator.rb +79 -0
  49. data/vendor/plugins/engines/generators/plugin_migration/templates/plugin_migration.erb +13 -0
  50. data/vendor/plugins/engines/init.rb +34 -47
  51. data/vendor/plugins/engines/install.rb +32 -0
  52. data/vendor/plugins/engines/lib/engines/{ruby_extensions.rb → deprecated_config_support.rb} +135 -113
  53. data/vendor/plugins/engines/lib/engines/plugin.rb +214 -0
  54. data/vendor/plugins/engines/lib/engines/plugin_list.rb +31 -0
  55. data/vendor/plugins/engines/lib/engines/plugin_migrator.rb +60 -0
  56. data/vendor/plugins/engines/lib/engines/rails_extensions/active_record.rb +19 -0
  57. data/vendor/plugins/engines/lib/engines/rails_extensions/dependencies.rb +143 -0
  58. data/vendor/plugins/engines/lib/engines/rails_extensions/migrations.rb +155 -0
  59. data/vendor/plugins/engines/lib/engines/rails_extensions/public_asset_helpers.rb +116 -0
  60. data/vendor/plugins/engines/lib/engines/rails_extensions/rails.rb +20 -0
  61. data/vendor/plugins/engines/lib/engines/rails_extensions/rails_initializer.rb +86 -0
  62. data/vendor/plugins/engines/lib/engines/rails_extensions/routing.rb +77 -0
  63. data/vendor/plugins/engines/lib/engines/rails_extensions/templates.rb +140 -0
  64. data/vendor/plugins/engines/lib/engines/rails_extensions.rb +6 -0
  65. data/vendor/plugins/engines/lib/engines/testing.rb +88 -0
  66. data/vendor/plugins/engines/lib/engines.rb +281 -425
  67. data/vendor/plugins/engines/tasks/engines.rake +108 -137
  68. metadata +218 -250
  69. data/db/ROOT/perso.txt +0 -214
  70. data/public/images/indicator.gif +0 -0
  71. data/public/images/orange_by_darren_Hester_350o.jpg +0 -0
  72. data/public/images/smile.png +0 -0
  73. data/vendor/plugins/engines/generators/engine/USAGE +0 -26
  74. data/vendor/plugins/engines/generators/engine/engine_generator.rb +0 -199
  75. data/vendor/plugins/engines/generators/engine/templates/README +0 -85
  76. data/vendor/plugins/engines/generators/engine/templates/init_engine.erb +0 -15
  77. data/vendor/plugins/engines/generators/engine/templates/install.erb +0 -4
  78. data/vendor/plugins/engines/generators/engine/templates/lib/engine.erb +0 -6
  79. data/vendor/plugins/engines/generators/engine/templates/licenses/GPL +0 -18
  80. data/vendor/plugins/engines/generators/engine/templates/licenses/LGPL +0 -19
  81. data/vendor/plugins/engines/generators/engine/templates/licenses/MIT +0 -22
  82. data/vendor/plugins/engines/generators/engine/templates/licenses/None +0 -1
  83. data/vendor/plugins/engines/generators/engine/templates/public/javascripts/engine.js +0 -0
  84. data/vendor/plugins/engines/generators/engine/templates/public/stylesheets/engine.css +0 -0
  85. data/vendor/plugins/engines/generators/engine/templates/tasks/engine.rake +0 -0
  86. data/vendor/plugins/engines/generators/engine/templates/test/test_helper.erb +0 -17
  87. data/vendor/plugins/engines/lib/bundles/require_resource.rb +0 -124
  88. data/vendor/plugins/engines/lib/bundles.rb +0 -77
  89. data/vendor/plugins/engines/lib/engines/action_mailer_extensions.rb +0 -140
  90. data/vendor/plugins/engines/lib/engines/action_view_extensions.rb +0 -141
  91. data/vendor/plugins/engines/lib/engines/active_record_extensions.rb +0 -21
  92. data/vendor/plugins/engines/lib/engines/dependencies_extensions.rb +0 -129
  93. data/vendor/plugins/engines/lib/engines/migration_extensions.rb +0 -53
  94. data/vendor/plugins/engines/lib/engines/routing_extensions.rb +0 -28
  95. data/vendor/plugins/engines/lib/engines/testing_extensions.rb +0 -327
  96. data/vendor/plugins/engines/tasks/deprecated_engines.rake +0 -7
  97. data/vendor/plugins/engines/test/action_view_extensions_test.rb +0 -9
  98. data/vendor/plugins/engines/test/ruby_extensions_test.rb +0 -115
  99. data/vendor/plugins/guid/README.TXT +0 -29
  100. data/vendor/plugins/guid/init.rb +0 -30
  101. data/vendor/plugins/guid/lib/usesguid.rb +0 -37
  102. data/vendor/plugins/guid/lib/uuid22.rb +0 -43
  103. data/vendor/plugins/guid/lib/uuidtools.rb +0 -572
  104. data/vendor/plugins/responds_to_parent/MIT-LICENSE +0 -20
  105. data/vendor/plugins/responds_to_parent/README +0 -42
  106. data/vendor/plugins/responds_to_parent/Rakefile +0 -22
  107. data/vendor/plugins/responds_to_parent/init.rb +0 -1
  108. data/vendor/plugins/responds_to_parent/lib/responds_to_parent.rb +0 -46
  109. 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