genspec 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +122 -0
  3. data/Rakefile +55 -0
  4. data/VERSION +1 -0
  5. data/genspec.gemspec +83 -0
  6. data/lib/genspec.rb +14 -0
  7. data/lib/genspec/generation_matchers.rb +27 -0
  8. data/lib/genspec/generation_matchers/generation_matcher.rb +147 -0
  9. data/lib/genspec/generation_matchers/result_matcher.rb +42 -0
  10. data/lib/genspec/generator_example_group.rb +71 -0
  11. data/pkg/genspec-0.0.0.gem +0 -0
  12. data/pkg/genspec-0.1.0.gem +0 -0
  13. data/rdoc/classes/GenSpec.html +124 -0
  14. data/rdoc/classes/GenSpec/GenerationMatchers.html +197 -0
  15. data/rdoc/classes/GenSpec/GenerationMatchers/GenerationMatcher.html +363 -0
  16. data/rdoc/classes/GenSpec/GenerationMatchers/ResultMatcher.html +241 -0
  17. data/rdoc/classes/GenSpec/GeneratorExampleGroup.html +285 -0
  18. data/rdoc/created.rid +1 -0
  19. data/rdoc/files/README_rdoc.html +261 -0
  20. data/rdoc/files/lib/genspec/generation_matchers/generation_matcher_rb.html +101 -0
  21. data/rdoc/files/lib/genspec/generation_matchers/result_matcher_rb.html +101 -0
  22. data/rdoc/files/lib/genspec/generation_matchers_rb.html +109 -0
  23. data/rdoc/files/lib/genspec/generator_example_group_rb.html +101 -0
  24. data/rdoc/files/lib/genspec_rb.html +114 -0
  25. data/rdoc/fr_class_index.html +31 -0
  26. data/rdoc/fr_file_index.html +32 -0
  27. data/rdoc/fr_method_index.html +46 -0
  28. data/rdoc/index.html +26 -0
  29. data/rdoc/rdoc-style.css +208 -0
  30. data/spec/environment_spec.rb +18 -0
  31. data/spec/generators/test_spec.rb +96 -0
  32. data/spec/spec_helper.rb +4 -0
  33. data/spec/support/generators/test/templates/file +1 -0
  34. data/spec/support/generators/test/test_generator.rb +29 -0
  35. metadata +124 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Colin MacKenzie IV
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.rdoc ADDED
@@ -0,0 +1,122 @@
1
+ = genspec
2
+
3
+ Simple, expressive Rails generator testing for RSpec.
4
+
5
+ == Installation
6
+
7
+ sudo gem install genspec
8
+
9
+ ...then, in your config/environments/test.rb...
10
+
11
+ config.gem 'genspec'
12
+
13
+ == Usage
14
+
15
+ Just like rspec-rails uses the structure of your spec/ directory to infer which test is being run (controllers,
16
+ helpers, lib, etc.), you just need to create a spec/generators directory and put your generator specs in there.
17
+ A basic generator spec might look something like this:
18
+
19
+ # in spec/generators/custom_controller_spec.rb
20
+ require 'spec_helper'
21
+
22
+ describe :custom_controller do
23
+ context "with no arguments or options" do
24
+ it "should generate a help message" do
25
+ subject.should output("A Help Message")
26
+ end
27
+ end
28
+
29
+ context "with a name argument" do
30
+ with_args :users
31
+
32
+ it "should generate a UsersController" do
33
+ subject.should generate("app/controllers/users_controller.rb")
34
+ end
35
+ end
36
+ end
37
+
38
+ === Checking for Output
39
+
40
+ If you need to test the generator's feedback rather than the generator's results, you can use the _output_ matcher to
41
+ assert that your generator has produced some specific content in its output (which would be either a logger of some
42
+ sort or $stdout). This is helpful for making sure your help message is accurate, for instance.
43
+
44
+ # Ex 1: String
45
+ it "should generate a help message" do
46
+ subject.should output("A Help Message")
47
+ end
48
+
49
+ # Ex 2: Regular Expression
50
+ it "should generate a help message" do
51
+ subject.should output(/A [hH]elp Message/)
52
+ end
53
+
54
+
55
+ === Checking Generated Files
56
+
57
+ This is the preferred way to test which files were actually generated, because this matcher checks your
58
+ generator's *behavior*. That means it won't care _how_ a file is generated, as long as it _is_ generated. It's as simple
59
+ as passing the name of the file you expected to be generated:
60
+
61
+ it "should generate a readme file" do
62
+ subject.should generate("README")
63
+ end
64
+
65
+ You can also check the generated file's content by simply passing a block. The argument in the block is the plaintext
66
+ content of the file:
67
+
68
+ it "should generate a model called 'user'" do
69
+ subject.should generate("app/models/user.rb") { |content|
70
+ content.should =~ /class User < ActiveRecord\:\:Base/
71
+ }
72
+ end
73
+
74
+ === Checking Generation Methods
75
+
76
+ This is the most intrusive form of generation matching, and should be avoided where possible. However, in some cases
77
+ you just can't check behavior directly (migrations are a case in point, because the version number is unpredictable).
78
+ In that case, you can verify that a particular method is called within the generator's manifest like so:
79
+
80
+ it "should generate a user migration template" do
81
+ subject.should generate(:migration_template, "migration_template.erb", "db/migrate", :migration_file => "create_users")
82
+ end
83
+
84
+ You can stop passing arguments at any time. This has the effect of widening the range of acceptable parameters. For
85
+ instance, the following example does the same thing but will accept _any_ migration file name in _any_ destination
86
+ directory, as long as the "migration_template.erb" file is used for the source:
87
+
88
+ it "should generate a migration template" do
89
+ subject.should generate(:migration_template, "migration_template.rb")
90
+ end
91
+
92
+ Any method that is normally available to Rails generators can be tested in this way:
93
+
94
+ it "should test one of each generation type" do
95
+ subject.should generate(:directory, "db/migrate")
96
+ subject.should generate(:file, "input_file", "output_file")
97
+ subject.should generate(:template, "input_template", "output_template")
98
+ subject.should generate(:class_collisions, 'ActionController::Base')
99
+ subject.should generate(:migration_template, "file", "directory", :migration_file_name => "filename")
100
+ subject.should generate(:resource_routes, 'model_name')
101
+ subject.should generate(:readme, "README")
102
+ end
103
+
104
+ But again... Use these as last resorts, for the most part. Technically, you will probably need this for
105
+ :migration_template and :resource_routes, but anything else should really be tested for _behavior_. Like any other
106
+ class, you should really only care about how the generator interacts with other objects -- in this case, your file
107
+ system -- and not so much about what it's doing on the inside.
108
+
109
+
110
+ == Note on Patches/Pull Requests
111
+
112
+ * Fork the project.
113
+ * Make your feature addition or bug fix.
114
+ * Add tests for it. This is important so I don't break it in a
115
+ future version unintentionally.
116
+ * Commit, do not mess with rakefile, version, or history.
117
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
118
+ * Send me a pull request. Bonus points for topic branches.
119
+
120
+ == Copyright
121
+
122
+ Copyright (c) 2010 Colin MacKenzie IV. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "genspec"
8
+ gem.summary = "Simple, expressive Rails generator testing for RSpec."
9
+ gem.description = "Just like rspec-rails uses the structure of your spec/ directory to infer which test is being run (controllers, helpers, lib, etc.), you just need to create a spec/generators directory and put your generator specs in there."
10
+ gem.email = "sinisterchipmunk@gmail.com"
11
+ gem.homepage = "http://www.thoughtsincomputation.com"
12
+ gem.authors = ["Colin MacKenzie IV"]
13
+ gem.files = FileList['**/*']
14
+ gem.add_dependency "rspec"
15
+ gem.add_dependency "sc-core-ext", ">= 1.2.0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "genspec #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
data/genspec.gemspec ADDED
@@ -0,0 +1,83 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{genspec}
8
+ s.version = "0.1.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Colin MacKenzie IV"]
12
+ s.date = %q{2010-06-21}
13
+ s.description = %q{Just like rspec-rails uses the structure of your spec/ directory to infer which test is being run (controllers, helpers, lib, etc.), you just need to create a spec/generators directory and put your generator specs in there.}
14
+ s.email = %q{sinisterchipmunk@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "genspec.gemspec",
25
+ "lib/genspec.rb",
26
+ "lib/genspec/generation_matchers.rb",
27
+ "lib/genspec/generation_matchers/generation_matcher.rb",
28
+ "lib/genspec/generation_matchers/result_matcher.rb",
29
+ "lib/genspec/generator_example_group.rb",
30
+ "pkg/genspec-0.0.0.gem",
31
+ "pkg/genspec-0.1.0.gem",
32
+ "rdoc/classes/GenSpec.html",
33
+ "rdoc/classes/GenSpec/GenerationMatchers.html",
34
+ "rdoc/classes/GenSpec/GenerationMatchers/GenerationMatcher.html",
35
+ "rdoc/classes/GenSpec/GenerationMatchers/ResultMatcher.html",
36
+ "rdoc/classes/GenSpec/GeneratorExampleGroup.html",
37
+ "rdoc/created.rid",
38
+ "rdoc/files/README_rdoc.html",
39
+ "rdoc/files/lib/genspec/generation_matchers/generation_matcher_rb.html",
40
+ "rdoc/files/lib/genspec/generation_matchers/result_matcher_rb.html",
41
+ "rdoc/files/lib/genspec/generation_matchers_rb.html",
42
+ "rdoc/files/lib/genspec/generator_example_group_rb.html",
43
+ "rdoc/files/lib/genspec_rb.html",
44
+ "rdoc/fr_class_index.html",
45
+ "rdoc/fr_file_index.html",
46
+ "rdoc/fr_method_index.html",
47
+ "rdoc/index.html",
48
+ "rdoc/rdoc-style.css",
49
+ "spec/environment_spec.rb",
50
+ "spec/generators/test_spec.rb",
51
+ "spec/spec_helper.rb",
52
+ "spec/support/generators/test/templates/file",
53
+ "spec/support/generators/test/test_generator.rb"
54
+ ]
55
+ s.homepage = %q{http://www.thoughtsincomputation.com}
56
+ s.rdoc_options = ["--charset=UTF-8"]
57
+ s.require_paths = ["lib"]
58
+ s.rubygems_version = %q{1.3.6}
59
+ s.summary = %q{Simple, expressive Rails generator testing for RSpec.}
60
+ s.test_files = [
61
+ "spec/environment_spec.rb",
62
+ "spec/generators/test_spec.rb",
63
+ "spec/spec_helper.rb",
64
+ "spec/support/generators/test/test_generator.rb"
65
+ ]
66
+
67
+ if s.respond_to? :specification_version then
68
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
69
+ s.specification_version = 3
70
+
71
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
72
+ s.add_runtime_dependency(%q<rspec>, [">= 0"])
73
+ s.add_runtime_dependency(%q<sc-core-ext>, [">= 1.2.0"])
74
+ else
75
+ s.add_dependency(%q<rspec>, [">= 0"])
76
+ s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
77
+ end
78
+ else
79
+ s.add_dependency(%q<rspec>, [">= 0"])
80
+ s.add_dependency(%q<sc-core-ext>, [">= 1.2.0"])
81
+ end
82
+ end
83
+
data/lib/genspec.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'spec'
2
+ require 'fileutils'
3
+
4
+ require 'sc-core-ext'
5
+
6
+ require 'genspec/generation_matchers'
7
+ require 'genspec/generator_example_group'
8
+
9
+ if defined?(RAILS_ROOT)
10
+ require 'rails_generator'
11
+ require 'rails_generator/scripts/generate'
12
+ end
13
+
14
+ Spec::Example::ExampleGroupFactory.register(:generator, GenSpec::GeneratorExampleGroup)
@@ -0,0 +1,27 @@
1
+ require 'genspec/generation_matchers/generation_matcher'
2
+ require 'genspec/generation_matchers/result_matcher'
3
+
4
+ module GenSpec
5
+ module GenerationMatchers
6
+ # Valid types: :dependency, :class_collisions, :file, :template, :complex_template, :directory, :readme,
7
+ # :migration_template, :route_resources
8
+ def generate(kind, *args, &block)
9
+ case kind.to_s
10
+ when *GenSpec::GenerationMatchers::GenerationMatcher.generation_methods
11
+ GenSpec::GenerationMatchers::GenerationMatcher.new(kind, *args, &block)
12
+ else
13
+ if kind.kind_of?(String)
14
+ GenSpec::GenerationMatchers::ResultMatcher.new(kind, &block)
15
+ else
16
+ raise ArgumentError, "No generator matcher for #{kind.inspect}"
17
+ end
18
+ end
19
+ end
20
+
21
+ # This tests the content sent to the command line, instead of the generated product.
22
+ # Useful for testing help messages, etc.
23
+ def output(text_or_regexp)
24
+ GenSpec::GenerationMatchers::GenerationMatcher.new(:output, text_or_regexp)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,147 @@
1
+ module GenSpec
2
+ module GenerationMatchers
3
+ class GenerationMatcher
4
+ delegate :generation_methods, :to => 'self.class'
5
+
6
+ def initialize(kind = nil, *args)
7
+ raise ArgumentError, "Call with kind" unless kind
8
+ @kind = kind.to_s
9
+ @args = args
10
+ @but_found = nil
11
+ end
12
+
13
+ def self.generation_methods
14
+ @generation_methods ||= %w(dependency class_collisions file template complex_template directory readme
15
+ migration_template route_resources)
16
+ end
17
+
18
+ def matches?(target)
19
+ # if it's a string then it's an error message or some such from the generator.
20
+ if target.kind_of?(Rails::Generator::GeneratorError)
21
+ # if we're not checking output then why hold on to it? Raise it like the error it is!
22
+ raise target unless @kind == 'output'
23
+ @log = target.message
24
+ else
25
+ temporary_root(target) do
26
+ swap_loggers(target) do
27
+ replay(target)
28
+ end
29
+ end
30
+ end
31
+ match_content
32
+ matched?
33
+ end
34
+
35
+ def failure_message
36
+ "Expected to generate #{@kind}#{with_file}#{but_found}"
37
+ end
38
+
39
+ def negative_failure_message
40
+ "Expected not to generate #{@kind}#{with_file}"
41
+ end
42
+
43
+ protected
44
+
45
+ def replay(target)
46
+ @create = Rails::Generator::Commands::Create.new(target)
47
+ target.manifest.replay(self)
48
+ after_replay(target)
49
+ end
50
+
51
+ # hook
52
+ def after_replay(target)
53
+ end
54
+
55
+ def matched!
56
+ @matched = true
57
+ end
58
+
59
+ def matched?
60
+ !!@matched
61
+ end
62
+
63
+ private
64
+ def temporary_root(target)
65
+ # We could bear to split this into two methods, one called #suspend_logging or some such.
66
+ original_root = target.instance_variable_get("@destination_root")
67
+
68
+ Dir.mktmpdir do |dir|
69
+ # need to copy a few files for some methods, ie route_resources
70
+ Dir.mkdir(File.join(dir, "config"))
71
+ FileUtils.cp File.join(RAILS_ROOT, "config/routes.rb"), File.join(dir, "config/routes.rb")
72
+ target.instance_variable_set("@destination_root", dir)
73
+ yield
74
+ end
75
+ ensure
76
+ target.instance_variable_set("@destination_root", original_root)
77
+ end
78
+
79
+ def swap_loggers(target)
80
+ @log = ""
81
+ original_logger = Rails::Generator::Base.logger
82
+ original_quiet = target.logger.quiet
83
+ target.logger.quiet = true
84
+ Rails::Generator::Base.logger = Rails::Generator::SimpleLogger.new(StringIO.new(@log))
85
+
86
+ yield
87
+ rescue
88
+ if original_logger != Rails::Generator::Base.logger
89
+ Kernel::raise $!.class, "#{$!.message}\n#{@log}", $!.backtrace
90
+ else
91
+ Kernel::raise $!
92
+ end
93
+ ensure
94
+ Rails::Generator::Base.logger = original_logger
95
+ target.logger.quiet = original_quiet
96
+ line = "Generator '#{target.spec.name}'"
97
+ line << " with args #{target.args.inspect}" unless target.args.empty?
98
+ Rails.logger.debug line
99
+ Rails.logger.debug @log
100
+ end
101
+
102
+ def match_content
103
+ # check for a match if #kind is 'output'
104
+ if @kind == 'output'
105
+ @but_found = @log.dup
106
+ if (text = @args.first).kind_of?(String)
107
+ if @log =~ /#{Regexp::escape text}/m
108
+ matched!
109
+ end
110
+ elsif (regexp = @args.first).kind_of?(Regexp)
111
+ if @log =~ regexp
112
+ matched!
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ def but_found
119
+ if @but_found.nil?
120
+ ""
121
+ else
122
+ ",\n but found #{@but_found.inspect}"
123
+ end
124
+ end
125
+
126
+ def with_file
127
+ !@args.empty? ? "\n with #{@args.inspect}" : ""
128
+ end
129
+
130
+ def method_missing(name, *args, &block)
131
+ if generation_methods.include? name.to_s
132
+ @create.send(name, *args, &block)
133
+ if name.to_s == @kind && (@args.empty? || args == @args)
134
+ matched!
135
+ elsif name.to_s == @kind
136
+ @but_found = args
137
+ else
138
+ nil
139
+ end
140
+ else super
141
+ end
142
+ #rescue
143
+ # Kernel::raise $!.class, $!.message, caller
144
+ end
145
+ end
146
+ end
147
+ end