Permalink
Browse files

Initial commit

  • Loading branch information...
1 parent d2e37b9 commit 1b279594adc9592e775b91785b5a3863118dc02e @sinisterchipmunk committed Jun 12, 2010
View
2 .gitignore
@@ -1,3 +1,5 @@
+.idea
+
## MAC OS
.DS_Store
View
6 Rakefile
@@ -5,12 +5,12 @@ begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "genspec"
- gem.summary = %Q{TODO: one-line summary of your gem}
- gem.description = %Q{TODO: longer description of your gem}
+ gem.summary = %Q{one-line summary of your gem}
+ gem.description = %Q{longer description of your gem}
gem.email = "sinisterchipmunk@gmail.com"
gem.homepage = "http://github.com/sinisterchipmunk/genspec"
gem.authors = ["Colin MacKenzie IV"]
- gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
+ gem.files = FileList['**/*']
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
end
Jeweler::GemcutterTasks.new
View
59 genspec.gemspec
@@ -0,0 +1,59 @@
+# Generated by jeweler
+# DO NOT EDIT THIS FILE DIRECTLY
+# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
+# -*- encoding: utf-8 -*-
+
+Gem::Specification.new do |s|
+ s.name = %q{genspec}
+ s.version = "0.0.0"
+
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
+ s.authors = ["Colin MacKenzie IV"]
+ s.date = %q{2010-06-12}
+ s.description = %q{longer description of your gem}
+ s.email = %q{sinisterchipmunk@gmail.com}
+ s.extra_rdoc_files = [
+ "LICENSE",
+ "README.rdoc"
+ ]
+ s.files = [
+ "LICENSE",
+ "README.rdoc",
+ "Rakefile",
+ "VERSION",
+ "genspec.gemspec",
+ "lib/genspec.rb",
+ "lib/genspec/generation_matchers.rb",
+ "lib/genspec/generation_matchers/generation_matcher.rb",
+ "lib/genspec/generation_matchers/result_matcher.rb",
+ "lib/genspec/generator_example_group.rb",
+ "pkg/genspec-0.0.0.gem",
+ "spec/environment_spec.rb",
+ "spec/generators/test_spec.rb",
+ "spec/spec_helper.rb",
+ "spec/support/generators/test/templates/file",
+ "spec/support/generators/test/test_generator.rb"
+ ]
+ s.homepage = %q{http://github.com/sinisterchipmunk/genspec}
+ s.rdoc_options = ["--charset=UTF-8"]
+ s.require_paths = ["lib"]
+ s.rubygems_version = %q{1.3.6}
+ s.summary = %q{one-line summary of your gem}
+ s.test_files = [
+ "spec/environment_spec.rb",
+ "spec/generators/test_spec.rb",
+ "spec/spec_helper.rb",
+ "spec/support/generators/test/test_generator.rb"
+ ]
+
+ if s.respond_to? :specification_version then
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
+ s.specification_version = 3
+
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
+ else
+ end
+ else
+ end
+end
+
View
14 lib/genspec.rb
@@ -0,0 +1,14 @@
+require 'spec'
+require 'fileutils'
+
+require 'sc-core-ext'
+
+require 'genspec/generation_matchers'
+require 'genspec/generator_example_group'
+
+if defined?(RAILS_ROOT)
+ require 'rails_generator'
+ require 'rails_generator/scripts/generate'
+end
+
+Spec::Example::ExampleGroupFactory.register(:generator, GenSpec::GeneratorExampleGroup)
View
27 lib/genspec/generation_matchers.rb
@@ -0,0 +1,27 @@
+require 'genspec/generation_matchers/generation_matcher'
+require 'genspec/generation_matchers/result_matcher'
+
+module GenSpec
+ module GenerationMatchers
+ # Valid types: :dependency, :class_collisions, :file, :template, :complex_template, :directory, :readme,
+ # :migration_template, :route_resources
+ def generate(kind, *args, &block)
+ case kind.to_s
+ when *GenSpec::GenerationMatchers::GenerationMatcher.generation_methods
+ GenSpec::GenerationMatchers::GenerationMatcher.new(kind, *args, &block)
+ else
+ if kind.kind_of?(String)
+ GenSpec::GenerationMatchers::ResultMatcher.new(kind, &block)
+ else
+ raise ArgumentError, "No generator matcher for #{kind.inspect}"
+ end
+ end
+ end
+
+ # This tests the content sent to the command line, instead of the generated product.
+ # Useful for testing help messages, etc.
+ def output(text_or_regexp)
+ GenSpec::GenerationMatchers::GenerationMatcher.new(:output, text_or_regexp)
+ end
+ end
+end
View
137 lib/genspec/generation_matchers/generation_matcher.rb
@@ -0,0 +1,137 @@
+module GenSpec
+ module GenerationMatchers
+ class GenerationMatcher
+ delegate :generation_methods, :to => 'self.class'
+
+ def initialize(kind = nil, *args)
+ raise ArgumentError, "Call with kind" unless kind
+ @kind = kind.to_s
+ @args = args
+ @but_found = nil
+ end
+
+ def self.generation_methods
+ @generation_methods ||= %w(dependency class_collisions file template complex_template directory readme
+ migration_template route_resources)
+ end
+
+ def matches?(target)
+ # if it's a string then it's an error message or some such from the generator.
+ if target.kind_of?(Rails::Generator::GeneratorError)
+ # if we're not checking output then why hold on to it? Raise it like the error it is!
+ raise target unless @kind == 'output'
+ @log = target.message
+ else
+ temporary_root(target) do
+ replay(target)
+ end
+ end
+ match_content
+ matched?
+ end
+
+ def failure_message
+ "Expected to generate #{@kind}#{with_file}#{but_found}"
+ end
+
+ def negative_failure_message
+ "Expected not to generate #{@kind}#{with_file}"
+ end
+
+ protected
+
+ def replay(target)
+ @create = Rails::Generator::Commands::Create.new(target)
+ target.manifest.replay(self)
+ after_replay(target)
+ end
+
+ # hook
+ def after_replay(target)
+ end
+
+ def matched!
+ @matched = true
+ end
+
+ def matched?
+ !!@matched
+ end
+
+ private
+ def temporary_root(target)
+ # We could bear to split this into two methods, one called #suspend_logging or some such.
+ original_root = target.instance_variable_get("@destination_root")
+ original_logger = Rails::Generator::Base.logger
+ original_quiet = target.logger.quiet
+ @log = ""
+
+ ### WHY does this not work? Instead we are forced to reroute the log output rather than just silence it.
+ target.logger.quiet = true
+ Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(StringIO.new(@log))
+
+ Dir.mktmpdir do |dir|
+ # need to copy a few files for some methods, ie route_resources
+ Dir.mkdir(File.join(dir, "config"))
+ FileUtils.cp File.join(RAILS_ROOT, "config/routes.rb"), File.join(dir, "config/routes.rb")
+ target.instance_variable_set("@destination_root", dir)
+ yield
+ end
+ rescue
+ if original_logger != Rails::Generator::Base.logger
+ Kernel::raise $!.class, "#{$!.message}\n#{@log}", $!.backtrace
+ else
+ Kernel::raise $!
+ end
+ ensure
+ Rails::Generator::Base.logger = original_logger
+ target.logger.quiet = original_quiet
+ target.instance_variable_set("@destination_root", original_root)
+ end
+
+ def match_content
+ # check for a match if #kind is 'output'
+ if @kind == 'output'
+ @but_found = @log.dup
+ if (text = @args.first).kind_of?(String)
+ if @log =~ /#{Regexp::escape text}/m
+ matched!
+ end
+ elsif (regexp = @args.first).kind_of?(Regexp)
+ if @log =~ regexp
+ matched!
+ end
+ end
+ end
+ end
+
+ def but_found
+ if @but_found.nil?
+ ""
+ else
+ ",\n but found #{@but_found.inspect}"
+ end
+ end
+
+ def with_file
+ !@args.empty? ? "\n with #{@args.inspect}" : ""
+ end
+
+ def method_missing(name, *args, &block)
+ if generation_methods.include? name.to_s
+ @create.send(name, *args, &block)
+ if name.to_s == @kind && (@args.empty? || args == @args)
+ matched!
+ elsif name.to_s == @kind
+ @but_found = args
+ else
+ nil
+ end
+ else super
+ end
+ #rescue
+ # Kernel::raise $!.class, $!.message, caller
+ end
+ end
+ end
+end
View
42 lib/genspec/generation_matchers/result_matcher.rb
@@ -0,0 +1,42 @@
+module GenSpec
+ module GenerationMatchers
+ class ResultMatcher < GenSpec::GenerationMatchers::GenerationMatcher
+ attr_reader :filename
+
+ def initialize(filename, &block)
+ @filename = filename
+ @block = block
+ super(:does_not_matter)
+ end
+
+ def after_replay(target)
+ path = File.join(target.destination_root, filename)
+ if File.exist?(path)
+ matched!
+ validate_block(target, path)
+ end
+ end
+
+ def failure_message
+ "Expected to generate file #{filename}"
+ end
+
+ def negative_failure_message
+ "Expected to not generate file #{filename}"
+ end
+
+ private
+ def validate_block(target, path)
+ if @block
+ if @block.arity == 2
+ @block.call(File.read(path), target)
+ else
+ @block.call(File.read(path))
+ end
+ else
+ true
+ end
+ end
+ end
+ end
+end
View
71 lib/genspec/generator_example_group.rb
@@ -0,0 +1,71 @@
+module GenSpec
+ class GeneratorExampleGroup
+ extend Spec::Example::ExampleGroupMethods
+ include Spec::Example::ExampleMethods
+ include GenSpec::GenerationMatchers
+ delegate :generator_arguments, :generator_options, :generator_args, :to => "self.class"
+
+ class << self
+ def generator_arguments
+ @generator_args.dup? || []
+ end
+
+ def generator_options
+ @generator_options.dup? || {}
+ end
+
+ def with_arguments(*args, &block)
+ if block_given?
+ context "with arguments #{args.inspect}" do
+ with_arguments(*args)
+ instance_eval(&block)
+ end
+ else
+ @generator_args = args.flatten.collect { |c| c.to_s }
+ end
+ end
+
+ def with_options(hash, &block)
+ if block_given?
+ context "with options #{hash.inspect}" do
+ with_options(hash)
+ instance_eval(&block)
+ end
+ else
+ @generator_options = hash
+ end
+ end
+
+ alias_method :generator_args, :generator_arguments
+ alias_method :with_args, :with_arguments
+ end
+
+ def subject(&block)
+ block.nil? ?
+ explicit_subject || implicit_subject : @explicit_subject_block = block
+ end
+
+ private
+ def explicit_subject
+ group = self
+ while group.respond_to?(:explicit_subject_block)
+ return group.explicit_subject_block if group.explicit_subject_block
+ group = group.superclass
+ end
+ end
+
+ def implicit_subject
+ target = example_group_hierarchy[1].described_class || example_group_hierarchy[1].description_args.first
+
+ if target.kind_of?(Symbol) || target.kind_of?(String)
+ target = self.class.lookup_missing_generator("#{target.to_s.underscore}_generator".camelize)
+ end
+ target.new(generator_args, generator_options)
+ rescue Rails::Generator::GeneratorError, Rails::Generator::UsageError
+ # let them pass, so the user can test the output.
+ # TODO: Make this disable-able via configuration.
+
+ $! # return this because it is the content we'll check against.
+ end
+ end
+end
View
18 spec/environment_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+# To make sure test environment is loading. Other successful tests will render
+# this one redundant, so it's safe to remove.
+#
+# For those who would flame me, this gem evolved out of a bit of test code that
+# was written for another project. Since the test code itself became a gem, I
+# didn't necessarily have tests for my tests.
+#
+# Since I'm starting with a code base before the test environment exists, this
+# file merely assures me that the test environment is loading.
+#
+describe "rails" do
+ it "should be defined" do
+ Rails
+ GenSpec
+ end
+end
View
96 spec/generators/test_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+describe :test do
+ context "with no options or arguments" do
+ it "should generate a file called default_file" do
+ subject.should generate("default_file")
+ subject.should_not generate("some_other_file")
+
+ subject.should generate(:file)
+ subject.should generate(:file, "file", "default_file")
+ end
+
+ it "should generate a file with specific content" do
+ subject.should generate("default_file") { |content| content.should == "content!" }
+ subject.should generate("default_file") { |content| content.should_not == "!content" }
+ subject.should_not generate("some_other_file")
+ end
+
+ it "should check for class collisions" do
+ subject.should generate(:class_collisions)
+ subject.should generate(:class_collisions, 'ActionController::Base')
+ subject.should_not generate(:class_collisions, 'ActionController')
+ subject.should generate(:class_collisions, 'SomethingValid')
+ end
+
+ it "should generate a template called 'default_template'" do
+ subject.should generate(:template)
+ subject.should generate(:template, 'file', 'file_template')
+ end
+
+ it "shoud generate a directory called 'a_directory'" do
+ subject.should generate(:directory)
+ subject.should generate(:directory, "a_directory")
+ subject.should generate("a_directory")
+ subject.should_not generate(:directory, 'another_directory')
+ end
+
+ it "should generate a migration template" do
+ subject.should generate(:migration_template, "file", "directory", :migration_file_name => 'migration')
+ # Uh, is there an easier, not-so-internally-dependent way to see if migration templates are generated?
+ end
+
+ it "should generate resource routes" do
+ subject.should generate(:route_resources, 'model')
+ end
+
+ it "should generate a readme" do
+ # we have to silence stdout because readme prints to stdout. Duh, right?
+ # That's also the reason for the :readme option. That way we can default it to 'disabled' for all other
+ # tests.
+ stdout = $stdout
+ $stdout = StringIO.new("")
+ self.class.with_options :readme => true
+ subject.should generate(:readme)
+ subject.should generate(:readme, "file")
+ $stdout = stdout
+ end
+ end
+
+ context "with options" do
+ with_options :help => true do
+ it "should generate 'Rails Info:'" do
+ subject.should output("Rails Info:")
+ end
+
+ it "should generate 'Rails Info:'" do
+ subject.should output(/rails info\:/i)
+ end
+ end
+ end
+
+ context "with arguments" do
+ with_args :test_arg
+
+ it "should generate a :file from template 'file' to file 'test_arg'" do
+ subject.should generate(:file, "file", "test_arg")
+ end
+
+ it "should generate file 'test_arg'" do
+ subject.should generate('test_arg')
+ end
+ end
+
+ # FIXME: Uh, how best to write a spec around this? I'm actually trying to test #with_args with a block...
+ with_args :test_arg do
+ it "should generate file 'test_arg'" do
+ subject.should generate('test_arg')
+ end
+ end
+
+ with_options :help => true do
+ it "should generate 'Rails Info:'" do
+ subject.should output("Rails Info:")
+ end
+ end
+end
View
4 spec/spec_helper.rb
@@ -0,0 +1,4 @@
+require File.expand_path("../../../../../config/environment", __FILE__)
+
+Rails::Generator::Base.append_sources Rails::Generator::PathSource.new(:test,
+ File.expand_path("../support/generators", __FILE__))
View
1 spec/support/generators/test/templates/file
@@ -0,0 +1 @@
+content!
View
29 spec/support/generators/test/test_generator.rb
@@ -0,0 +1,29 @@
+class TestGenerator < Rails::Generator::Base
+ def manifest
+ record do |m|
+ if args.empty?
+ m.file "file", "default_file"
+ else
+ m.file "file", args.first
+ end
+
+ m.template 'file', 'file_template'
+ m.directory 'a_directory'
+
+ m.class_collisions 'ActionController::Base'
+ m.class_collisions 'SomethingValid'
+
+ m.migration_template "file", "directory", :migration_file_name => 'migration'
+
+ m.route_resources 'model'
+
+ if options[:readme]
+ m.readme 'file'
+ end
+ end
+ end
+
+ def add_options!(opt)
+ opt.on('--readme') { |o| o[:readme] = true }
+ end
+end
View
10 test/helper.rb
@@ -1,10 +0,0 @@
-require 'rubygems'
-require 'test/unit'
-require 'shoulda'
-
-$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
-$LOAD_PATH.unshift(File.dirname(__FILE__))
-require 'genspec'
-
-class Test::Unit::TestCase
-end
View
7 test/test_genspec.rb
@@ -1,7 +0,0 @@
-require 'helper'
-
-class TestGenspec < Test::Unit::TestCase
- should "probably rename this file and start testing for real" do
- flunk "hey buddy, you should probably rename this file and start testing for real"
- end
-end

0 comments on commit 1b27959

Please sign in to comment.