desert 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,7 @@
1
+ 0.1.1
2
+ - Works with edge Rails
3
+ - Fixed double loading issue with not fully expanded load_paths.
4
+
5
+ 0.1.0
6
+ - Fixed [#13346] ActionController::Base.helper raises error when helper is only in plugin
7
+ - Desert does not require files that have been required before Desert was loaded
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Pivotal Labs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,77 @@
1
+ = Desert
2
+
3
+ Desert is a component framework for Rails that allows you to seamlessly define in your plugins:
4
+ * Models
5
+ * Controllers
6
+ * Views
7
+ * Helpers
8
+ * Routes
9
+ * Migrations
10
+ * Plugin Dependencies
11
+
12
+ Classes are automatically mixed in with your own or other plugins' classes.
13
+ This allows you to make full featured composable components.
14
+ Desert is a replacement for Appable Plugins (http://wiki.pluginaweek.org/Appable_plugins).
15
+
16
+
17
+ == Using desert
18
+
19
+ To use Desert, you need to do only a few things:
20
+
21
+ * require 'desert' before Rails::Initializer.run in environment.rb
22
+
23
+ environment.rb
24
+ require 'desert'
25
+ Rails::Initializer.run do
26
+ end
27
+
28
+ * Install your desert plugin routes in routes.rb
29
+
30
+ map.routes_from_plugin(:spiffy)
31
+
32
+
33
+ == Example
34
+ Say you want to create a plugin named acts_as_spiffy.
35
+ Desert allows Spiffy to have a set of features that can be reused and extended in several projects.
36
+
37
+ The Spiffy project has a:
38
+ * SpiffyController
39
+ * Spiffy model
40
+ * SpiffyHelper
41
+ * spiffy.rhtml
42
+ * SpiffyLib library class
43
+
44
+ The Spiffy plugin acts as its own mini Rails application.
45
+ Here is the directory structure:
46
+ RAILS_ROOT/vendor/plugins/spiffy/app/controllers/spiffy_controller.rb
47
+ RAILS_ROOT/vendor/plugins/spiffy/app/models/spiffy.rb
48
+ RAILS_ROOT/vendor/plugins/spiffy/app/helpers/spiffy_helper.rb
49
+ RAILS_ROOT/vendor/plugins/spiffy/app/views/spiffy/spiffy.rhtml
50
+ RAILS_ROOT/vendor/plugins/spiffy/lib/spiffy_lib.rb
51
+
52
+ Now, say there is a Spiffy Store rails application that uses acts_as_spiffy.
53
+ The Rails app can open up any of the Spiffy classes and override any of the methods.
54
+
55
+ Say spiffy.rb in the Spiffy plugin is defined as:
56
+ class Spiffy < ActiveRecord::Base
57
+ def why?
58
+ "I just am Spiffy"
59
+ end
60
+ end
61
+
62
+ The Spiffy#why method can be overridden in RAILS_ROOT/app/models/spiffy.rb
63
+ class Spiffy < ActiveRecord::Base
64
+ def why?
65
+ "I sell Spiffy stuff"
66
+ end
67
+ end
68
+
69
+
70
+ == Running plugin tests
71
+
72
+ You can run your plugin tests/specs like so:
73
+
74
+ rake desert:testspec:plugins PLUGIN=spiffy
75
+
76
+ Leaving off the PLUGIN environment variable will cause it to run all the test/specs for
77
+ all installed plugins, which may not be what you want.
@@ -0,0 +1,71 @@
1
+ require "rake"
2
+ require 'rake/gempackagetask'
3
+ require 'rake/contrib/rubyforgepublisher'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ desc "Runs the Rspec suite"
9
+ task(:default) do
10
+ run_suite
11
+ end
12
+
13
+ desc "Runs the Rspec suite"
14
+ task(:spec) do
15
+ run_suite
16
+ end
17
+
18
+ def run_suite
19
+ dir = File.dirname(__FILE__)
20
+ system("ruby #{dir}/spec/spec_suite.rb") || raise("Example Suite failed")
21
+ end
22
+
23
+ desc "Copies the trunk to a tag with the name of the current release"
24
+ task(:tag_release) do
25
+ tag_release
26
+ end
27
+
28
+ PKG_NAME = "desert"
29
+ PKG_VERSION = "0.1.1"
30
+ PKG_FILES = FileList[
31
+ '[A-Z]*',
32
+ '*.rb',
33
+ 'lib/**/*.rb',
34
+ 'lib/generators/**/*',
35
+ 'lib/generators/**/templates/*',
36
+ 'examples/**/*.rb'
37
+ ]
38
+
39
+ spec = Gem::Specification.new do |s|
40
+ s.name = PKG_NAME
41
+ s.version = PKG_VERSION
42
+ s.summary = "Desert is a component framework for Rails that allows your plugins to be packaged as mini Rails apps."
43
+ s.test_files = "examples/spec_suite.rb"
44
+ s.description = s.summary
45
+
46
+ s.files = PKG_FILES.to_a
47
+ s.require_path = 'lib'
48
+
49
+ s.has_rdoc = true
50
+ s.extra_rdoc_files = [ "README", "CHANGES" ]
51
+ s.rdoc_options = ["--main", "README", "--inline-source", "--line-numbers"]
52
+
53
+ s.test_files = Dir.glob('spec/*_spec.rb')
54
+ s.require_path = 'lib'
55
+ s.autorequire = 'desert'
56
+ s.author = "Pivotal Labs"
57
+ s.email = "opensource@pivotallabs.com"
58
+ s.homepage = "http://pivotallabs.com"
59
+ s.rubyforge_project = "pivotalrb"
60
+ end
61
+
62
+ Rake::GemPackageTask.new(spec) do |pkg|
63
+ pkg.need_zip = true
64
+ pkg.need_tar = true
65
+ end
66
+
67
+ def tag_release
68
+ dashed_version = PKG_VERSION.gsub('.', '-')
69
+ svn_user = "#{ENV["SVN_USER"]}@" || ""
70
+ `svn cp svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/desert/trunk svn+ssh://#{svn_user}rubyforge.org/var/svn/pivotalrb/desert/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
71
+ end
data/init.rb ADDED
File without changes
@@ -0,0 +1,32 @@
1
+ require "active_support"
2
+ require "active_record"
3
+ require "action_controller"
4
+ require "action_mailer"
5
+
6
+ dir = File.dirname(__FILE__)
7
+ require "#{dir}/desert/plugin"
8
+ require "#{dir}/desert/manager"
9
+ require "#{dir}/desert/version_checker"
10
+
11
+ if Desert::VersionChecker.rails_version_is_below_1990?
12
+ require "#{dir}/desert/rails/1.x/initializer"
13
+ else
14
+ require "#{dir}/desert/rails/2.x/plugin"
15
+ end
16
+ require "#{dir}/desert/rails/dependencies"
17
+ require "#{dir}/desert/rails/migration"
18
+ require "#{dir}/desert/rails/migrator"
19
+ require "#{dir}/desert/ruby/object"
20
+
21
+ require "#{dir}/desert/rails/route_set"
22
+
23
+ require "#{dir}/desert/plugin_migrations/migrator"
24
+ require "#{dir}/desert/plugin_migrations/extensions/schema_statements"
25
+
26
+ require "#{dir}/desert/plugin_templates/action_controller"
27
+ if Desert::VersionChecker.rails_version_is_below_rc2?
28
+ require "#{dir}/desert/plugin_templates/1.x/action_mailer"
29
+ else
30
+ require "#{dir}/desert/plugin_templates/2.x/action_mailer"
31
+ end
32
+ require "#{dir}/desert/plugin_templates/action_view"
@@ -0,0 +1,112 @@
1
+ module Desert
2
+ class Manager # nodoc
3
+ class << self
4
+ def instance
5
+ @instance ||= new
6
+ end
7
+ attr_writer :instance
8
+
9
+ protected
10
+ def method_missing(method_name, *args, &block)
11
+ instance.__send__(method_name, *args, &block)
12
+ end
13
+ end
14
+
15
+ attr_reader :loading_plugin, :plugins_in_registration
16
+
17
+ def initialize
18
+ @plugins = []
19
+ @plugins_in_registration = []
20
+ end
21
+
22
+ def plugins
23
+ @plugins.dup
24
+ end
25
+
26
+ def load_paths
27
+ paths = []
28
+ plugin_paths.each do |component_root|
29
+ paths << File.join(component_root, 'app')
30
+ paths << File.join(component_root, 'app','models')
31
+ paths << File.join(component_root, 'app','controllers')
32
+ paths << File.join(component_root, 'app','helpers')
33
+ paths << File.join(component_root, 'lib')
34
+ end
35
+ Dependencies.load_paths.reverse.each do |path|
36
+ paths << File.expand_path(path) unless paths.include?(File.expand_path(path))
37
+ end
38
+ paths
39
+ end
40
+
41
+ def register_plugin(plugin_path)
42
+ plugin = Plugin.new(plugin_path)
43
+ @plugins_in_registration << plugin
44
+
45
+ yield if block_given?
46
+
47
+ Dependencies.load_paths << plugin.models_path
48
+ Dependencies.load_paths << plugin.controllers_path
49
+ Dependencies.load_paths << plugin.helpers_path
50
+
51
+ @plugins_in_registration.pop
52
+
53
+ if existing_plugin = find_plugin(plugin.name)
54
+ return existing_plugin
55
+ end
56
+
57
+ @plugins << plugin
58
+ plugin
59
+ end
60
+
61
+ def find_plugin(name_or_directory)
62
+ name = File.basename(File.expand_path(name_or_directory))
63
+ plugins.find do |plugin|
64
+ plugin.name == name
65
+ end
66
+ end
67
+
68
+ def plugin_exists?(name_or_directory)
69
+ !find_plugin(name_or_directory).nil?
70
+ end
71
+
72
+ def plugin_path(name)
73
+ plugin = find_plugin(name)
74
+ return nil unless plugin
75
+ plugin.path
76
+ end
77
+
78
+ def files_on_load_path(file)
79
+ desert_file_exists = false
80
+ files = []
81
+ load_paths.each do |path|
82
+ full_path = File.join(path, file)
83
+ full_path << '.rb' unless File.extname(full_path) == '.rb'
84
+ files << full_path if File.exists?(full_path)
85
+ end
86
+ files
87
+ end
88
+
89
+ def directory_on_load_path?(dir_suffix)
90
+ Desert::Manager.load_paths.each do |path|
91
+ return true if File.directory?(File.join(path, dir_suffix))
92
+ end
93
+ return false
94
+ end
95
+
96
+ def layout_paths
97
+ layout_paths = plugins.reverse.collect do |plugin|
98
+ plugin.layouts_path
99
+ end
100
+ layout_paths
101
+ end
102
+
103
+ protected
104
+ def plugin_paths
105
+ plugins_and_app.collect { |plugin| plugin.path }
106
+ end
107
+
108
+ def plugins_and_app
109
+ plugins + @plugins_in_registration + [Plugin.new(RAILS_ROOT)]
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,80 @@
1
+ module Desert
2
+ class Plugin # nodoc
3
+ attr_reader :name, :path
4
+ def initialize(path)
5
+ @path = File.expand_path(path)
6
+ @name = File.basename(@path)
7
+ end
8
+
9
+ def migration_path
10
+ "#{@path}/db/migrate"
11
+ end
12
+
13
+ # The path to the views for this plugin
14
+ def templates_path
15
+ "#{@path}/app/views"
16
+ end
17
+
18
+ def controllers_path
19
+ "#{@path}/app/controllers"
20
+ end
21
+
22
+ # TODO: Test me
23
+ def models_path
24
+ "#{@path}/app/models"
25
+ end
26
+
27
+ # TODO: Test me
28
+ def helpers_path
29
+ "#{@path}/app/helpers"
30
+ end
31
+
32
+ # The path to the layout for this plugin
33
+ def layouts_path
34
+ "#{templates_path}/layouts"
35
+ end
36
+
37
+ # Finds a template with the specified path
38
+ def find_template(template)
39
+ template_path = "#{templates_path}/#{template}"
40
+ File.exists?(template_path) ? template_path : nil
41
+ end
42
+
43
+ def framework_paths
44
+ # TODO: Don't include dirs for frameworks that are not used
45
+ %w(
46
+ railties
47
+ railties/lib
48
+ actionpack/lib
49
+ activesupport/lib
50
+ activerecord/lib
51
+ actionmailer/lib
52
+ actionwebservice/lib
53
+ ).map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) }
54
+ end
55
+
56
+ def ==(other)
57
+ self.path == other.path
58
+ end
59
+
60
+ def migration
61
+ @migration ||= PluginAWeek::PluginMigrations::Migrator.new(:up, migration_path)
62
+ end
63
+
64
+ def up_to_date?
65
+ with_current_plugin do
66
+ migration.latest_version <= migration.current_version
67
+ end
68
+ end
69
+
70
+ def with_current_plugin
71
+ old_plugin = PluginAWeek::PluginMigrations::Migrator.current_plugin
72
+ begin
73
+ PluginAWeek::PluginMigrations::Migrator.current_plugin = self
74
+ yield
75
+ ensure
76
+ PluginAWeek::PluginMigrations::Migrator.current_plugin = old_plugin
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,38 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module SchemaStatements #:nodoc:
4
+ def initialize_schema_information_with_plugins
5
+ initialize_schema_information_without_plugins
6
+
7
+ begin
8
+ execute "CREATE TABLE #{PluginAWeek::PluginMigrations::Migrator.schema_info_table_name} (plugin_name #{type_to_sql(:string)}, version #{type_to_sql(:integer)})"
9
+ rescue ActiveRecord::StatementInvalid
10
+ # Schema has been initialized
11
+ end
12
+ end
13
+ alias_method_chain :initialize_schema_information, :plugins
14
+
15
+ def dump_schema_information_with_plugins
16
+ schema_information = []
17
+
18
+ dump = dump_schema_information_without_plugins
19
+ schema_information << dump if dump
20
+
21
+ begin
22
+ plugins = ActiveRecord::Base.connection.select_all("SELECT * FROM #{PluginAWeek::PluginMigrations::Migrator.schema_info_table_name}")
23
+ plugins.each do |plugin|
24
+ if (version = plugin['version'].to_i) > 0
25
+ plugin_name = ActiveRecord::Base.quote_value(plugin['plugin_name'])
26
+ schema_information << "INSERT INTO #{PluginAWeek::PluginMigrations::Migrator.schema_info_table_name} (plugin_name, version) VALUES (#{plugin_name}, #{version})"
27
+ end
28
+ end
29
+ rescue ActiveRecord::StatementInvalid
30
+ # No Schema Info
31
+ end
32
+
33
+ schema_information.join(";\n")
34
+ end
35
+ alias_method_chain :dump_schema_information, :plugins
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,43 @@
1
+ module PluginAWeek #:nodoc:
2
+ module PluginMigrations
3
+ # Responsible for migrating plugins. PluginAWeek::PluginMigrations.Migrator.current_plugin
4
+ # indicates which plugin is currently being migrated
5
+ class Migrator < ActiveRecord::Migrator
6
+ # We need to be able to set the current plugin being migrated.
7
+ cattr_accessor :current_plugin
8
+
9
+ class << self
10
+ # Runs the migrations from a plugin, up (or down) to the version given
11
+ def migrate_plugin(plugin, version = nil)
12
+ self.current_plugin = plugin
13
+ migrate(plugin.migration_path, version)
14
+ end
15
+
16
+ def schema_info_table_name #:nodoc:
17
+ ActiveRecord::Base.table_name_prefix + 'plugin_schema_info' + ActiveRecord::Base.table_name_suffix
18
+ end
19
+
20
+ def current_version #:nodoc:
21
+ result = ActiveRecord::Base.connection.select_one("SELECT version FROM #{schema_info_table_name} WHERE plugin_name = '#{current_plugin.name}'")
22
+ if result
23
+ result['version'].to_i
24
+ else
25
+ # There probably isn't an entry for this plugin in the migration info table.
26
+ 0
27
+ end
28
+ end
29
+ end
30
+
31
+ def set_schema_version(version)
32
+ version = down? ? version.to_i - 1 : version.to_i
33
+
34
+ if ActiveRecord::Base.connection.select_one("SELECT version FROM #{self.class.schema_info_table_name} WHERE plugin_name = '#{current_plugin.name}'").nil?
35
+ # We need to create the entry since it doesn't exist
36
+ ActiveRecord::Base.connection.execute("INSERT INTO #{self.class.schema_info_table_name} (version, plugin_name) VALUES (#{version},'#{current_plugin.name}')")
37
+ else
38
+ ActiveRecord::Base.connection.update("UPDATE #{self.class.schema_info_table_name} SET version = #{version} WHERE plugin_name = '#{current_plugin.name}'")
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ module ActionMailer #:nodoc
2
+ class Base #:nodoc:
3
+ private
4
+ def template_path_with_plugin_routing
5
+ template_paths = [template_path_without_plugin_routing]
6
+ Desert::Manager.plugins.reverse.each do |plugin|
7
+ template_paths << "#{plugin.templates_path}/#{mailer_name}"
8
+ end
9
+ "{#{template_paths * ','}}"
10
+ end
11
+ alias_method_chain :template_path, :plugin_routing
12
+
13
+ def initialize_template_class(assigns)
14
+ base_path = Dir["#{template_path}/#{@template}.*"].first
15
+ returning(template = ActionView::Base.new(File.dirname(base_path), assigns, self)) do
16
+ template.extend ApplicationHelper
17
+ template.extend self.class.master_helper_module
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module ActionMailer #:nodoc
2
+ class Base #:nodoc:
3
+ private
4
+ def template_path_with_plugin_routing
5
+ template_paths = [template_path_without_plugin_routing]
6
+ Desert::Manager.plugins.reverse.each do |plugin|
7
+ template_paths << "#{plugin.templates_path}/#{mailer_name}"
8
+ end
9
+ "{#{template_paths * ','}}"
10
+ end
11
+ alias_method_chain :template_path, :plugin_routing
12
+
13
+ def initialize_template_class(assigns)
14
+ view_paths = Dir[template_path].collect do |path|
15
+ File.dirname(path)
16
+ end
17
+ returning(template = ActionView::Base.new(view_paths, assigns, self)) do
18
+ template.extend ApplicationHelper
19
+ template.extend self.class.master_helper_module
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ module ActionController #:nodoc:
2
+ module Layout #:nodoc:
3
+ module ClassMethods #:nodoc:
4
+ private
5
+ def layout_list_with_plugin_routing
6
+ plugin_layouts = Desert::Manager.layout_paths.join(",")
7
+ layout_list_without_plugin_routing + Dir["{#{plugin_layouts}}/**/*"]
8
+ end
9
+ alias_method_chain :layout_list, :plugin_routing
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ if ActionView.const_defined?(:TemplateFinder)
2
+ module ActionView #:nodoc:
3
+ class TemplateFinder #:nodoc:
4
+ def initialize_with_desert_plugins(*args)
5
+ initialize_without_desert_plugins *args
6
+
7
+ Desert::Manager.plugins.reverse.each do |plugin|
8
+ append_view_path plugin.templates_path
9
+ end
10
+ end
11
+ alias_method_chain :initialize, :desert_plugins
12
+ end
13
+ end
14
+ else
15
+ module ActionView #:nodoc:
16
+ class Base #:nodoc:
17
+ private
18
+ def full_template_path_with_plugin_routing(template_path, extension)
19
+ full_template_path = full_template_path_without_plugin_routing(template_path, extension)
20
+
21
+ unless File.exist?(full_template_path)
22
+ # Look through the plugins for the template
23
+ Desert::Manager.plugins.reverse.each do |plugin|
24
+ if plugin_template_path = plugin.find_template("#{template_path}.#{extension}")
25
+ full_template_path = plugin_template_path
26
+ break
27
+ end
28
+ end
29
+ end
30
+
31
+ full_template_path
32
+ end
33
+ alias_method_chain :full_template_path, :plugin_routing
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ module Rails
2
+ class Initializer
3
+ def load_plugin_with_desert(directory)
4
+ return if Desert::Manager.plugin_exists?(directory)
5
+ plugin = Desert::Manager.register_plugin(directory) do
6
+ load_plugin_without_desert(directory)
7
+ end
8
+ # TODO: Can we use Initializer::Configuration#default_load_paths to do this?
9
+ configuration.controller_paths << plugin.controllers_path
10
+ end
11
+ alias_method_chain :load_plugin, :desert
12
+
13
+ def require_plugin(plugin_name)
14
+ find_plugins(configuration.plugin_paths).sort.each do |path|
15
+ return load_plugin(path) if File.basename(path) == plugin_name
16
+ end
17
+ raise "Plugin '#{plugin_name}' does not exist"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ class Rails::Plugin
2
+ attr_accessor :initializer
3
+ def require_plugin(plugin_name)
4
+ initializer.configuration.plugin_locators.each do |locator|
5
+ locator.new(initializer).each do |plugin_loader|
6
+ return plugin_loader.load(initializer) if plugin_loader.name.to_s == plugin_name.to_s
7
+ end
8
+ end
9
+ raise "Plugin '#{plugin_name}' does not exist"
10
+ end
11
+
12
+ def load_with_desert(initializer)
13
+ @initializer = initializer
14
+ return if Desert::Manager.plugin_exists?(directory)
15
+ plugin = Desert::Manager.register_plugin(directory) do
16
+ load_without_desert(initializer)
17
+ end
18
+ # TODO: Can we use Initializer::Configuration#default_load_paths to do this?
19
+ initializer.configuration.controller_paths << plugin.controllers_path
20
+ end
21
+ alias_method_chain :load, :desert
22
+ end
@@ -0,0 +1,88 @@
1
+ module Dependencies
2
+ def load_missing_constant_with_desert(from_mod, const_name)
3
+ from_mod = guard_against_anonymous_module(from_mod)
4
+ qualified_name = qualified_name_for from_mod, const_name
5
+ path_suffix = qualified_name.underscore
6
+
7
+ if define_constant_with_desert_loading(from_mod, const_name, qualified_name, path_suffix)
8
+ return from_mod.const_get(const_name)
9
+ end
10
+
11
+ if has_parent_module?(from_mod)
12
+ look_for_constant_in_parent_module(from_mod, const_name, qualified_name, path_suffix)
13
+ else
14
+ raise NameError, "Constant #{qualified_name} from #{path_suffix}.rb not found"
15
+ end
16
+ end
17
+ alias_method_chain :load_missing_constant, :desert
18
+
19
+ def load_once_path?(path)
20
+ load_once_paths.any? { |base| File.expand_path(path).starts_with? File.expand_path(base) }
21
+ end
22
+
23
+ def depend_on_with_desert(file_name, swallow_load_errors = false)
24
+ successfully_loaded_in_plugin = false
25
+ Desert::Manager.files_on_load_path(file_name).each do |file|
26
+ require_or_load(file)
27
+ successfully_loaded_in_plugin = true
28
+ end
29
+ begin
30
+ unless successfully_loaded_in_plugin
31
+ require_or_load file_name
32
+ loaded << File.expand_path(file_name)
33
+ end
34
+ rescue LoadError
35
+ if !swallow_load_errors && !successfully_loaded_in_plugin
36
+ raise
37
+ end
38
+ end
39
+ end
40
+ alias_method_chain :depend_on, :desert
41
+
42
+ protected
43
+ def define_constant_with_desert_loading(from_mod, const_name, qualified_name, path_suffix)
44
+ define_constant_from_file(from_mod, const_name, qualified_name, path_suffix) ||
45
+ define_constant_from_directory(from_mod, const_name, qualified_name, path_suffix)
46
+ end
47
+
48
+ def has_parent_module?(mod)
49
+ mod != Object
50
+ end
51
+
52
+ def look_for_constant_in_parent_module(from_mod, const_name, qualified_name, path_suffix)
53
+ return from_mod.parent.const_missing(const_name)
54
+ rescue NameError => e
55
+ raise NameError, "Constant #{qualified_name} from #{path_suffix}.rb not found\n#{e.message}"
56
+ end
57
+
58
+ def guard_against_anonymous_module(from_mod)
59
+ return Object if from_mod.name.blank?
60
+ return from_mod
61
+ end
62
+
63
+ def define_constant_from_file(from_mod, const_name, qualified_name, path_suffix)
64
+ files = Desert::Manager.files_on_load_path(path_suffix)
65
+ files.each do |file|
66
+ # TODO: JLM/BT -- figure out why require_or_load does not work on Windows.
67
+ # require_or_load file
68
+ load file
69
+ loaded << file.gsub(/\.rb$/, '')
70
+ next if autoloaded_constants.include?(qualified_name)
71
+ next if load_once_path?(file)
72
+ autoloaded_constants << qualified_name
73
+ end
74
+ loaded << File.expand_path(path_suffix)
75
+ from_mod.const_defined?(const_name)
76
+ end
77
+
78
+ def define_constant_from_directory(from_mod, const_name, qualified_name, path_suffix)
79
+ return false unless Desert::Manager.directory_on_load_path?(path_suffix)
80
+
81
+ if autoloaded_constants.include?(qualified_name)
82
+ raise "Constant #{qualified_name} is already autoloaded but is not defined as a constant"
83
+ end
84
+ from_mod.const_set(const_name, Module.new)
85
+ autoloaded_constants << qualified_name
86
+ return true
87
+ end
88
+ end
@@ -0,0 +1,42 @@
1
+ class ActiveRecord::Migration
2
+ module DesertMigration
3
+ def migrate_plugin(plugin_name, version)
4
+ plugin = find_plugin(plugin_name)
5
+ PluginAWeek::PluginMigrations::Migrator.migrate_plugin(
6
+ plugin,
7
+ version
8
+ )
9
+ end
10
+
11
+ def schema_version_equivalent_to(plugin_name, version)
12
+ plugin = find_plugin(plugin_name)
13
+ PluginAWeek::PluginMigrations::Migrator.current_plugin = plugin
14
+ PluginAWeek::PluginMigrations::Migrator.allocate.set_schema_version(version)
15
+ end
16
+
17
+ protected
18
+ def find_plugin(plugin_name)
19
+ plugin = Desert::Manager.find_plugin(plugin_name)
20
+ return plugin if plugin
21
+ raise ArgumentError, "No plugin found named #{plugin_name}"
22
+ end
23
+
24
+ def table_exists?(table_name)
25
+ vals = select_all("DESC #{table_name}")
26
+ return true
27
+ rescue ActiveRecord::StatementInvalid
28
+ return false
29
+ end
30
+
31
+ def column_exists?(table_name, column_name)
32
+ val = select_one("select #{column_name} from #{table_name}")
33
+ return true
34
+ rescue ActiveRecord::StatementInvalid
35
+ return false
36
+ end
37
+ def table_exists?(table_name)
38
+ ActiveRecord::Base.connection.tables.include? table_name
39
+ end
40
+ end
41
+ extend DesertMigration
42
+ end
@@ -0,0 +1,14 @@
1
+ class ActiveRecord::Migrator
2
+ module DesertMigrator
3
+ def latest_version
4
+ return 0 if migration_classes.empty?
5
+ migration_classes.last.first
6
+ end
7
+
8
+ def migration_classes_with_caching
9
+ @migration_classes ||= migration_classes_without_caching
10
+ end
11
+ end
12
+ include DesertMigrator
13
+ alias_method_chain :migration_classes, :caching
14
+ end
@@ -0,0 +1,23 @@
1
+ module Desert
2
+ module Rails
3
+ module RouteSet
4
+ # Loads the set of routes from within a plugin and evaluates them at this
5
+ # point within an application's main <tt>routes.rb</tt> file.
6
+ #
7
+ # Plugin routes are loaded from <tt><plugin_root>/routes.rb</tt>.
8
+ def routes_from_plugin(name)
9
+ name = name.to_s
10
+ routes_path = File.join(
11
+ Desert::Manager.plugin_path(name),
12
+ "config/routes.rb"
13
+ )
14
+ RAILS_DEFAULT_LOGGER.debug "Loading routes from #{routes_path}."
15
+ eval(IO.read(routes_path), binding, routes_path) if File.file?(routes_path)
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class ActionController::Routing::RouteSet::Mapper
22
+ include Desert::Rails::RouteSet
23
+ end
@@ -0,0 +1,34 @@
1
+ class Object
2
+ def require_with_desert(path)
3
+ relative_include_path = (path =~ /\.rb$/) ? path : "#{path}.rb"
4
+ if $".include?(relative_include_path)
5
+ return false
6
+ else
7
+ __each_matching_file(path) do |expanded_path|
8
+ require_without_desert expanded_path
9
+ end
10
+ $" << relative_include_path
11
+ return true
12
+ end
13
+ end
14
+ alias_method_chain :require, :desert
15
+
16
+ def load_with_desert(file)
17
+ __each_matching_file(file) do |file|
18
+ load_without_desert file
19
+ end
20
+ end
21
+ alias_method_chain :load, :desert
22
+
23
+ private
24
+ def __each_matching_file(file)
25
+ files = Desert::Manager.instance.files_on_load_path(file)
26
+ desert_file_exists = files.empty? ? false : true
27
+ files.each do |component_file|
28
+ yield(component_file)
29
+ end
30
+
31
+ return true if desert_file_exists
32
+ yield(file)
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ module Desert
2
+ class VersionChecker
3
+ class << self
4
+ def current_rails_version_matches?(version_requirement)
5
+ version_matches?(::Rails::VERSION::STRING, version_requirement)
6
+ end
7
+
8
+ def version_matches?(version, version_requirement)
9
+ Gem::Version::Requirement.new([version_requirement]).satisfied_by?(Gem::Version.new(version))
10
+ end
11
+
12
+ def rails_version_is_below_1990?
13
+ result = current_rails_version_matches?('<1.99.0')
14
+ result
15
+ end
16
+
17
+ def rails_version_is_below_rc2?
18
+ current_rails_version_matches?('<1.99.1')
19
+ end
20
+
21
+ def rails_version_is_1991?
22
+ current_rails_version_matches?('=1.99.1')
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ NAME
2
+ desert_plugin - creates a directory structure and starter files for a new desert plugin
3
+
4
+ SYNOPSIS
5
+ desert_plugin [plugin name]
6
+
7
+ DESCRIPTION
8
+ |-- vendor
9
+ `-- plugins
10
+ `-- [plugin name]
11
+
12
+ EXAMPLE
13
+ ./script/generate desert_plugin spiffy
14
+
@@ -0,0 +1,40 @@
1
+ class DesertPluginGenerator < Rails::Generator::NamedBase
2
+ def manifest
3
+ record do |m|
4
+ m.directory "vendor/plugins/#{file_name}"
5
+
6
+ m.directory "vendor/plugins/#{file_name}/app"
7
+ m.directory "vendor/plugins/#{file_name}/app/controllers"
8
+ m.directory "vendor/plugins/#{file_name}/app/helpers"
9
+ m.directory "vendor/plugins/#{file_name}/app/models"
10
+ m.directory "vendor/plugins/#{file_name}/app/views"
11
+
12
+ m.directory "vendor/plugins/#{file_name}/config"
13
+ m.template "routes.rb", "vendor/plugins/#{file_name}/config/routes.rb"
14
+ map_route_from_plugin(m)
15
+
16
+ m.directory "vendor/plugins/#{file_name}/db"
17
+ m.directory "vendor/plugins/#{file_name}/db/migrate"
18
+ m.template "plugin_migration.rb", "vendor/plugins/#{file_name}/db/migrate/001_init_#{file_name}_plugin.rb"
19
+
20
+ m.directory "vendor/plugins/#{file_name}/lib"
21
+
22
+ m.directory "vendor/plugins/#{file_name}/spec"
23
+ m.directory "vendor/plugins/#{file_name}/spec/controllers"
24
+ m.directory "vendor/plugins/#{file_name}/spec/fixtures"
25
+ m.directory "vendor/plugins/#{file_name}/spec/models"
26
+ m.directory "vendor/plugins/#{file_name}/spec/views"
27
+ m.file "spec_helper.rb", "vendor/plugins/#{file_name}/spec/spec_helper.rb"
28
+
29
+ m.file "empty_file", "vendor/plugins/#{file_name}/init.rb"
30
+ end
31
+ end
32
+
33
+ def map_route_from_plugin(m)
34
+ logger.route "adding map.routes_from_plugin(:#{file_name}) to top of routes.rb"
35
+ sentinel = 'ActionController::Routing::Routes.draw do |map|'
36
+ m.gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match|
37
+ "#{match}\n map.routes_from_plugin(:#{file_name})\n"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ class Init<%= class_name %>Plugin < ActiveRecord::Migration
2
+ def self.up
3
+ create_table "<%= plural_name %>", :force => true do |t|
4
+ t.column "some_<%= file_name %>_column", :string
5
+ end
6
+ end
7
+
8
+ def self.down
9
+ drop_table :<%= plural_name %>
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ resources :<%= plural_name %>
@@ -0,0 +1,8 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
3
+ require 'spec'
4
+ require 'spec/rails'
5
+
6
+ Spec::Runner.configure do |config|
7
+ config.fixture_path = "#{File.dirname(__FILE__)}/../spec/fixtures"
8
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: desert
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.1
7
+ date: 2008-03-18 00:00:00 -07:00
8
+ summary: Desert is a component framework for Rails that allows your plugins to be packaged as mini Rails apps.
9
+ require_paths:
10
+ - lib
11
+ email: opensource@pivotallabs.com
12
+ homepage: http://pivotallabs.com
13
+ rubyforge_project: pivotalrb
14
+ description: Desert is a component framework for Rails that allows your plugins to be packaged as mini Rails apps.
15
+ autorequire: desert
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Pivotal Labs
31
+ files:
32
+ - CHANGES
33
+ - MIT-LICENSE
34
+ - README
35
+ - Rakefile
36
+ - init.rb
37
+ - lib/desert.rb
38
+ - lib/generators/desert_plugin/templates/routes.rb
39
+ - lib/generators/desert_plugin/templates/spec_helper.rb
40
+ - lib/generators/desert_plugin/templates/plugin_migration.rb
41
+ - lib/generators/desert_plugin/desert_plugin_generator.rb
42
+ - lib/desert/plugin_templates/1.x/action_mailer.rb
43
+ - lib/desert/plugin_templates/action_view.rb
44
+ - lib/desert/plugin_templates/2.x/action_mailer.rb
45
+ - lib/desert/plugin_templates/action_controller.rb
46
+ - lib/desert/manager.rb
47
+ - lib/desert/rails/1.x/initializer.rb
48
+ - lib/desert/rails/migration.rb
49
+ - lib/desert/rails/migrator.rb
50
+ - lib/desert/rails/2.x/plugin.rb
51
+ - lib/desert/rails/route_set.rb
52
+ - lib/desert/rails/dependencies.rb
53
+ - lib/desert/version_checker.rb
54
+ - lib/desert/ruby/object.rb
55
+ - lib/desert/plugin_migrations/migrator.rb
56
+ - lib/desert/plugin_migrations/extensions/schema_statements.rb
57
+ - lib/desert/plugin.rb
58
+ - lib/generators/desert_plugin
59
+ - lib/generators/desert_plugin/templates
60
+ - lib/generators/desert_plugin/templates/empty_file
61
+ - lib/generators/desert_plugin/USAGE
62
+ test_files: []
63
+
64
+ rdoc_options:
65
+ - --main
66
+ - README
67
+ - --inline-source
68
+ - --line-numbers
69
+ extra_rdoc_files:
70
+ - README
71
+ - CHANGES
72
+ executables: []
73
+
74
+ extensions: []
75
+
76
+ requirements: []
77
+
78
+ dependencies: []
79
+