pager-engines 2.0.20080513

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.
Files changed (115) hide show
  1. data/CHANGELOG +267 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +83 -0
  4. data/Rakefile +188 -0
  5. data/about.yml +7 -0
  6. data/boot.rb +19 -0
  7. data/generators/plugin_migration/USAGE +45 -0
  8. data/generators/plugin_migration/plugin_migration_generator.rb +79 -0
  9. data/generators/plugin_migration/templates/plugin_migration.erb +13 -0
  10. data/init.rb +5 -0
  11. data/lib/engines.rb +174 -0
  12. data/lib/engines/assets.rb +38 -0
  13. data/lib/engines/plugin.rb +142 -0
  14. data/lib/engines/plugin/list.rb +30 -0
  15. data/lib/engines/plugin/loader.rb +18 -0
  16. data/lib/engines/plugin/locator.rb +37 -0
  17. data/lib/engines/plugin/migrator.rb +73 -0
  18. data/lib/engines/rails_extensions/action_mailer.rb +85 -0
  19. data/lib/engines/rails_extensions/asset_helpers.rb +119 -0
  20. data/lib/engines/rails_extensions/dependencies.rb +145 -0
  21. data/lib/engines/rails_extensions/migrations.rb +161 -0
  22. data/lib/engines/rails_extensions/rails.rb +11 -0
  23. data/lib/engines/rails_extensions/routing.rb +84 -0
  24. data/lib/engines/testing.rb +87 -0
  25. data/lib/engines_initializer.rb +5 -0
  26. data/rails/init.rb +5 -0
  27. data/tasks/engines.rake +179 -0
  28. data/test/app/controllers/app_and_plugin_controller.rb +5 -0
  29. data/test/app/controllers/application.rb +18 -0
  30. data/test/app/controllers/namespace/app_and_plugin_controller.rb +5 -0
  31. data/test/app/helpers/mail_helper.rb +5 -0
  32. data/test/app/models/app_and_plugin_model.rb +3 -0
  33. data/test/app/models/notify_mail.rb +26 -0
  34. data/test/app/things/thing.rb +3 -0
  35. data/test/app/views/app_and_plugin/a_view.html.erb +1 -0
  36. data/test/app/views/namespace/app_and_plugin/a_view.html.erb +1 -0
  37. data/test/app/views/notify_mail/implicit_multipart.text.html.erb +1 -0
  38. data/test/app/views/notify_mail/implicit_multipart.text.plain.erb +1 -0
  39. data/test/app/views/notify_mail/multipart_html.html.erb +1 -0
  40. data/test/app/views/notify_mail/multipart_plain.html.erb +1 -0
  41. data/test/app/views/notify_mail/signup.text.plain.erb +5 -0
  42. data/test/app/views/plugin_mail/mail_from_plugin_with_application_template.text.plain.erb +1 -0
  43. data/test/app/views/plugin_mail/multipart_from_plugin_with_application_template_plain.html.erb +1 -0
  44. data/test/functional/controller_loading_test.rb +51 -0
  45. data/test/functional/routes_test.rb +33 -0
  46. data/test/functional/view_helpers_test.rb +32 -0
  47. data/test/functional/view_loading_test.rb +60 -0
  48. data/test/lib/app_and_plugin_lib_model.rb +3 -0
  49. data/test/lib/engines_test_helper.rb +36 -0
  50. data/test/plugins/alpha_plugin/app/controllers/alpha_plugin_controller.rb +8 -0
  51. data/test/plugins/alpha_plugin/app/controllers/app_and_plugin_controller.rb +5 -0
  52. data/test/plugins/alpha_plugin/app/controllers/namespace/alpha_plugin_controller.rb +5 -0
  53. data/test/plugins/alpha_plugin/app/controllers/namespace/app_and_plugin_controller.rb +5 -0
  54. data/test/plugins/alpha_plugin/app/controllers/namespace/shared_plugin_controller.rb +5 -0
  55. data/test/plugins/alpha_plugin/app/controllers/shared_plugin_controller.rb +5 -0
  56. data/test/plugins/alpha_plugin/app/models/alpha_plugin_model.rb +3 -0
  57. data/test/plugins/alpha_plugin/app/models/app_and_plugin_model.rb +7 -0
  58. data/test/plugins/alpha_plugin/app/models/shared_plugin_model.rb +3 -0
  59. data/test/plugins/alpha_plugin/app/views/alpha_plugin/a_view.html.erb +1 -0
  60. data/test/plugins/alpha_plugin/app/views/app_and_plugin/a_view.html.erb +1 -0
  61. data/test/plugins/alpha_plugin/app/views/layouts/plugin_layout.erb +1 -0
  62. data/test/plugins/alpha_plugin/app/views/namespace/alpha_plugin/a_view.html.erb +1 -0
  63. data/test/plugins/alpha_plugin/app/views/namespace/app_and_plugin/a_view.html.erb +1 -0
  64. data/test/plugins/alpha_plugin/app/views/namespace/shared_plugin/a_view.html.erb +1 -0
  65. data/test/plugins/alpha_plugin/app/views/shared_plugin/a_view.html.erb +1 -0
  66. data/test/plugins/alpha_plugin/lib/alpha_plugin_lib_model.rb +3 -0
  67. data/test/plugins/alpha_plugin/lib/app_and_plugin_lib_model.rb +7 -0
  68. data/test/plugins/beta_plugin/app/controllers/app_and_plugin_controller.rb +5 -0
  69. data/test/plugins/beta_plugin/app/controllers/namespace/shared_plugin_controller.rb +5 -0
  70. data/test/plugins/beta_plugin/app/controllers/shared_plugin_controller.rb +5 -0
  71. data/test/plugins/beta_plugin/app/models/shared_plugin_model.rb +3 -0
  72. data/test/plugins/beta_plugin/app/views/namespace/shared_plugin/a_view.html.erb +1 -0
  73. data/test/plugins/beta_plugin/app/views/shared_plugin/a_view.html.erb +1 -0
  74. data/test/plugins/beta_plugin/init.rb +1 -0
  75. data/test/plugins/not_a_plugin/public/should_not_be_copied.txt +0 -0
  76. data/test/plugins/test_assets/app/controllers/assets_controller.rb +2 -0
  77. data/test/plugins/test_assets/app/views/assets/index.html.erb +3 -0
  78. data/test/plugins/test_assets/app/views/layouts/assets.html.erb +3 -0
  79. data/test/plugins/test_assets/init.rb +0 -0
  80. data/test/plugins/test_assets/public/file.txt +0 -0
  81. data/test/plugins/test_assets/public/subfolder/file_in_subfolder.txt +0 -0
  82. data/test/plugins/test_assets_with_assets_directory/assets/file.txt +0 -0
  83. data/test/plugins/test_assets_with_assets_directory/assets/subfolder/file_in_subfolder.txt +0 -0
  84. data/test/plugins/test_assets_with_assets_directory/init.rb +0 -0
  85. data/test/plugins/test_assets_with_no_subdirectory/assets/file.txt +0 -0
  86. data/test/plugins/test_assets_with_no_subdirectory/init.rb +0 -0
  87. data/test/plugins/test_code_mixing/app/things/thing.rb +3 -0
  88. data/test/plugins/test_code_mixing/init.rb +1 -0
  89. data/test/plugins/test_load_path/init.rb +0 -0
  90. data/test/plugins/test_migration/db/migrate/001_create_tests.rb +11 -0
  91. data/test/plugins/test_migration/db/migrate/002_create_others.rb +11 -0
  92. data/test/plugins/test_migration/init.rb +0 -0
  93. data/test/plugins/test_plugin_mailing/app/models/plugin_mail.rb +26 -0
  94. data/test/plugins/test_plugin_mailing/app/views/plugin_mail/mail_from_plugin.text.plain.erb +1 -0
  95. data/test/plugins/test_plugin_mailing/app/views/plugin_mail/multipart_from_plugin_html.html.erb +1 -0
  96. data/test/plugins/test_plugin_mailing/app/views/plugin_mail/multipart_from_plugin_plain.html.erb +1 -0
  97. data/test/plugins/test_plugin_mailing/app/views/plugin_mail/multipart_from_plugin_with_application_template_html.html.erb +1 -0
  98. data/test/plugins/test_plugin_mailing/app/views/plugin_mail/multipart_from_plugin_with_application_template_plain.html.erb +1 -0
  99. data/test/plugins/test_plugin_mailing/init.rb +0 -0
  100. data/test/plugins/test_routing/app/controllers/namespace/test_routing_controller.rb +5 -0
  101. data/test/plugins/test_routing/app/controllers/test_routing_controller.rb +9 -0
  102. data/test/plugins/test_routing/init.rb +0 -0
  103. data/test/plugins/test_routing/routes.rb +2 -0
  104. data/test/plugins/test_testing/init.rb +0 -0
  105. data/test/plugins/test_testing/test/fixtures/testing_fixtures.yml +0 -0
  106. data/test/unit/action_mailer_test.rb +54 -0
  107. data/test/unit/arbitrary_code_mixing_test.rb +41 -0
  108. data/test/unit/assets_test.rb +48 -0
  109. data/test/unit/backwards_compat_test.rb +8 -0
  110. data/test/unit/load_path_test.rb +58 -0
  111. data/test/unit/migration_test.rb +43 -0
  112. data/test/unit/model_and_lib_test.rb +37 -0
  113. data/test/unit/plugins_test.rb +11 -0
  114. data/test/unit/testing_test.rb +18 -0
  115. metadata +255 -0
