rails3_plugin_toolbox 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +51 -37
- data/Rakefile +0 -27
- data/VERSION +1 -1
- data/config/database.yml +22 -0
- data/lib/plugin_toolbox/extender.rb +48 -28
- data/lib/plugin_toolbox/loader.rb +24 -23
- data/lib/plugin_toolbox/util.rb +34 -17
- data/lib/rails3_plugin_toolbox.rb +2 -3
- data/{spec/plugin_toolbox/loader_spec.rb → log/development.log} +0 -0
- data/rails3_plugin_toolbox.gemspec +13 -10
- data/spec/plugin_toolbox/extender_action_spec.rb +61 -0
- data/spec/plugin_toolbox/extender_i18n_spec.rb +37 -0
- data/spec/plugin_toolbox/extender_spec.rb +39 -0
- data/spec/spec_helper.rb +45 -1
- data/wiki/add_rake_tasks.markdown +22 -0
- data/wiki/engine.markdown +52 -0
- data/wiki/how_to_guide.markdown +153 -0
- metadata +12 -9
- data/lib/plugin_toolbox/validator/hook_validator.rb +0 -11
- data/lib/plugin_toolbox/validator/type_validator.rb +0 -11
- data/spec/plugin_toolbox/util_spec.rb +0 -0
data/README.markdown
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
A toolbox to facilitate creating Plugins for Rails 3 without having to necessarily know a lot about the Rails 3 internals and Plugin architecture.
|
4
4
|
|
5
|
-
##
|
5
|
+
## RSpec test suite
|
6
6
|
|
7
|
-
|
7
|
+
I have now discovered how to test he validity of Rails extensions in RSpec without having to have a full Rails 3 host app.
|
8
|
+
RSpec 2 test suite will soon be added :)
|
9
|
+
|
10
|
+
http://gist.github.com/542441
|
8
11
|
|
9
12
|
## Inspiration
|
10
13
|
|
@@ -19,47 +22,58 @@ This project was inspired by the Yehuda Katz article at http://www.railsdispatch
|
|
19
22
|
<pre>
|
20
23
|
# my_plugin/lib/load.rb
|
21
24
|
module MyPlugin
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
25
|
+
Rails3::PluginExtender.new do
|
26
|
+
|
27
|
+
extend_from_module Ultra::View, :stuff, :in => :view
|
28
|
+
extend_with Ultra::Controller, :in => :controller
|
29
|
+
|
30
|
+
extend_rails :i18n do
|
31
|
+
with MyAddition
|
32
|
+
extend_from_module Ultra::Power, :util, :logging, :monitor
|
33
|
+
|
34
|
+
before :initialize do
|
35
|
+
MyOtherAddition.say 'before localized!'
|
36
|
+
end
|
37
|
+
|
38
|
+
before :configuration do
|
39
|
+
MyOtherAddition.configured = 'was configured!'
|
40
|
+
end
|
41
|
+
|
42
|
+
before :eager_load do
|
43
|
+
puts "before eager load!"
|
44
|
+
end
|
45
|
+
|
46
|
+
after :initialize do
|
47
|
+
MyAddition.say 'localized!'
|
48
|
+
end
|
49
|
+
end
|
34
50
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
40
|
-
</pre>
|
51
|
+
extend_rails :view do
|
52
|
+
with MyViewAddition
|
53
|
+
end
|
41
54
|
|
42
|
-
|
55
|
+
extend_rails :controller do
|
56
|
+
with MyViewAddition
|
57
|
+
end
|
43
58
|
|
44
|
-
|
59
|
+
extend_rails :mailer do
|
60
|
+
with MyMailAddition, 'MyOtherMailAddition'
|
61
|
+
end
|
45
62
|
|
46
|
-
|
47
|
-
|
48
|
-
module MyPlugin
|
49
|
-
class Railtie < ::Rails::Railtie
|
50
|
-
rake_tasks do
|
51
|
-
load "my_plugin/railties/tasks.rake"
|
63
|
+
extend_rails :AR do
|
64
|
+
with MyActiveRecordAddition
|
52
65
|
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
</pre>
|
56
66
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
67
|
+
extend_rails(:controller) do
|
68
|
+
extend_from_module Ultra::Power, :util, :logging, :monitor
|
69
|
+
extend_from_module Ultra::Power::More, :extra, :stuff
|
70
|
+
end
|
71
|
+
|
72
|
+
after(:initialize) do
|
73
|
+
include MyCoreModule
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
63
77
|
</pre>
|
64
78
|
|
65
79
|
|
data/Rakefile
CHANGED
@@ -16,30 +16,3 @@ begin
|
|
16
16
|
rescue LoadError
|
17
17
|
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
18
|
end
|
19
|
-
|
20
|
-
# require 'spec/rake/spectask'
|
21
|
-
# Spec::Rake::SpecTask.new(:spec) do |spec|
|
22
|
-
# spec.libs << 'lib' << 'spec'
|
23
|
-
# spec.spec_files = FileList['spec/**/*_spec.rb']
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# Spec::Rake::SpecTask.new(:rcov) do |spec|
|
27
|
-
# spec.libs << 'lib' << 'spec'
|
28
|
-
# spec.pattern = 'spec/**/*_spec.rb'
|
29
|
-
# spec.rcov = true
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# task :spec => :check_dependencies
|
33
|
-
#
|
34
|
-
# task :default => :spec
|
35
|
-
#
|
36
|
-
# require 'rake/rdoctask'
|
37
|
-
# Rake::RDocTask.new do |rdoc|
|
38
|
-
# version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
39
|
-
#
|
40
|
-
# rdoc.rdoc_dir = 'rdoc'
|
41
|
-
# rdoc.title = "rails3_plugin_toolbox #{version}"
|
42
|
-
# rdoc.rdoc_files.include('README*')
|
43
|
-
# rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
-
# end
|
45
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/config/database.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# SQLite version 3.x
|
2
|
+
# gem install sqlite3-ruby (not necessary on OS X Leopard)
|
3
|
+
development:
|
4
|
+
adapter: sqlite3
|
5
|
+
database: db/development.sqlite3
|
6
|
+
pool: 5
|
7
|
+
timeout: 5000
|
8
|
+
|
9
|
+
# Warning: The database defined as "test" will be erased and
|
10
|
+
# re-generated from your development database when you run "rake".
|
11
|
+
# Do not set this db to the same as development or production.
|
12
|
+
test:
|
13
|
+
adapter: sqlite3
|
14
|
+
database: db/test.sqlite3
|
15
|
+
pool: 5
|
16
|
+
timeout: 5000
|
17
|
+
|
18
|
+
production:
|
19
|
+
adapter: sqlite3
|
20
|
+
database: db/production.sqlite3
|
21
|
+
pool: 5
|
22
|
+
timeout: 5000
|
@@ -1,45 +1,65 @@
|
|
1
1
|
require 'plugin_toolbox/util'
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
include
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
3
|
+
module Rails3
|
4
|
+
class PluginExtender
|
5
|
+
include LoadHandler
|
6
|
+
|
7
|
+
def initialize &block
|
8
|
+
if block
|
9
|
+
block.arity < 1 ? self.instance_eval(&block) : block.call(self)
|
10
|
+
end
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
|
+
# load after: [:initialize, :configuration, :eager_load]
|
13
14
|
def extend_rails(type, &block)
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def extend! module_name, type
|
18
|
-
do_extend! get_module(module_name), get_type(type.to_sym)
|
15
|
+
on_load(type, &block)
|
19
16
|
end
|
20
|
-
|
21
|
-
def extend_with module_name
|
22
|
-
type = options[:in] || options
|
23
|
-
extend! module_name, type
|
24
|
-
end
|
25
|
-
alias_method :with, :extend_with
|
26
17
|
|
18
|
+
|
27
19
|
# convenience method to extend with multiple modules all within the same base module
|
28
|
-
def extend_from_module base_name, *module_names
|
20
|
+
def extend_from_module base_name, *module_names, options
|
21
|
+
raise ArgumentError, "You must specify an options Hash as the last argument for #extend_from_module" if !options.kind_of? Hash
|
29
22
|
module_names.each do |name|
|
30
|
-
extend_with "#{base_name.camelize}::#{name.to_s.camelize}".constantize
|
23
|
+
extend_with "#{base_name.camelize}::#{name.to_s.camelize}".constantize, options
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def extend_with *module_names, options
|
28
|
+
raise ArgumentError, "You must specify an options Hash as the last argument for #extend_with" if !options.kind_of? Hash
|
29
|
+
type = options[:in]
|
30
|
+
raise ArgumentError, "You must specify an :in option to indicate which Rails 3 component base class to extend with #{module_names}" if !type
|
31
|
+
module_names.each do |name|
|
32
|
+
extend! name, type
|
31
33
|
end
|
32
|
-
|
33
|
-
extend! module_name, type
|
34
|
-
end
|
34
|
+
end
|
35
35
|
|
36
|
+
module DSL
|
37
|
+
include Rails3::PluginExtender::LoadHandler
|
38
|
+
|
39
|
+
def extend_from_module base_name, *module_names
|
40
|
+
module_names.each do |name|
|
41
|
+
include "#{base_name.camelize}::#{name.to_s.camelize}".constantize
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def extend_with *module_names
|
46
|
+
module_names.each do |name|
|
47
|
+
include name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
alias_method :with, :extend_with
|
51
|
+
end
|
52
|
+
|
36
53
|
protected
|
37
|
-
|
54
|
+
|
55
|
+
def extend! module_name, type
|
56
|
+
do_extend! get_module(module_name), get_load_type(type.to_sym)
|
57
|
+
end
|
58
|
+
|
38
59
|
def do_extend! module_name, type
|
39
60
|
ActiveSupport.on_load(type) do
|
40
61
|
include module_name
|
41
62
|
end
|
42
|
-
end
|
43
|
-
|
63
|
+
end
|
44
64
|
end
|
45
65
|
end
|
@@ -1,31 +1,32 @@
|
|
1
|
-
|
1
|
+
module Rails3
|
2
|
+
class PluginExtender
|
3
|
+
module LoadHandler
|
4
|
+
include Rails3::PluginExtender::Util
|
2
5
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def load_before type, &block
|
8
|
-
raise ArgumentError "#{type} is not a valid before hook" if !valid_before_hook?
|
9
|
-
ActiveSupport.on_load(:"before_#{type}") do |loader|
|
10
|
-
do_loader loader, &block
|
6
|
+
def before type, &block
|
7
|
+
type = type.to_sym
|
8
|
+
raise ArgumentError, "#{type} is not a valid before hook" if !valid_before_hook? type
|
9
|
+
load_handling :"before_#{type}", &block
|
11
10
|
end
|
12
|
-
end
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
do_loader loader, &block
|
12
|
+
def after type, &block
|
13
|
+
type = type.to_sym
|
14
|
+
raise ArgumentError, "#{type} is not a valid after hook" if !valid_after_hook? type
|
15
|
+
load_handling :"after_#{type}", &block
|
19
16
|
end
|
20
|
-
end
|
21
|
-
|
22
|
-
protected
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
18
|
+
def on_load type, &block
|
19
|
+
type = get_load_type type
|
20
|
+
raise ArgumentError, "#{type} is not a valid load hook" if !valid_load_hook? type
|
21
|
+
load_handling type, &block
|
28
22
|
end
|
29
|
-
|
23
|
+
|
24
|
+
def load_handling type, &block
|
25
|
+
ActiveSupport.on_load type do
|
26
|
+
extend Rails3::PluginExtender::DSL
|
27
|
+
instance_eval(&block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
30
31
|
end
|
31
32
|
end
|
data/lib/plugin_toolbox/util.rb
CHANGED
@@ -1,24 +1,41 @@
|
|
1
|
-
|
1
|
+
module Rails3
|
2
|
+
class PluginExtender
|
3
|
+
module Util
|
4
|
+
INIT = :initialize
|
5
|
+
ACTIVE_MODULES = {:AR => :active_record, :view => :action_view, :controller => :action_controller, :mailer => :action_mailer}
|
6
|
+
|
7
|
+
def get_load_type type
|
8
|
+
return ACTIVE_MODULES[type] if ACTIVE_MODULES[type]
|
9
|
+
return type if ACTIVE_MODULES.values.include? type
|
10
|
+
return type if type == :i18n
|
11
|
+
logger.warn "WARNING: The Rails 3 load handler for the component #{type} is not part of the default load process."
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_module module_name
|
15
|
+
case module_name
|
16
|
+
when Constant
|
17
|
+
module_name
|
18
|
+
when String
|
19
|
+
module_name.to_s.constantize
|
20
|
+
else
|
21
|
+
raise ArgumentError, "#{module_name} could not be converted into a module constant"
|
22
|
+
end
|
23
|
+
end
|
2
24
|
|
3
|
-
|
4
|
-
module Util
|
5
|
-
include Rails::PluginToolbox::TypeValidator
|
25
|
+
protected
|
6
26
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
raise ArgumentError, "The type #{type} was not recognized"
|
11
|
-
end
|
27
|
+
def valid_before_hook? type
|
28
|
+
[INIT, :configuration, :eager_load].include?(type)
|
29
|
+
end
|
12
30
|
|
13
|
-
|
14
|
-
|
15
|
-
when Constant
|
16
|
-
module_name
|
17
|
-
when String
|
18
|
-
module_name.to_s.constantize
|
19
|
-
else
|
20
|
-
raise ArgumentError, "#{module_name} could not be converted into a loaded module"
|
31
|
+
def valid_after_hook? type
|
32
|
+
INIT == type
|
21
33
|
end
|
34
|
+
|
35
|
+
def valid_load_hook? type
|
36
|
+
@valid_load_hooks ||= [ACTIVE_MODULES.keys, ACTIVE_MODULES.values, :i18n].flatten
|
37
|
+
@valid_load_hooks.include?(type)
|
38
|
+
end
|
22
39
|
end
|
23
40
|
end
|
24
41
|
end
|
File without changes
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rails3_plugin_toolbox}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kristian Mandrup"]
|
12
|
-
s.date = %q{2010-08-
|
12
|
+
s.date = %q{2010-08-22}
|
13
13
|
s.description = %q{Toolbox to facilitate Rails 3 plugin development}
|
14
14
|
s.email = %q{kmandrup@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -24,17 +24,20 @@ Gem::Specification.new do |s|
|
|
24
24
|
"README.markdown",
|
25
25
|
"Rakefile",
|
26
26
|
"VERSION",
|
27
|
+
"config/database.yml",
|
27
28
|
"lib/plugin_toolbox/extender.rb",
|
28
29
|
"lib/plugin_toolbox/loader.rb",
|
29
30
|
"lib/plugin_toolbox/util.rb",
|
30
|
-
"lib/plugin_toolbox/validator/hook_validator.rb",
|
31
|
-
"lib/plugin_toolbox/validator/type_validator.rb",
|
32
31
|
"lib/rails3_plugin_toolbox.rb",
|
32
|
+
"log/development.log",
|
33
33
|
"rails3_plugin_toolbox.gemspec",
|
34
|
+
"spec/plugin_toolbox/extender_action_spec.rb",
|
35
|
+
"spec/plugin_toolbox/extender_i18n_spec.rb",
|
34
36
|
"spec/plugin_toolbox/extender_spec.rb",
|
35
|
-
"spec/
|
36
|
-
"
|
37
|
-
"
|
37
|
+
"spec/spec_helper.rb",
|
38
|
+
"wiki/add_rake_tasks.markdown",
|
39
|
+
"wiki/engine.markdown",
|
40
|
+
"wiki/how_to_guide.markdown"
|
38
41
|
]
|
39
42
|
s.homepage = %q{http://github.com/kristianmandrup/rails3_plugin_toolbox}
|
40
43
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -42,9 +45,9 @@ Gem::Specification.new do |s|
|
|
42
45
|
s.rubygems_version = %q{1.3.7}
|
43
46
|
s.summary = %q{Toolbox to facilitate Rails 3 plugin development}
|
44
47
|
s.test_files = [
|
45
|
-
"spec/plugin_toolbox/
|
46
|
-
"spec/plugin_toolbox/
|
47
|
-
"spec/plugin_toolbox/
|
48
|
+
"spec/plugin_toolbox/extender_action_spec.rb",
|
49
|
+
"spec/plugin_toolbox/extender_i18n_spec.rb",
|
50
|
+
"spec/plugin_toolbox/extender_spec.rb",
|
48
51
|
"spec/spec_helper.rb"
|
49
52
|
]
|
50
53
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rails3::PluginExtender do
|
4
|
+
describe '#extend_rails' do
|
5
|
+
it "should extend Action View" do
|
6
|
+
extender = Rails3::PluginExtender.new
|
7
|
+
extender.extend_rails :view do
|
8
|
+
with MyAddition
|
9
|
+
|
10
|
+
after :initialize do
|
11
|
+
MyAddition.say 'view it!'
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Initialize the rails application
|
16
|
+
Minimal::Application.initialize!
|
17
|
+
|
18
|
+
ActionView::Base.instance_methods.grep(/zzz/).should_not be_empty
|
19
|
+
|
20
|
+
MyAddition.heard.should == 'view it!'
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should extend Action Controller" do
|
24
|
+
Rails3::PluginExtender.new do
|
25
|
+
extend_rails :controller do
|
26
|
+
with MyAddition
|
27
|
+
|
28
|
+
after :initialize do
|
29
|
+
MyAddition.say 'control it!'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Initialize the rails application
|
35
|
+
Minimal::Application.initialize!
|
36
|
+
|
37
|
+
ActionController::Base.instance_methods.grep(/zzz/).should_not be_empty
|
38
|
+
|
39
|
+
MyAddition.heard.should == 'control it!'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should extend Action Mailer" do
|
43
|
+
Rails3::PluginExtender.new do
|
44
|
+
extend_rails :mailer do
|
45
|
+
with MyAddition
|
46
|
+
|
47
|
+
after :initialize do
|
48
|
+
MyAddition.say 'mail me!'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Initialize the rails application
|
54
|
+
Minimal::Application.initialize!
|
55
|
+
|
56
|
+
ActionMailer::Base.instance_methods.grep(/zzz/).should_not be_empty
|
57
|
+
|
58
|
+
MyAddition.heard.should == 'mail me!'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rails3::PluginExtender do
|
4
|
+
describe '#extend_rails' do
|
5
|
+
it "should extend i18n" do
|
6
|
+
Rails3::PluginExtender.new do
|
7
|
+
extend_rails :i18n do
|
8
|
+
with MyAddition
|
9
|
+
|
10
|
+
before :initialize do
|
11
|
+
MyOtherAddition.say 'before localized!'
|
12
|
+
end
|
13
|
+
|
14
|
+
before :configuration do
|
15
|
+
MyOtherAddition.configured = 'was configured!'
|
16
|
+
end
|
17
|
+
|
18
|
+
before :eager_load do
|
19
|
+
puts "before eager load!"
|
20
|
+
end
|
21
|
+
|
22
|
+
after :initialize do
|
23
|
+
MyAddition.say 'localized!'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Initialize the rails application
|
29
|
+
Minimal::Application.initialize!
|
30
|
+
I18n.methods.grep(/zzz/).should_not be_empty
|
31
|
+
|
32
|
+
MyAddition.heard.should == 'localized!'
|
33
|
+
MyOtherAddition.heard.should == 'before localized!'
|
34
|
+
MyOtherAddition.configured.should == 'was configured!'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rails3::PluginExtender do
|
4
|
+
describe '#extend_rails' do
|
5
|
+
it "should NOT extend Unknown" do
|
6
|
+
lambda do
|
7
|
+
Rails3::PluginExtender.new
|
8
|
+
extend_rails :unknown do
|
9
|
+
with MyAddition
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end.should raise_error
|
13
|
+
|
14
|
+
# Initialize the rails application
|
15
|
+
Minimal::Application.initialize!
|
16
|
+
|
17
|
+
MyAddition.heard.should == null
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should extend Active Record" do
|
21
|
+
Rails3::PluginExtender.new do
|
22
|
+
extend_rails :AR do
|
23
|
+
with MyAddition
|
24
|
+
|
25
|
+
after :initialize do
|
26
|
+
MyAddition.say 'record me!'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Initialize the rails application
|
32
|
+
Minimal::Application.initialize!
|
33
|
+
|
34
|
+
ActiveRecord::Base.instance_methods.grep(/zzz/).should_not be_empty
|
35
|
+
|
36
|
+
MyAddition.heard.should == 'record me!'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,51 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require 'rspec/autorun'
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_record'
|
5
|
+
require 'action_controller'
|
6
|
+
require 'action_view'
|
3
7
|
require 'rails3_plugin_toolbox'
|
4
8
|
|
9
|
+
# See http://www.igvita.com/2010/08/04/rails-3-internals-railtie-creating-plugins/
|
10
|
+
|
11
|
+
# require 'active_support/railtie'
|
12
|
+
require 'rails/all'
|
13
|
+
|
14
|
+
module Minimal
|
15
|
+
class Application < Rails::Application
|
16
|
+
config.active_support.deprecation = :log
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module MyAddition
|
21
|
+
def zzz
|
22
|
+
'zzz'
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
attr_accessor :heard
|
27
|
+
|
28
|
+
def say message
|
29
|
+
@heard = message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module MyOtherAddition
|
35
|
+
def yyy
|
36
|
+
'yyy'
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
attr_accessor :heard
|
41
|
+
attr_accessor :configured
|
42
|
+
|
43
|
+
def say message
|
44
|
+
@heard = message
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
5
50
|
RSpec.configure do |config|
|
6
|
-
|
7
51
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
The following are some general tips for adding custom rake tasks and generators to your Rails 3 plugin.
|
2
|
+
|
3
|
+
## Custom rake tasks
|
4
|
+
|
5
|
+
<pre>
|
6
|
+
# my_plugin/lib/railtie.rb
|
7
|
+
module MyPlugin
|
8
|
+
class Railtie < ::Rails::Railtie
|
9
|
+
rake_tasks do
|
10
|
+
load "my_plugin/railties/tasks.rake"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
</pre>
|
15
|
+
|
16
|
+
<pre>
|
17
|
+
# my_plugin/lib/railties/tasks.rake
|
18
|
+
desc "Talk about being in my_gem"
|
19
|
+
task :my_gem do
|
20
|
+
puts "You're in my_gem"
|
21
|
+
end
|
22
|
+
</pre>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
Any Rails::Engine is also a Rails::Railtie, so the same methods (like rake_tasks and generators) and configuration available in the latter can also be used in the former.
|
2
|
+
|
3
|
+
<pre>
|
4
|
+
class MyEngine < Rails::Engine
|
5
|
+
# Add a load path for this specific Engine
|
6
|
+
config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
|
7
|
+
|
8
|
+
initializer "my_engine.add_middleware" do |app|
|
9
|
+
app.middleware.use MyEngine::Middleware
|
10
|
+
end
|
11
|
+
end
|
12
|
+
</pre>
|
13
|
+
|
14
|
+
h2.Paths
|
15
|
+
|
16
|
+
Since Rails 3.0, both your Application and Engines do not have hardcoded paths. This means that you are not required to place your controllers at “app/controllers“, but in any place which you find convenient.
|
17
|
+
|
18
|
+
For example, let’s suppose you want to lay your controllers at lib/controllers, all you need to do is:
|
19
|
+
|
20
|
+
<pre>
|
21
|
+
class MyEngine < Rails::Engine
|
22
|
+
paths.app.controllers = "lib/controllers"
|
23
|
+
end
|
24
|
+
|
25
|
+
</pre>
|
26
|
+
|
27
|
+
You can also have your controllers being loaded from both “app/controllers“ and “lib/controllers“:
|
28
|
+
|
29
|
+
<pre>
|
30
|
+
class MyEngine < Rails::Engine
|
31
|
+
paths.app.controllers << "lib/controllers"
|
32
|
+
end
|
33
|
+
</pre>
|
34
|
+
|
35
|
+
The available paths in an Engine are:
|
36
|
+
|
37
|
+
<pre>
|
38
|
+
class MyEngine < Rails::Engine
|
39
|
+
paths.app = "app"
|
40
|
+
paths.app.controllers = "app/controllers"
|
41
|
+
paths.app.helpers = "app/helpers"
|
42
|
+
paths.app.models = "app/models"
|
43
|
+
paths.app.views = "app/views"
|
44
|
+
paths.lib = "lib"
|
45
|
+
paths.lib.tasks = "lib/tasks"
|
46
|
+
paths.config = "config"
|
47
|
+
paths.config.initializers = "config/initializers"
|
48
|
+
paths.config.locales = "config/locales"
|
49
|
+
paths.config.routes = "config/routes.rb"
|
50
|
+
end
|
51
|
+
|
52
|
+
</pre>
|
@@ -0,0 +1,153 @@
|
|
1
|
+
http://www.igvita.com/2010/08/04/rails-3-internals-railtie-creating-plugins/
|
2
|
+
|
3
|
+
The documentation for Railtie is a great place to get started, but the interesting observation is that each of the major Rails components (Action Mailer/Controller/View/Record) is itself a Railtie, and Rails as you know it is simply pieced together by requiring all of the independent components. Even better, if your plugin or gem needs to hook into the Rails initialization process, all you need to do is inherit from Railtie and you are ready to go!
|
4
|
+
|
5
|
+
So how does Railtie know to call your classes and initializers? Railtie defines a self.inherited method, which is called anytime a subclass of Railtie is created, and stashes a reference to this class in a @subclasses variable. From there, the framework can simply call Railtie.subclasses and perform all the initialization as usual - a clever use of the Ruby object model.
|
6
|
+
|
7
|
+
module NewPlugin
|
8
|
+
# namespace our plugin and inherit from Rails::Railtie
|
9
|
+
# to get our plugin into the initialization process
|
10
|
+
class Railtie < Rails::Railtie
|
11
|
+
|
12
|
+
# configure our plugin on boot. other extension points such
|
13
|
+
# as configuration, rake tasks, etc, are also available
|
14
|
+
initializer "newplugin.initialize" do |app|
|
15
|
+
|
16
|
+
# subscribe to all rails notifications: controllers, AR, etc.
|
17
|
+
ActiveSupport::Notifications.subscribe do |*args|
|
18
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
19
|
+
puts "Got notification: #{event.inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
---
|
27
|
+
|
28
|
+
Railtie is the core of the Rails Framework and provides several hooks to extend Rails and/or modify the initialization process.
|
29
|
+
|
30
|
+
Every major component of Rails (Action Mailer, Action Controller, Action View, Active Record and Active Resource) are all Railties, so each of them is responsible to set their own initialization. This makes, for example, Rails absent of any Active Record hook, allowing any other ORM framework to hook in.
|
31
|
+
|
32
|
+
Developing a Rails extension does not require any implementation of Railtie, but if you need to interact with the Rails framework during or after boot, then Railtie is what you need to do that interaction.
|
33
|
+
|
34
|
+
For example, the following would need you to implement Railtie in your plugin:
|
35
|
+
|
36
|
+
* creating initializers
|
37
|
+
* configuring a Rails framework or the Application, like setting a generator
|
38
|
+
* adding Rails config.* keys to the environment
|
39
|
+
* setting up a subscriber to the Rails +ActiveSupport::Notifications+
|
40
|
+
* adding rake tasks into rails
|
41
|
+
|
42
|
+
h2. Creating your Railtie
|
43
|
+
|
44
|
+
Implementing Railtie in your Rails extension is done by creating a class Railtie that has your extension name and making sure that this gets loaded during boot time of the Rails stack.
|
45
|
+
|
46
|
+
You can do this however you wish, but here is an example if you want to provide it for a gem that can be used with or without Rails:
|
47
|
+
|
48
|
+
* Create a file (say, lib/my_gem/railtie.rb) which contains class Railtie inheriting from Rails::Railtie and is namespaced to your gem:
|
49
|
+
|
50
|
+
<pre>
|
51
|
+
# lib/my_gem/railtie.rb
|
52
|
+
module MyGem
|
53
|
+
class Railtie < Rails::Railtie
|
54
|
+
end
|
55
|
+
end
|
56
|
+
</pre>
|
57
|
+
|
58
|
+
* Require your own gem as well as rails in this file:
|
59
|
+
|
60
|
+
<pre>
|
61
|
+
# lib/my_gem/railtie.rb
|
62
|
+
require 'my_gem'
|
63
|
+
require 'rails'
|
64
|
+
|
65
|
+
module MyGem
|
66
|
+
class Railtie < Rails::Railtie
|
67
|
+
end
|
68
|
+
end
|
69
|
+
</pre>
|
70
|
+
|
71
|
+
h2. Initializers
|
72
|
+
|
73
|
+
To add an initialization step from your Railtie to Rails boot process, you just need to create an initializer block:
|
74
|
+
|
75
|
+
<pre>
|
76
|
+
class MyRailtie < Rails::Railtie
|
77
|
+
initializer "my_railtie.configure_rails_initialization" do
|
78
|
+
# some initialization behavior
|
79
|
+
end
|
80
|
+
end
|
81
|
+
</pre>
|
82
|
+
|
83
|
+
If specified, the block can also receive the application object, in case you need to access some application specific configuration, like middleware:
|
84
|
+
|
85
|
+
<pre>
|
86
|
+
class MyRailtie < Rails::Railtie
|
87
|
+
initializer "my_railtie.configure_rails_initialization" do |app|
|
88
|
+
app.middleware.use MyRailtie::Middleware
|
89
|
+
end
|
90
|
+
end
|
91
|
+
</pre>
|
92
|
+
|
93
|
+
Finally, you can also pass :before and :after as option to initializer, in case you want to couple it with a specific step in the initialization process.
|
94
|
+
Configuration
|
95
|
+
|
96
|
+
Inside the Railtie class, you can access a config object which contains configuration shared by all railties and the application:
|
97
|
+
|
98
|
+
<pre>
|
99
|
+
class MyRailtie < Rails::Railtie
|
100
|
+
# Customize the ORM
|
101
|
+
config.generators.orm :my_railtie_orm
|
102
|
+
|
103
|
+
# Add a to_prepare block which is executed once in production
|
104
|
+
# and before each request in development
|
105
|
+
config.to_prepare do
|
106
|
+
MyRailtie.setup!
|
107
|
+
end
|
108
|
+
end
|
109
|
+
</pre>
|
110
|
+
|
111
|
+
Loading rake tasks and generators
|
112
|
+
|
113
|
+
If your railtie has rake tasks, you can tell Rails to load them through the method rake tasks:
|
114
|
+
|
115
|
+
<pre>
|
116
|
+
class MyRailtie < Railtie
|
117
|
+
rake_tasks do
|
118
|
+
load "path/to/my_railtie.tasks"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
</pre>
|
122
|
+
|
123
|
+
By default, Rails load generators from your load path. However, if you want to place your generators at a different location, you can specify in your Railtie a block which will load them during normal generators lookup:
|
124
|
+
|
125
|
+
<pre>
|
126
|
+
class MyRailtie < Railtie
|
127
|
+
generators do
|
128
|
+
require "path/to/my_railtie_generator"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
</pre>
|
132
|
+
|
133
|
+
Application, Plugin and Engine
|
134
|
+
|
135
|
+
A Rails::Engine is nothing more than a Railtie with some initializers already set. And since Rails::Application and Rails::Plugin are engines, the same configuration described here can be used in all three.
|
136
|
+
|
137
|
+
Be sure to look at the documentation of those specific classes for more information.
|
138
|
+
|
139
|
+
Methods:
|
140
|
+
|
141
|
+
* abstract_railtie?
|
142
|
+
* console
|
143
|
+
* eager_load!
|
144
|
+
* generators
|
145
|
+
* inherited
|
146
|
+
* load_console
|
147
|
+
* load_generators
|
148
|
+
* load_tasks
|
149
|
+
* log_subscriber
|
150
|
+
* railtie_name
|
151
|
+
* rake_tasks
|
152
|
+
* subclasses
|
153
|
+
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Kristian Mandrup
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-08-
|
17
|
+
date: 2010-08-22 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -82,17 +82,20 @@ files:
|
|
82
82
|
- README.markdown
|
83
83
|
- Rakefile
|
84
84
|
- VERSION
|
85
|
+
- config/database.yml
|
85
86
|
- lib/plugin_toolbox/extender.rb
|
86
87
|
- lib/plugin_toolbox/loader.rb
|
87
88
|
- lib/plugin_toolbox/util.rb
|
88
|
-
- lib/plugin_toolbox/validator/hook_validator.rb
|
89
|
-
- lib/plugin_toolbox/validator/type_validator.rb
|
90
89
|
- lib/rails3_plugin_toolbox.rb
|
90
|
+
- log/development.log
|
91
91
|
- rails3_plugin_toolbox.gemspec
|
92
|
+
- spec/plugin_toolbox/extender_action_spec.rb
|
93
|
+
- spec/plugin_toolbox/extender_i18n_spec.rb
|
92
94
|
- spec/plugin_toolbox/extender_spec.rb
|
93
|
-
- spec/plugin_toolbox/loader_spec.rb
|
94
|
-
- spec/plugin_toolbox/util_spec.rb
|
95
95
|
- spec/spec_helper.rb
|
96
|
+
- wiki/add_rake_tasks.markdown
|
97
|
+
- wiki/engine.markdown
|
98
|
+
- wiki/how_to_guide.markdown
|
96
99
|
has_rdoc: true
|
97
100
|
homepage: http://github.com/kristianmandrup/rails3_plugin_toolbox
|
98
101
|
licenses: []
|
@@ -126,7 +129,7 @@ signing_key:
|
|
126
129
|
specification_version: 3
|
127
130
|
summary: Toolbox to facilitate Rails 3 plugin development
|
128
131
|
test_files:
|
132
|
+
- spec/plugin_toolbox/extender_action_spec.rb
|
133
|
+
- spec/plugin_toolbox/extender_i18n_spec.rb
|
129
134
|
- spec/plugin_toolbox/extender_spec.rb
|
130
|
-
- spec/plugin_toolbox/loader_spec.rb
|
131
|
-
- spec/plugin_toolbox/util_spec.rb
|
132
135
|
- spec/spec_helper.rb
|
@@ -1,11 +0,0 @@
|
|
1
|
-
module Rails::PluginToolbox
|
2
|
-
module HookValidator
|
3
|
-
def valid_before_hook?
|
4
|
-
[:initialize, :configuration, :eager_load].include?(type)
|
5
|
-
end
|
6
|
-
|
7
|
-
def valid_after_hook?
|
8
|
-
[:initialize, :i18n, :active_record, :action_view, :action_controller, :action_mailer].include?(type)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
File without changes
|