modelfactory 0.7.0

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.
@@ -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)