@@ -0,0 +1,7 @@
1
+ author: James Adam
2
+ email: james.adam@gmail.com
3
+ homepage: http://www.rails-engines.org
4
+ summary: Enhances the plugin mechanism to perform more flexible sharing
5
+ description: The Rails Engines plugin allows the sharing of almost any type of code or asset that you could use in a Rails application, including controllers, models, stylesheets, and views.
6
+ license: MIT
7
+ version: 2.0.0-RC1
data/boot.rb ADDED
@@ -0,0 +1,19 @@
1
+ begin
2
+ require 'rails/version'
3
+ unless Rails::VERSION::MAJOR >= 2 ||
4
+ (Rails::VERSION::MAJOR >= 1 && Rails::VERSION::MINOR >= 99)
5
+ raise "This version of the engines plugin requires Rails 2.0 or later!"
6
+ end
7
+ end
8
+
9
+ require File.join(File.dirname(__FILE__), 'lib/engines')
10
+
11
+ # initialize Rails::Configuration with our own default values to spare users
12
+ # some hassle with the installation and keep the environment cleaner
13
+
14
+ { :default_plugin_locators => [Engines::Plugin::FileSystemLocator,
15
+ (Engines::Plugin::GemLocator if defined?(Gem))].compact ,
16
+ :default_plugin_loader => Engines::Plugin::Loader,
17
+ :default_plugins => [:engines, :all] }.each do |name, default|
18
+ Rails::Configuration.send(:define_method, name) { default }
19
+ end
@@ -0,0 +1,45 @@
1
+ Description:
2
+ The plugin migration generator assists in working with schema additions
3
+ required by plugins. Instead of running migrations from plugins directly,
4
+ the generator creates a regular Rails migration which will be responsible
5
+ for migrating the plugins from their current version to the latest version
6
+ installed.
7
+
8
+ This is important because the set of application migrations remains an
9
+ accurate record of the state of the database, even as plugins are installed
10
+ and removed during the development process.
11
+
12
+ Example:
13
+ ./script/generate plugin_migration [<plugin_name> <another_plugin_name> ...]
14
+
15
+ This will generate:
16
+
17
+ RAILS_ROOT
18
+ |- db
19
+ |-migrate
20
+ |- xxx_plugin_migrations.rb
21
+
22
+ which contains the migrations for the given plugin(s).
23
+
24
+
25
+ Advanced Usage:
26
+
27
+ There may be situations where you need *complete* control over the migrations
28
+ of plugins in your application, migrating a certainly plugin down to X, and
29
+ another plugin up to Y, where neither X or Y are the latest migrations for those
30
+ plugins.
31
+
32
+ For those unfortunate few, I have two pieces of advice:
33
+
34
+ 1. Why? This is a code smell [http://c2.com/xp/CodeSmell.html].
35
+
36
+ 2. Well, OK. Don't panic. You can completely control plugin migrations by
37
+ creating your own migrations. To manually migrate a plugin to a specific
38
+ version, simply use
39
+
40
+ Engines.plugins[:your_plugin_name].migrate(version)
41
+
42
+ where version is the integer of the migration this plugin should end
43
+ up at.
44
+
45
+ With great power comes great responsibility. Use this wisely.
@@ -0,0 +1,79 @@
1
+ # Generates a migration which migrates all plugins to their latest versions
2
+ # within the database.
3
+ class PluginMigrationGenerator < Rails::Generator::Base
4
+
5
+ def initialize(runtime_args, runtime_options={})
6
+ super
7
+ @options = {:assigns => {}}
8
+
9
+ ensure_plugin_schema_table_exists
10
+ get_plugins_to_migrate(runtime_args)
11
+
12
+ if @plugins_to_migrate.empty?
13
+ puts "All plugins are migrated to their latest versions"
14
+ exit(0)
15
+ end
16
+
17
+ @options[:migration_file_name] = build_migration_name
18
+ @options[:assigns][:class_name] = build_migration_name.classify
19
+ end
20
+
21
+ def manifest
22
+ record do |m|
23
+ m.migration_template 'plugin_migration.erb', 'db/migrate', @options
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ # Create the plugin schema table if it doesn't already exist. See
30
+ # Engines::RailsExtensions::Migrations#initialize_schema_migrations_table_with_engine_additions
31
+ def ensure_plugin_schema_table_exists
32
+ ActiveRecord::Base.connection.initialize_schema_migrations_table
33
+ end
34
+
35
+ # Determine all the plugins which have migrations that aren't present
36
+ # according to the plugin schema information from the database.
37
+ def get_plugins_to_migrate(plugin_names)
38
+
39
+ # First, grab all the plugins which exist and have migrations
40
+ @plugins_to_migrate = if plugin_names.empty?
41
+ Engines.plugins
42
+ else
43
+ plugin_names.map do |name|
44
+ Engines.plugins[name] ? Engines.plugins[name] : raise("Cannot find the plugin '#{name}'")
45
+ end
46
+ end
47
+
48
+ @plugins_to_migrate.reject! { |p| p.latest_migration.nil? }
49
+
50
+ # Then find the current versions from the database
51
+ @current_versions = {}
52
+ @plugins_to_migrate.each do |plugin|
53
+ @current_versions[plugin.name] = Engines::Plugin::Migrator.current_version(plugin)
54
+ end
55
+
56
+ # Then find the latest versions from their migration directories
57
+ @new_versions = {}
58
+ @plugins_to_migrate.each do |plugin|
59
+ @new_versions[plugin.name] = plugin.latest_migration
60
+ end
61
+
62
+ # Remove any plugins that don't need migration
63
+ @plugins_to_migrate.map { |p| p.name }.each do |name|
64
+ @plugins_to_migrate.delete(Engines.plugins[name]) if @current_versions[name] == @new_versions[name]
65
+ end
66
+
67
+ @options[:assigns][:plugins] = @plugins_to_migrate
68
+ @options[:assigns][:new_versions] = @new_versions
69
+ @options[:assigns][:current_versions] = @current_versions
70
+ end
71
+
72
+ # Construct a unique migration name based on the plugins involved and the
73
+ # versions they should reach after this migration is run.
74
+ def build_migration_name
75
+ @plugins_to_migrate.map do |plugin|
76
+ "#{plugin.name}_to_version_#{@new_versions[plugin.name]}"
77
+ end.join("_and_")
78
+ end
79
+ end
@@ -0,0 +1,13 @@
1
+ class <%= class_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ <%- plugins.each do |plugin| -%>
4
+ Engines.plugins["<%= plugin.name %>"].migrate(<%= new_versions[plugin.name] %>)
5
+ <%- end -%>
6
+ end
7
+
8
+ def self.down
9
+ <%- plugins.each do |plugin| -%>
10
+ Engines.plugins["<%= plugin.name %>"].migrate(<%= current_versions[plugin.name] %>)
11
+ <%- end -%>
12
+ end
13
+ end
data/init.rb ADDED
@@ -0,0 +1,5 @@
1
+ # Only call Engines.init once, in the after_initialize block so that Rails
2
+ # plugin reloading works when turned on
3
+ config.after_initialize do
4
+ Engines.init if defined? :Engines
5
+ end
@@ -0,0 +1,174 @@
1
+ require 'active_support'
2
+ require File.join(File.dirname(__FILE__), 'engines/plugin')
3
+ require File.join(File.dirname(__FILE__), 'engines/plugin/list')
4
+ require File.join(File.dirname(__FILE__), 'engines/plugin/loader')
5
+ require File.join(File.dirname(__FILE__), 'engines/plugin/locator')
6
+ require File.join(File.dirname(__FILE__), 'engines/assets')
7
+ require File.join(File.dirname(__FILE__), 'engines/rails_extensions/rails')
8
+
9
+ # == Parameters
10
+ #
11
+ # The Engines module has a number of public configuration parameters:
12
+ #
13
+ # [+public_directory+] The directory into which plugin assets should be
14
+ # mirrored. Defaults to <tt>RAILS_ROOT/public/plugin_assets</tt>.
15
+ # [+schema_info_table+] The table to use when storing plugin migration
16
+ # version information. Defaults to +plugin_schema_info+.
17
+ #
18
+ # Additionally, there are a few flags which control the behaviour of
19
+ # some of the features the engines plugin adds to Rails:
20
+ #
21
+ # [+disable_application_view_loading+] A boolean flag determining whether
22
+ # or not views should be loaded from
23
+ # the main <tt>app/views</tt> directory.
24
+ # Defaults to false; probably only
25
+ # useful when testing your plugin.
26
+ # [+disable_application_code_loading+] A boolean flag determining whether
27
+ # or not to load controllers/helpers
28
+ # from the main +app+ directory,
29
+ # if corresponding code exists within
30
+ # a plugin. Defaults to false; again,
31
+ # probably only useful when testing
32
+ # your plugin.
33
+ # [+disable_code_mixing+] A boolean flag indicating whether all plugin
34
+ # copies of a particular controller/helper should
35
+ # be loaded and allowed to override each other,
36
+ # or if the first matching file should be loaded
37
+ # instead. Defaults to false.
38
+ #
39
+ module Engines
40
+ # The set of all loaded plugins
41
+ mattr_accessor :plugins
42
+ self.plugins = Engines::Plugin::List.new
43
+
44
+ # List of extensions to load, can be changed in init.rb before calling Engines.init
45
+ mattr_accessor :rails_extensions
46
+ self.rails_extensions = %w(action_mailer asset_helpers routing migrations dependencies)
47
+
48
+ # The name of the public directory to mirror public engine assets into.
49
+ # Defaults to <tt>RAILS_ROOT/public/plugin_assets</tt>.
50
+ mattr_accessor :public_directory
51
+ self.public_directory = File.join(RAILS_ROOT, 'public', 'plugin_assets')
52
+
53
+ # The table in which to store plugin schema information. Defaults to
54
+ # "plugin_schema_info".
55
+ mattr_accessor :schema_info_table
56
+ self.schema_info_table = "plugin_schema_info"
57
+
58
+ #--
59
+ # These attributes control the behaviour of the engines extensions
60
+ #++
61
+
62
+ # Set this to true if views should *only* be loaded from plugins
63
+ mattr_accessor :disable_application_view_loading
64
+ self.disable_application_view_loading = false
65
+
66
+ # Set this to true if controller/helper code shouldn't be loaded
67
+ # from the application
68
+ mattr_accessor :disable_application_code_loading
69
+ self.disable_application_code_loading = false
70
+
71
+ # Set this ti true if code should not be mixed (i.e. it will be loaded
72
+ # from the first valid path on $LOAD_PATH)
73
+ mattr_accessor :disable_code_mixing
74
+ self.disable_code_mixing = false
75
+
76
+ # This is used to determine which files are candidates for the "code
77
+ # mixing" feature that the engines plugin provides, where classes from
78
+ # plugins can be loaded, and then code from the application loaded
79
+ # on top of that code to override certain methods.
80
+ mattr_accessor :code_mixing_file_types
81
+ self.code_mixing_file_types = %w(controller helper)
82
+
83
+ class << self
84
+ def init
85
+ load_extensions
86
+ Engines::Assets.initialize_base_public_directory
87
+ end
88
+
89
+ def logger
90
+ RAILS_DEFAULT_LOGGER
91
+ end
92
+
93
+ def load_extensions
94
+ rails_extensions.each { |name| require "engines/rails_extensions/#{name}" }
95
+ # load the testing extensions, if we are in the test environment.
96
+ require "engines/testing" if RAILS_ENV == "test"
97
+ end
98
+
99
+ def select_existing_paths(paths)
100
+ paths.select { |path| File.directory?(path) }
101
+ end
102
+
103
+ # The engines plugin will, by default, mix code from controllers and helpers,
104
+ # allowing application code to override specific methods in the corresponding
105
+ # controller or helper classes and modules. However, if other file types should
106
+ # also be mixed like this, they can be added by calling this method. For example,
107
+ # if you want to include "things" within your plugin and override them from
108
+ # your applications, you should use the following layout:
109
+ #
110
+ # app/
111
+ # +-- things/
112
+ # | +-- one_thing.rb
113
+ # | +-- another_thing.rb
114
+ # ...
115
+ # vendor/
116
+ # +-- plugins/
117
+ # +-- my_plugin/
118
+ # +-- app/
119
+ # +-- things/
120
+ # +-- one_thing.rb
121
+ # +-- another_thing.rb
122
+ #
123
+ # The important point here is that your "things" are named <whatever>_thing.rb,
124
+ # and that they are placed within plugin/app/things (the pluralized form of 'thing').
125
+ #
126
+ # It's important to note that you'll also want to ensure that the "things" are
127
+ # on your load path in your plugin's init.rb:
128
+ #
129
+ # Rails.plugins[:my_plugin].code_paths << "app/things"
130
+ #
131
+ def mix_code_from(*types)
132
+ self.code_mixing_file_types += types.map { |x| x.to_s.singularize }
133
+ end
134
+
135
+ # A general purpose method to mirror a directory (+source+) into a destination
136
+ # directory, including all files and subdirectories. Files will not be mirrored
137
+ # if they are identical already (checked via FileUtils#identical?).
138
+ def mirror_files_from(source, destination)
139
+ return unless File.directory?(source)
140
+
141
+ # TODO: use Rake::FileList#pathmap?
142
+ source_files = Dir[source + "/**/*"]
143
+ source_dirs = source_files.select { |d| File.directory?(d) }
144
+ source_files -= source_dirs
145
+
146
+ unless source_files.empty?
147
+ base_target_dir = File.join(destination, File.dirname(source_files.first))
148
+ FileUtils.mkdir_p(base_target_dir)
149
+ end
150
+
151
+ source_dirs.each do |dir|
152
+ # strip down these paths so we have simple, relative paths we can
153
+ # add to the destination
154
+ target_dir = File.join(destination, dir.gsub(source, ''))
155
+ begin
156
+ FileUtils.mkdir_p(target_dir)
157
+ rescue Exception => e
158
+ raise "Could not create directory #{target_dir}: \n" + e
159
+ end
160
+ end
161
+
162
+ source_files.each do |file|
163
+ begin
164
+ target = File.join(destination, file.gsub(source, ''))
165
+ unless File.exist?(target) && FileUtils.identical?(file, target)
166
+ FileUtils.cp(file, target)
167
+ end
168
+ rescue Exception => e
169
+ raise "Could not copy #{file} to #{target}: \n" + e
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,38 @@
1
+ module Engines
2
+ module Assets
3
+ class << self
4
+ @@readme = %{Files in this directory are automatically generated from your plugins.
5
+ They are copied from the 'assets' directories of each plugin into this directory
6
+ each time Rails starts (script/server, script/console... and so on).
7
+ Any edits you make will NOT persist across the next server restart; instead you
8
+ should edit the files within the <plugin_name>/assets/ directory itself.}
9
+
10
+ # Ensure that the plugin asset subdirectory of RAILS_ROOT/public exists, and
11
+ # that we've added a little warning message to instruct developers not to mess with
12
+ # the files inside, since they're automatically generated.
13
+ def initialize_base_public_directory
14
+ dir = Engines.public_directory
15
+ unless File.exist?(dir)
16
+ Engines.logger.debug "Creating public engine files directory '#{dir}'"
17
+ FileUtils.mkdir_p(dir)
18
+ end
19
+ readme = File.join(dir, "README")
20
+ File.open(readme, 'w') { |f| f.puts @@readme } unless File.exist?(readme)
21
+ end
22
+
23
+ # Replicates the subdirectories under the plugins's +assets+ (or +public+)
24
+ # directory into the corresponding public directory. See also
25
+ # Plugin#public_directory for more.
26
+ def mirror_files_for(plugin)
27
+ return if plugin.public_directory.nil?
28
+ begin
29
+ Engines.logger.debug "Attempting to copy plugin assets from '#{plugin.public_directory}' to '#{Engines.public_directory}'"
30
+ Engines.mirror_files_from(plugin.public_directory, File.join(Engines.public_directory, plugin.name))
31
+ rescue Exception => e
32
+ Engines.logger.warn "WARNING: Couldn't create the public file structure for plugin '#{plugin.name}'; Error follows:"
33
+ Engines.logger.warn e
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,142 @@
1
+ # An instance of Plugin is created for each plugin loaded by Rails, and
2
+ # stored in the <tt>Engines.plugins</tt> PluginList
3
+ # (see Engines::RailsExtensions::RailsInitializer for more details).
4
+ #
5
+ # Engines.plugins[:plugin_name]
6
+ #
7
+ # If this plugin contains paths in directories other than <tt>app/controllers</tt>,
8
+ # <tt>app/helpers</tt>, <tt>app/models</tt> and <tt>components</tt>, authors can
9
+ # declare this by adding extra paths to #code_paths:
10
+ #
11
+ # Rails.plugin[:my_plugin].code_paths << "app/sweepers" << "vendor/my_lib"
12
+ #
13
+ # Other properties of the Plugin instance can also be set.
14
+ module Engines
15
+ module PluginExtension
16
+ def self.included(base)
17
+ # Plugins can add code paths to this attribute in init.rb if they
18
+ # need plugin directories to be added to the load path, i.e.
19
+ #
20
+ # plugin.code_paths << 'app/other_classes'
21
+ #
22
+ # Defaults to ["app/controllers", "app/helpers", "app/models", "components"]
23
+ base.send :attr_accessor, :code_paths
24
+
25
+ # Plugins can add paths to this attribute in init.rb if they need
26
+ # controllers loaded from additional locations.
27
+ base.send :attr_accessor, :controller_paths
28
+
29
+ # The directory in this plugin to mirror into the shared directory
30
+ # under +public+.
31
+ #
32
+ # Defaults to "assets" (see default_public_directory).
33
+ base.send :attr_accessor, :public_directory
34
+
35
+ base.alias_method_chain :initialize, :default_paths
36
+ end
37
+
38
+ protected
39
+
40
+ # The default set of code paths which will be added to $LOAD_PATH
41
+ # and Dependencies.load_paths
42
+ def default_code_paths
43
+ # lib will actually be removed from the load paths when we call
44
+ # uniq! in #inject_into_load_paths, but it's important to keep it
45
+ # around (for the documentation tasks, for instance).
46
+ %w(app/controllers app/helpers app/models components lib)
47
+ end
48
+
49
+ # The default set of code paths which will be added to the routing system
50
+ def default_controller_paths
51
+ %w(app/controllers components)
52
+ end
53
+
54
+ # Attempts to detect the directory to use for public files.
55
+ # If +assets+ exists in the plugin, this will be used. If +assets+ is missing
56
+ # but +public+ is found, +public+ will be used.
57
+ def default_public_directory
58
+ Engines.select_existing_paths(%w(assets public).map { |p| File.join(directory, p) }).first
59
+ end
60
+
61
+ def initialize_default_paths
62
+ @code_paths = default_code_paths
63
+ @controller_paths = default_controller_paths
64
+ @public_directory = default_public_directory
65
+ end
66
+
67
+ public
68
+
69
+ def initialize_with_default_paths(*args)
70
+ initialize_without_default_paths(*args)
71
+ initialize_default_paths
72
+ end
73
+
74
+ # Returns a list of paths this plugin wishes to make available in $LOAD_PATH
75
+ #
76
+ # Overwrites the correspondend method in the superclass
77
+ def load_paths
78
+ report_nonexistant_or_empty_plugin! unless valid?
79
+ select_existing_paths :code_paths
80
+ end
81
+
82
+ # Extends the superclass' load method to additionally mirror public assets
83
+ def load(initializer)
84
+ return if loaded?
85
+ super initializer
86
+ add_plugin_view_paths
87
+ Assets.mirror_files_for(self)
88
+ end
89
+
90
+ # for code_paths and controller_paths select those paths that actually
91
+ # exist in the plugin's directory
92
+ def select_existing_paths(name)
93
+ Engines.select_existing_paths(self.send(name).map { |p| File.join(directory, p) })
94
+ end
95
+
96
+ def add_plugin_view_paths
97
+ view_path = File.join(directory, 'app', 'views')
98
+ if File.exist?(view_path)
99
+ ActionController::Base.view_paths.insert(1, view_path) # push it just underneath the app
100
+ ActionView::TemplateFinder.process_view_paths(view_path)
101
+ end
102
+ end
103
+
104
+ # The path to this plugin's public files
105
+ def public_asset_directory
106
+ "#{File.basename(Engines.public_directory)}/#{name}"
107
+ end
108
+
109
+ # The path to this plugin's routes file
110
+ def routes_path
111
+ File.join(directory, "routes.rb")
112
+ end
113
+
114
+ # The directory containing this plugin's migrations (<tt>plugin/db/migrate</tt>)
115
+ def migration_directory
116
+ File.join(self.directory, 'db', 'migrate')
117
+ end
118
+
119
+ # Returns the version number of the latest migration for this plugin. Returns
120
+ # nil if this plugin has no migrations.
121
+ def latest_migration
122
+ migrations = Dir[migration_directory+"/*.rb"]
123
+ return nil if migrations.empty?
124
+ migrations.map { |p| File.basename(p) }.sort.last.match(/0*(\d+)\_/)[1].to_i
125
+ end
126
+
127
+ # Migrate this plugin to the given version. See Engines::Plugin::Migrator for more
128
+ # information.
129
+ def migrate(version = nil)
130
+ Engines::Plugin::Migrator.migrate_plugin(self, version)
131
+ end
132
+ end
133
+
134
+ class Plugin < Rails::Plugin
135
+ include PluginExtension
136
+ end
137
+
138
+ class GemPlugin < Rails::GemPlugin
139
+ include PluginExtension
140
+ end
141
+ end
142
+