提交 cfcea1d5 编写于 作者: P Piotr Sarnacki

Added 'rails plugin new' generator which generates gem plugin skeleton.

This command is based on enginex gem by José Valim. It generates gem structure
and ads dummy application into test/dummy. This can be used to start developing
any kind of extension for rails 3.
上级 d446392f
......@@ -11,4 +11,9 @@
require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit }
require 'rails/commands/application'
if ARGV.first == 'plugin'
ARGV.shift
require 'rails/commands/plugin_new'
else
require 'rails/commands/application'
end
require 'rails/version'
if %w(--version -v).include? ARGV.first
puts "Rails #{Rails::VERSION::STRING}"
exit(0)
end
if ARGV.first != "new"
ARGV[0] = "--help"
else
ARGV.shift
end
require 'rails/generators'
require 'rails/generators/rails/plugin_new/plugin_new_generator'
Rails::Generators::PluginNewGenerator.start
require "rails/generators/rails/app/app_generator"
module Rails
class PluginBuilder
def rakefile
template "Rakefile"
end
def readme
copy_file "README.rdoc"
end
def gemfile
template "Gemfile"
end
def license
template "MIT-LICENSE"
end
def gemspec
template "%name%.gemspec"
end
def gitignore
copy_file "gitignore", ".gitignore"
end
def lib
directory "lib"
end
def test
directory "test"
end
def test_dummy
invoke Rails::Generators::AppGenerator,
[ File.expand_path(dummy_path, destination_root) ]
end
def test_dummy_config
store_application_definition!
template "rails/boot.rb", "#{dummy_path}/config/boot.rb", :force => true
template "rails/application.rb", "#{dummy_path}/config/application.rb", :force => true
end
def test_dummy_clean
inside dummy_path do
remove_file ".gitignore"
remove_file "db/seeds.rb"
remove_file "doc"
remove_file "Gemfile"
remove_file "lib/tasks"
remove_file "public/images/rails.png"
remove_file "public/index.html"
remove_file "public/robots.txt"
remove_file "README"
remove_file "test"
remove_file "vendor"
end
end
def script
directory "script" do |content|
"#{shebang}\n" + content
end
chmod "script", 0755, :verbose => false
end
end
module Generators
class PluginNewGenerator < Base
attr_accessor :rails_template
add_shebang_option!
argument :plugin_path, :type => :string
class_option :builder, :type => :string, :aliases => "-b",
:desc => "Path to a plugin builder (can be a filesystem path or URL)"
class_option :skip_gemfile, :type => :boolean, :default => false,
:desc => "Don't create a Gemfile"
class_option :skip_git, :type => :boolean, :aliases => "-G", :default => false,
:desc => "Skip Git ignores and keeps"
class_option :help, :type => :boolean, :aliases => "-h", :group => :rails,
:desc => "Show this help message and quit"
def self.say_step(message)
@step = (@step || 0) + 1
class_eval <<-METHOD, __FILE__, __LINE__ + 1
def step_#{@step}
#{"puts" if @step > 1}
say_status "STEP #{@step}", #{message.inspect}
end
METHOD
end
def initialize(*args)
raise Error, "Options should be given after plugin name. For details run: rails plugin --help" if args[0].blank?
@original_wd = Dir.pwd
super
end
say_step "Creating gem skeleton"
def create_root
self.destination_root = File.expand_path(plugin_path, destination_root)
valid_plugin_const?
empty_directory '.'
FileUtils.cd(destination_root) unless options[:pretend]
end
def create_root_files
build(:readme)
build(:rakefile)
build(:gemspec)
build(:license)
build(:gitignore) unless options[:skip_git]
build(:gemfile) unless options[:skip_gemfile]
end
def create_config_files
build(:config)
end
def create_lib_files
build(:lib)
end
def create_script_files
build(:script)
end
def create_test_files
build(:test) unless options[:skip_test_unit]
end
say_step "Vendoring Rails application at test/dummy"
def create_test_dummy_files
build(:test_dummy)
end
say_step "Configuring Rails application"
def change_config_files
build(:test_dummy_config)
end
say_step "Removing unneeded files"
def remove_uneeded_rails_files
build(:test_dummy_clean)
end
protected
def self.banner
"rails plugin new #{self.arguments.map(&:usage).join(' ')} [options]"
end
def builder
@builder ||= begin
if path = options[:builder]
if URI(path).is_a?(URI::HTTP)
contents = open(path, "Accept" => "application/x-thor-template") {|io| io.read }
else
contents = open(File.expand_path(path, @original_wd)) {|io| io.read }
end
prok = eval("proc { #{contents} }", TOPLEVEL_BINDING, path, 1)
instance_eval(&prok)
end
builder_class = defined?(::PluginBuilder) ? ::PluginBuilder : Rails::PluginBuilder
builder_class.send(:include, ActionMethods)
builder_class.new(self)
end
end
def build(meth, *args)
builder.send(meth, *args) if builder.respond_to?(meth)
end
def name
@name ||= File.basename(destination_root)
end
def camelized
@camelized ||= name.gsub(/\W/, '_').squeeze('_').camelize
end
def valid_plugin_const?
if camelized =~ /^\d/
raise Error, "Invalid plugin name #{name}. Please give a name which does not start with numbers."
elsif RESERVED_NAMES.include?(name)
raise Error, "Invalid plugin name #{name}. Please give a name which does not match one of the reserved rails words."
elsif Object.const_defined?(camelized)
raise Error, "Invalid plugin name #{name}, constant #{camelized} is already in use. Please choose another application name."
end
end
def dummy_path
"test/dummy"
end
def application_definition
@application_definition ||= begin
unless options[:pretend]
contents = File.read(File.expand_path("#{dummy_path}/config/application.rb", destination_root))
contents[(contents.index("module Dummy"))..-1]
end
end
end
alias :store_application_definition! :application_definition
end
end
end
# Provide a simple gemspec so you can easily use your
# project in your rails apps through git.
Gem::Specification.new do |s|
s.name = "<%= name %>"
s.summary = "Insert <%= camelized %> summary."
s.description = "Insert <%= camelized %> description."
s.files = Dir["lib/**/*"] + ["MIT-LICENSE", "Rakefile", "README.rdoc"]
s.version = "0.0.1"
end
source "http://rubygems.org"
gem "rails", :git => "http://github.com/rails/rails.git"
gem "arel" , :git => "http://github.com/rails/arel.git"
gem "rack" , :git => "http://github.com/rack/rack.git"
gem "capybara", ">= 0.3.9"
gem "sqlite3-ruby", :require => "sqlite3"
if RUBY_VERSION < '1.9'
gem "ruby-debug", ">= 0.10.3"
end
Copyright <%= Date.today.year %> YOURNAME
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
= <%= camelized %>
This project rocks and uses MIT-LICENSE.
\ No newline at end of file
# encoding: UTF-8
require 'rubygems'
begin
require 'bundler/setup'
rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end
require 'rake'
require 'rake/rdoctask'
require 'rake/gempackagetask'
require 'rake/testtask'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = false
end
task :default => :test
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = '<%= camelized %>'
rdoc.options << '--line-numbers' << '--inline-source'
rdoc.rdoc_files.include('README.rdoc')
rdoc.rdoc_files.include('lib/**/*.rb')
end
spec = Gem::Specification.new do |s|
s.name = "<%= name %>"
s.summary = "Insert <%= camelized %> summary."
s.description = "Insert <%= camelized %> description."
s.files = FileList["[A-Z]*", "lib/**/*"]
s.version = "0.0.1"
end
Rake::GemPackageTask.new(spec) do |pkg|
end
desc "Install the gem #{spec.name}-#{spec.version}.gem"
task :install do
system("gem install pkg/#{spec.name}-#{spec.version}.gem --no-ri --no-rdoc")
end
.bundle/
log/*.log
pkg/
test/dummy/db/*.sqlite3
test/dummy/log/*.log
test/dummy/tmp/
\ No newline at end of file
require File.expand_path('../boot', __FILE__)
require "active_model/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_view/railtie"
require "action_mailer/railtie"
Bundler.require
require "<%= name %>"
<%= application_definition %>
require 'rubygems'
gemfile = File.expand_path('../../../../Gemfile', __FILE__)
if File.exist?(gemfile)
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
end
$:.unshift File.expand_path('../../../../lib', __FILE__)
\ No newline at end of file
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
ENGINE_PATH = File.expand_path('../..', __FILE__)
load File.expand_path('../../test/dummy/script/rails', __FILE__)
require 'test_helper'
class <%= camelized %>Test < ActiveSupport::TestCase
test "truth" do
assert_kind_of Module, <%= camelized %>
end
end
require 'test_helper'
class NagivationTest < ActiveSupport::IntegrationCase
test "truth" do
assert_kind_of Dummy::Application, Rails.application
end
end
# Define a bare test case to use with Capybara
class ActiveSupport::IntegrationCase < ActiveSupport::TestCase
include Capybara
include Rails.application.routes.url_helpers
end
\ No newline at end of file
# Configure Rails Envinronment
ENV["RAILS_ENV"] = "test"
require File.expand_path("../dummy/config/environment.rb", __FILE__)
require "rails/test_help"
ActionMailer::Base.delivery_method = :test
ActionMailer::Base.perform_deliveries = true
ActionMailer::Base.default_url_options[:host] = "test.com"
Rails.backtrace_cleaner.remove_silencers!
# Configure capybara for integration testing
require "capybara/rails"
Capybara.default_driver = :rack_test
Capybara.default_selector = :css
# Run any available migration
ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
# Load support files
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
\ No newline at end of file
class PluginBuilder
def gitignore
create_file ".gitignore", <<-R.strip
foobar
R
end
end
class PluginBuilder < Rails::PluginBuilder
def gitignore
create_file ".gitignore", <<-R.strip
foobar
R
end
end
require 'abstract_unit'
require 'generators/generators_test_helper'
require 'rails/generators/rails/plugin_new/plugin_new_generator'
DEFAULT_PLUGIN_FILES = %w(
.gitignore
Gemfile
Rakefile
bukkits.gemspec
MIT-LICENSE
lib
lib/bukkits.rb
script/rails
test/bukkits_test.rb
test/integration/navigation_test.rb
test/support/integration_case.rb
test/test_helper.rb
test/dummy
)
class PluginNewGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
destination File.join(Rails.root, "tmp/bukkits")
arguments [destination_root]
def setup
Rails.application = TestApp::Application
super
@bundle_command = File.basename(Thor::Util.ruby_command).sub(/ruby/, 'bundle')
Kernel::silence_warnings do
Thor::Base.shell.send(:attr_accessor, :always_force)
@shell = Thor::Base.shell.new
@shell.send(:always_force=, true)
end
end
def teardown
super
Rails.application = TestApp::Application.instance
end
def test_plugin_skeleton_is_created
run_generator
DEFAULT_PLUGIN_FILES.each{ |path| assert_file path }
end
def test_plugin_new_generate_pretend
run_generator ["testapp", "--pretend"]
DEFAULT_PLUGIN_FILES.each{ |path| assert_no_file path }
end
def test_options_before_plugin_name_raises_an_error
content = capture(:stderr){ run_generator(["--pretend", destination_root]) }
assert_equal "Options should be given after plugin name. For details run: rails plugin --help\n", content
end
def test_name_collision_raises_an_error
reserved_words = %w[application destroy plugin runner test]
reserved_words.each do |reserved|
content = capture(:stderr){ run_generator [File.join(destination_root, reserved)] }
assert_equal "Invalid plugin name #{reserved}. Please give a name which does not match one of the reserved rails words.\n", content
end
end
def test_invalid_plugin_name_raises_an_error
content = capture(:stderr){ run_generator [File.join(destination_root, "43-things")] }
assert_equal "Invalid plugin name 43-things. Please give a name which does not start with numbers.\n", content
end
def test_plugin_name_raises_an_error_if_name_already_used_constant
%w{ String Hash Class Module Set Symbol }.each do |ruby_class|
content = capture(:stderr){ run_generator [File.join(destination_root, ruby_class)] }
assert_equal "Invalid plugin name #{ruby_class}, constant #{ruby_class} is already in use. Please choose another application name.\n", content
end
end
def test_invalid_plugin_name_is_fixed
run_generator [File.join(destination_root, "things-43")]
assert_file "things-43/lib/things-43.rb", /module Things43/
end
def test_shebang_is_added_to_rails_file
run_generator [destination_root, "--ruby", "foo/bar/baz"]
assert_file "script/rails", /#!foo\/bar\/baz/
end
def test_shebang_when_is_the_same_as_default_use_env
run_generator [destination_root, "--ruby", Thor::Util.ruby_command]
assert_file "script/rails", /#!\/usr\/bin\/env/
end
def test_generating_test_files
run_generator
assert_file "test/test_helper.rb"
assert_directory "test/support/"
assert_directory "test/integration/"
assert_file "test/bukkits_test.rb", /assert_kind_of Module, Bukkits/
assert_file "test/integration/navigation_test.rb", /assert_kind_of Dummy::Application, Rails.application/
assert_file "test/support/integration_case.rb", /class ActiveSupport::IntegrationCase/
end
protected
def action(*args, &block)
silence(:stdout){ generator.send(*args, &block) }
end
end
class CustomPluginGeneratorTest < Rails::Generators::TestCase
include GeneratorsTestHelper
tests Rails::Generators::PluginNewGenerator
destination File.join(Rails.root, "tmp/bukkits")
arguments [destination_root]
def setup
Rails.application = TestApp::Application
super
@bundle_command = File.basename(Thor::Util.ruby_command).sub(/ruby/, 'bundle')
end
def teardown
super
Object.class_eval { remove_const :PluginBuilder if const_defined?(:PluginBuilder) }
Rails.application = TestApp::Application.instance
end
def test_builder_option_with_empty_app_builder
FileUtils.cd(destination_root)
run_generator([destination_root, "-b", "#{Rails.root}/lib/plugin_builders/empty_builder.rb"])
DEFAULT_PLUGIN_FILES.each{ |path| assert_no_file path }
end
def test_builder_option_with_simple_plugin_builder
FileUtils.cd(destination_root)
run_generator([destination_root, "-b", "#{Rails.root}/lib/plugin_builders/simple_builder.rb"])
(DEFAULT_PLUGIN_FILES - ['.gitignore']).each{ |path| assert_no_file path }
assert_file ".gitignore", "foobar"
end
def test_builder_option_with_relative_path
here = File.expand_path(File.dirname(__FILE__))
FileUtils.cd(here)
run_generator([destination_root, "-b", "../fixtures/lib/plugin_builders/simple_builder.rb"])
FileUtils.cd(destination_root)
(DEFAULT_PLUGIN_FILES - ['.gitignore']).each{ |path| assert_no_file path }
assert_file ".gitignore", "foobar"
end
def test_builder_option_with_tweak_plugin_builder
FileUtils.cd(destination_root)
run_generator([destination_root, "-b", "#{Rails.root}/lib/plugin_builders/tweak_builder.rb"])
DEFAULT_PLUGIN_FILES.each{ |path| assert_file path }
assert_file ".gitignore", "foobar"
end
def test_builder_option_with_http
path = "http://gist.github.com/103208.txt"
template = "class PluginBuilder; end"
template.instance_eval "def read; self; end" # Make the string respond to read
generator([destination_root], :builder => path).expects(:open).with(path, 'Accept' => 'application/x-thor-template').returns(template)
capture(:stdout) { generator.invoke_all }
DEFAULT_PLUGIN_FILES.each{ |path| assert_no_file path }
end
protected
def action(*args, &block)
silence(:stdout){ generator.send(*args, &block) }
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册