modelfactory 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ == 0.7.0 2008-11-11
2
+
3
+ * Documentation and rubygem
4
+
5
+ == 0.0.1 2008-01-25
6
+
7
+ * Initial release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Zack Hobson and Justin Balthrop, Geni.com
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.
@@ -0,0 +1,15 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ lib/fixture_converter.rb
7
+ lib/model_factory.rb
8
+ lib/model_factory/version.rb
9
+ script/destroy
10
+ script/fixtures2factories
11
+ script/generate
12
+ script/txt2html
13
+ setup.rb
14
+ test/model_factory_test.rb
15
+ test/test_helper.rb
@@ -0,0 +1,91 @@
1
+ = ModelFactory
2
+
3
+ ModelFactory is a module designed to replace the use of fixtures for testing
4
+ Rails applications.
5
+
6
+ The idea is that instead of keeping your test data in a nearly opaque fixture
7
+ file, you generate data in the test itself using a custom factory API designed
8
+ for your test environment.
9
+
10
+ By creating a new module just for your test factory API you make it easier
11
+ to spot factory calls in your tests and keep your factory code out of your
12
+ test code. ModelFactory adds some useful facilities for generating optional
13
+ defaults for commonly instantiated types. It also fakes up id generation in
14
+ the ActiveRecord models created with new, to assist in unit testing without
15
+ the database.
16
+
17
+ === A Note About Defaults
18
+
19
+ When writing tests that use factory-generated objects, it's important never
20
+ to depend on default values in your test assertions. If you depend on defaults
21
+ in your tests they become more fragile and the intention is harder to discern.
22
+
23
+ If you find yourself repeating the same initialization to avoid using defaults,
24
+ consider whether it would be appropriate to add a custom toplevel method to
25
+ your factory module that includes this initialization.
26
+
27
+ === A Note About ID Generation
28
+
29
+ Since basic ID generation is done when you instantiate objects using
30
+ Factory.new_<type> it is recommended not to mix such objects with those
31
+ created using Factory.create_<type>. Use the former in unit tests and
32
+ use the latter in functional tests.
33
+
34
+ == Using ModelFactory
35
+
36
+ Put something like this in your test helper:
37
+
38
+ require 'model_factory'
39
+
40
+ module Factory
41
+ extend ModelFactory
42
+
43
+ # a default block accepts a class and a hash of default values
44
+ default Color, {
45
+ :name => 'chartreuse'
46
+ }
47
+
48
+ default User, {
49
+ :first_name => 'Harry',
50
+ :last_name => 'Manchester',
51
+ :favorite_color => default_color
52
+ }
53
+
54
+ # Add class methods to create whatever kind of objects you need for your tests
55
+ def self.new_user_with_colorblindness
56
+ new_user { :favorite_color => nil }
57
+ end
58
+
59
+ end
60
+
61
+ Then in your tests you use Factory methods to instantiate your test objects:
62
+
63
+ # For most functional tests you can use create.
64
+ def test_something
65
+ user = Factory.create_user
66
+ user.friends << Factory.create_user(:first_name => 'Frank')
67
+ assert user.likes_frank?
68
+ end
69
+
70
+ # For unit tests you use new.
71
+ def test_something_else
72
+ user = Factory.new_user(:favorite_color => Factory.new_color(:name => 'blue'))
73
+ assert user.likes_blue?
74
+ end
75
+
76
+ # Assertions should not depend on default data, but it can be useful to create
77
+ # factory methods that build objects with specific traits.
78
+ def test_yet_something_else
79
+ user = Factory.new_user_with_colorblindness
80
+ assert !user.likes_blue?
81
+ end
82
+
83
+ == Installing ModelFactory
84
+
85
+ sudo gem install modelfactory
86
+
87
+ == License
88
+
89
+ Copyright (c) 2008 Justin Balthrop and Zack Hobson
90
+ Published under The MIT License, see License.txt
91
+
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ $:.unshift(File.dirname(__FILE__) + "/lib")
4
+ require 'model_factory/version'
5
+
6
+ Hoe.new('ModelFactory', ModelFactory::VERSION::STRING) do |p|
7
+ p.name = "modelfactory"
8
+ p.author = ['Justin Balthrop', 'Zack Hobson']
9
+ p.description = "A replacement for fixtures."
10
+ p.email = "justin@geni.com"
11
+ p.summary = "A replacement for fixtures."
12
+ p.url = "http://modelfactory.rubyforge.org/"
13
+ p.rubyforge_name = 'modelfactory'
14
+ p.remote_rdoc_dir = '' # Release to root
15
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
16
+ p.test_globs = ["test/**/*_test.rb"]
17
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store']
18
+ end
19
+
20
+
@@ -0,0 +1,97 @@
1
+ class FixtureConverter
2
+ def initialize(opts = {})
3
+ @body = []
4
+ @header = []
5
+ @indent_depth = opts[:indent_depth] || 0
6
+ @output_style = opts[:output_style]
7
+ end
8
+
9
+ def powder?
10
+ @output_style == :clay
11
+ end
12
+
13
+ def convert_fixture(path)
14
+ fixture = YAML.load_file(path)
15
+ return if not fixture
16
+
17
+ plural_model_name = path.basename.to_s.split('.').first
18
+ model_name = plural_model_name.singularize
19
+ header do
20
+ "#{plural_model_name} = {}"
21
+ end
22
+
23
+ body 'before(:all) do' if powder?
24
+ indent(powder?) do
25
+ body "# Setup #{plural_model_name}"
26
+ body "# Generated from fixture in #{path.dirname.basename}/#{path.basename}"
27
+ body '#'
28
+ body ''
29
+ fixture.each do |name, record|
30
+ max_length = record.keys.collect {|key| key.length}.max
31
+
32
+ body "#{plural_model_name}[:#{name}] = Factory.create_#{model_name}("
33
+ indent do
34
+ body do
35
+ record.collect do |key,value|
36
+ value = "\"#{value}\"" if value.kind_of?(String)
37
+ key = key.ljust(max_length)
38
+ ":#{key} => #{value}"
39
+ end
40
+ end
41
+ end
42
+ body ')'
43
+ end
44
+ end
45
+ body 'end' if powder?
46
+ body ''
47
+ end
48
+
49
+ def convert_scenario(path)
50
+ path.each_entry do |file|
51
+ next if file.extname != '.yml'
52
+ next if file.basename.to_s =~ /relationships/
53
+ convert_fixture( path + file )
54
+ end
55
+ end
56
+
57
+ def out
58
+ puts @header.join("\n")
59
+ puts "\n"
60
+ puts @body.join("\n")
61
+ end
62
+
63
+ private
64
+ INDENT = ' '
65
+
66
+ def indent(enabled = true)
67
+ @indent_depth += 1 if enabled
68
+ yield
69
+ @indent_depth -= 1 if enabled
70
+ end
71
+
72
+ def body(*lines)
73
+ lines = yield if block_given?
74
+ lines = [lines].flatten
75
+
76
+ lines.each do |line|
77
+ @body << indent_line(line, @indent_depth)
78
+ end
79
+ end
80
+
81
+ def header(*lines)
82
+ lines = yield if block_given?
83
+ lines = [lines].flatten
84
+
85
+ lines.each do |line|
86
+ @header << line
87
+ end
88
+ end
89
+
90
+ def indent_line(line, indent_depth)
91
+ ws = ''
92
+ indent_depth.times do
93
+ ws += INDENT
94
+ end
95
+ ws + line
96
+ end
97
+ end
@@ -0,0 +1,111 @@
1
+ require 'active_record'
2
+
3
+ module ModelFactory
4
+ def next_local_id # :nodoc:
5
+ @max_id ||= 0
6
+ return @max_id += 1
7
+ end
8
+
9
+ #
10
+ # When specifying defaults, you should only provide only enough data that
11
+ # the created instance is valid. If you want to include another factory
12
+ # object as a dependency use the special method default_* instead of
13
+ # create_* or new_*.
14
+ #
15
+ def default(class_type, defaults={})
16
+ class_name = class_type.name.demodulize.underscore
17
+
18
+ (class << self; self; end).module_eval do
19
+ define_method "create_#{class_name}" do |*args|
20
+ attributes = args.first || {}
21
+ create_instance(class_type, attributes, defaults)
22
+ end
23
+
24
+ define_method "new_#{class_name}" do |*args|
25
+ attributes = args.first || {}
26
+ new_instance(class_type, attributes, defaults)
27
+ end
28
+
29
+ define_method "default_#{class_name}" do |*args|
30
+ attributes = args.first || {}
31
+ default_closure(class_type, attributes, defaults)
32
+ end
33
+ end
34
+ end
35
+
36
+ def create_instance(class_type, attributes, defaults = {}) # :nodoc:
37
+ attributes = instantiate_defaults(:create, defaults.merge(attributes))
38
+ instance = class_type.create!(attributes)
39
+ if update_protected_attributes(instance, attributes)
40
+ instance.save
41
+ end
42
+ instance
43
+ end
44
+
45
+ def new_instance(class_type, attributes, defaults = {}) # :nodoc:
46
+ attributes = instantiate_defaults(:new, defaults.merge(attributes))
47
+ instance = class_type.new(attributes)
48
+ instance.id = next_local_id
49
+ update_protected_attributes(instance, attributes)
50
+ instance
51
+ end
52
+
53
+ def default_closure(class_type, attributes, defaults = {}) # :nodoc:
54
+ lambda do |create_or_new|
55
+ case create_or_new
56
+ when :new : new_instance(class_type, attributes, defaults)
57
+ when :create : create_instance(class_type, attributes, defaults)
58
+ end
59
+ end
60
+ end
61
+
62
+ def instantiate_defaults(create_or_new, attributes) # :nodoc:
63
+ attributes.each do |key, value|
64
+ if value.is_a?(Proc)
65
+ attributes[key] = value.arity == 0 ? value.call : value.call(create_or_new)
66
+ end
67
+ end
68
+ attributes
69
+ end
70
+
71
+ def update_protected_attributes(instance, attributes) # :nodoc:
72
+ modified = false
73
+ protected_attrs = instance.class.protected_attributes
74
+ protected_attrs = protected_attrs.to_set if protected_attrs
75
+ accessible_attrs = instance.class.accessible_attributes
76
+ accessible_attrs = accessible_attrs.to_set if accessible_attrs
77
+
78
+ if protected_attrs or accessible_attrs
79
+ attributes.each do |key, value|
80
+ # Support symbols and strings.
81
+ [key, key.to_s].each do |attr|
82
+ next if protected_attrs and not protected_attrs.include?(attr)
83
+ next if accessible_attrs and accessible_attrs.include?(attr)
84
+ end
85
+ modified = true
86
+ instance.send("#{key}=", value)
87
+ end
88
+ end
89
+ return modified
90
+ end
91
+
92
+ # Any class methods of the form "new_some_type(attrs)" or "create_some_type(attrs)" will be converted to
93
+ # "SomeType.new(attrs)" and "SomeType.create!(attrs)" respectively.
94
+ # These basically function as though you'd used the 'default' directive with empty defaults.
95
+ def method_missing(missing_method, attributes = {})
96
+ if missing_method.to_s.match(/^(new|create|default)_([a-z][\w_]+)$/)
97
+ method, class_name = $1, $2
98
+ class_type = class_name.camelize.constantize
99
+ case method
100
+ when 'create'
101
+ create_instance(class_type, attributes)
102
+ when 'new'
103
+ new_instance(class_type, attributes)
104
+ when 'default'
105
+ default_closure(class_type, attributes)
106
+ end
107
+ else
108
+ raise NoMethodError, "no such method '#{missing_method}'"
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,9 @@
1
+ module ModelFactory #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 7
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/ruby
2
+ require 'rubygems'
3
+ require 'active_support'
4
+ require 'pp'
5
+ require 'pathname'
6
+ require 'yaml'
7
+ require File.dirname(__FILE__) + '/../lib/fixture_converter'
8
+
9
+ path = Pathname.new(ARGV[0])
10
+
11
+ fc = FixtureConverter.new
12
+ if path.file?
13
+ fc.convert_fixture(path)
14
+ else
15
+ fc.convert_scenario(path)
16
+ end
17
+ fc.out
18
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.join(File.dirname(__FILE__), '..')
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ begin
5
+ require 'newgem'
6
+ rescue LoadError
7
+ puts "\n\nGenerating the website requires the newgem RubyGem"
8
+ puts "Install: gem install newgem\n\n"
9
+ exit(1)
10
+ end
11
+ require 'redcloth'
12
+ require 'syntax/convertors/html'
13
+ require 'erb'
14
+ require File.dirname(__FILE__) + '/../lib/model_factory/version.rb'
15
+
16
+ version = ModelFactory::VERSION::STRING
17
+ download = 'http://rubyforge.org/projects/model_factory'
18
+
19
+ class Fixnum
20
+ def ordinal
21
+ # teens
22
+ return 'th' if (10..19).include?(self % 100)
23
+ # others
24
+ case self % 10
25
+ when 1: return 'st'
26
+ when 2: return 'nd'
27
+ when 3: return 'rd'
28
+ else return 'th'
29
+ end
30
+ end
31
+ end
32
+
33
+ class Time
34
+ def pretty
35
+ return "#{mday}#{mday.ordinal} #{strftime('%B')} #{year}"
36
+ end
37
+ end
38
+
39
+ def convert_syntax(syntax, source)
40
+ return Syntax::Convertors::HTML.for_syntax(syntax).convert(source).gsub(%r!^<pre>|</pre>$!,'')
41
+ end
42
+
43
+ if ARGV.length >= 1
44
+ src, template = ARGV
45
+ template ||= File.join(File.dirname(__FILE__), '/../website/template.rhtml')
46
+
47
+ else
48
+ puts("Usage: #{File.split($0).last} source.txt [template.rhtml] > output.html")
49
+ exit!
50
+ end
51
+
52
+ template = ERB.new(File.open(template).read)
53
+
54
+ title = nil
55
+ body = nil
56
+ File.open(src) do |fsrc|
57
+ title_text = fsrc.readline
58
+ body_text = fsrc.read
59
+ syntax_items = []
60
+ body_text.gsub!(%r!<(pre|code)[^>]*?syntax=['"]([^'"]+)[^>]*>(.*?)</\1>!m){
61
+ ident = syntax_items.length
62
+ element, syntax, source = $1, $2, $3
63
+ syntax_items << "<#{element} class='syntax'>#{convert_syntax(syntax, source)}</#{element}>"
64
+ "syntax-temp-#{ident}"
65
+ }
66
+ title = RedCloth.new(title_text).to_html.gsub(%r!<.*?>!,'').strip
67
+ body = RedCloth.new(body_text).to_html
68
+ body.gsub!(%r!(?:<pre><code>)?syntax-temp-(\d+)(?:</code></pre>)?!){ syntax_items[$1.to_i] }
69
+ end
70
+ stat = File.stat(src)
71
+ created = stat.ctime
72
+ modified = stat.mtime
73
+
74
+ $stdout << template.result(binding)