genspec 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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