mleung-koujou 0.0.2

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.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2009-07-14
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,29 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ koujou.gemspec
7
+ lib/koujou.rb
8
+ lib/koujou/builder.rb
9
+ lib/koujou/custom_validation.rb
10
+ lib/koujou/data_generator.rb
11
+ lib/koujou/sequence.rb
12
+ lib/koujou/validation_reflection.rb
13
+ script/console
14
+ script/destroy
15
+ script/generate
16
+ test/lib/active_record_test_connector.rb
17
+ test/lib/models/car.rb
18
+ test/lib/models/comment.rb
19
+ test/lib/models/message.rb
20
+ test/lib/models/photo.rb
21
+ test/lib/models/post.rb
22
+ test/lib/models/profile.rb
23
+ test/lib/models/user.rb
24
+ test/test_builder.rb
25
+ test/test_custom_validation.rb
26
+ test/test_data_generator.rb
27
+ test/test_helper.rb
28
+ test/test_kojo.rb
29
+ test/test_sequence.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on koujou, see http://koujou.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,43 @@
1
+ = koujou
2
+
3
+ * http://github.com/mleung/koujou
4
+
5
+ == DESCRIPTION:
6
+
7
+ Koujou is a fixture replacement that requires no effort to use. You don't have to define a single thing
8
+ in your test_helper or whatever. Just call the koujou method on your active record model, and you're
9
+ off.
10
+
11
+ Check out: http://www.michaelleung.us/koujou for all the juicy details.
12
+
13
+ Please note this is a very early release of koujou, so if you find any issues, just post
14
+ them here: http://github.com/mleung/koujou/issues
15
+
16
+ == INSTALL:
17
+
18
+ gem install mleung-koujou --source http://gems.github.com
19
+
20
+ == LICENSE:
21
+
22
+ (The MIT License)
23
+
24
+ Copyright (c) 2009 Michael Leung
25
+
26
+ Permission is hereby granted, free of charge, to any person obtaining
27
+ a copy of this software and associated documentation files (the
28
+ 'Software'), to deal in the Software without restriction, including
29
+ without limitation the rights to use, copy, modify, merge, publish,
30
+ distribute, sublicense, and/or sell copies of the Software, and to
31
+ permit persons to whom the Software is furnished to do so, subject to
32
+ the following conditions:
33
+
34
+ The above copyright notice and this permission notice shall be
35
+ included in all copies or substantial portions of the Software.
36
+
37
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
38
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
40
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
41
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
42
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
43
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/koujou'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'koujou' do
14
+ self.developer 'Michael Leung', 'me@michaelleung.us'
15
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ # self.extra_deps = [['activesupport','>= 2.0.2']]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
data/lib/koujou.rb ADDED
@@ -0,0 +1,22 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ require 'faker'
7
+ require 'koujou/sequence'
8
+ require 'koujou/builder'
9
+ require 'koujou/data_generator'
10
+ require 'koujou/validation_reflection'
11
+ require 'koujou/custom_validation'
12
+
13
+ module Koujou
14
+ VERSION = '0.0.1'
15
+ end
16
+
17
+ ActiveRecord::Base.class_eval do
18
+ include Koujou::ActiveRecordExtensions::ValidationReflection
19
+ Koujou::ActiveRecordExtensions::ValidationReflection.install(self)
20
+ include Koujou::ActiveRecordExtensions::Builder
21
+ end
22
+
@@ -0,0 +1,209 @@
1
+ module Koujou #:nodoc:
2
+ module ActiveRecordExtensions # :nodoc:
3
+ module Builder # :nodoc:
4
+
5
+ def self.included(base) # :nodoc:
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods #:nodoc:
10
+
11
+ def koujou(create = true, attributes = nil)
12
+ generate_instance(create, attributes)
13
+ end
14
+
15
+ def koujou_build(attributes = nil)
16
+ koujou(false, attributes)
17
+ end
18
+
19
+ def koujou_create(attributes = nil)
20
+ koujou(true, attributes)
21
+ end
22
+
23
+ protected
24
+ def generate_instance(create, attributes)
25
+ # There were a metric ton of AR warnings about instance vars
26
+ # not being initialized when running our specs. This hides those.
27
+ silence_warnings do
28
+ instance = build_model_instance(self, attributes)
29
+ instance.save! if create
30
+ instance
31
+ end
32
+ end
33
+
34
+ def build_model_instance(klass, attributes = nil, recursed_from_model = nil)
35
+ # If we pass in a string here for klass instead of a constant
36
+ # we want to convert that.
37
+ klass = Kernel.const_get(klass) unless klass.respond_to?(:new)
38
+ instance = klass.new
39
+ # Set the models attributes if the user passed them in.
40
+ # This will allow attributes to be set regardless if
41
+ # they're required or not.
42
+ instance.attributes = attributes unless attributes.nil?
43
+
44
+ set_required_attributes!(instance, attributes)
45
+ set_unique_attributes!(instance, attributes)
46
+ set_confirmation_attributes!(instance, attributes)
47
+ set_length_validated_attributes!(instance, attributes)
48
+ set_inclusion_validated_attributes!(instance, attributes)
49
+ create_associations(instance, recursed_from_model)
50
+ CustomValidation.stub_custom_validations!(instance)
51
+
52
+ instance
53
+ end
54
+
55
+ def set_required_attributes!(instance, attributes)
56
+ instance.class.required_validations.each do |v|
57
+ # We want to skip over setting any required fields if the field
58
+ # should also be unique. We handle that in the set_unique_attributes!
59
+ # method with a sequence. Also, if it's a confirmation field (e.g. password_confirmation)
60
+ # we can skip it, because that gets set below.
61
+ standard_required_attributes(instance, v) do
62
+ # We don't want to set anything if the user passed in data for this field
63
+ # or if this is a validates_presence_of :some_id. The ids will be set
64
+ # when we create the association.
65
+ next if overridden_attribute?(attributes, v.name)
66
+ # has_required_id_validation?(instance, v.name)
67
+
68
+ generate_and_set_data(instance, v, false)
69
+ end
70
+ end
71
+ end
72
+
73
+ def set_unique_attributes!(instance, attributes)
74
+ instance.class.unique_validations.each do |v|
75
+ next if overridden_attribute?(attributes, v.name)
76
+ generate_and_set_data(instance, v, true)
77
+ end
78
+ end
79
+
80
+ # This generates set_length_validated_attributes! and set_inclusion_validated_attributes! methods.
81
+ # They used to look like this:
82
+ #
83
+ # def set_length_validated_attributes!(instance, attributes)
84
+ # instance.class.length_validations.each do |v|
85
+ # non_required_attributes(instance, v, attributes) { generate_and_set_data(instance, v, false) }
86
+ # end
87
+ # end
88
+ #
89
+ # def set_inclusion_of_validated_attributes!(instance, attributes)
90
+ # instance.class.inclusion_validations.each do |v|
91
+ # non_required_attributes(instance, v, attributes) { generate_and_set_data(instance, v, false) }
92
+ # end
93
+ # end
94
+ # I'm sure you see the similarities.
95
+ %w(length inclusion).each do |validation|
96
+ define_method("set_#{validation}_validated_attributes!") do |instance, attributes|
97
+ instance.class.send("#{validation}_validations").each do |v|
98
+ # Non required attributes are anything that doesn't have validates_presence_of,
99
+ # validates_uniqueness_of and also anything that hasn't been overriden. These
100
+ # values get set there. So there's no real point in setting them again here.
101
+ non_required_attributes(instance, v, attributes) { generate_and_set_data(instance, v, false) }
102
+ end
103
+ end
104
+ end
105
+
106
+
107
+ def set_confirmation_attributes!(instance, attributes)
108
+ instance.class.confirmation_validations.each do |v|
109
+ # This goes in and sets the models confirmation to whatever the corresponding
110
+ # fields value is. (e.g. password_confirmation= password)
111
+ instance.send("#{v.name}_confirmation=", instance.send("#{v.name}"))
112
+ end
113
+ end
114
+
115
+
116
+ def create_associations(instance, recursed_from_model = nil)
117
+ # We loop through all the has_one or belongs_to associations on the current instance
118
+ # using introspection, and build up and assign some models to each, if the user has
119
+ # required the id (e.g. requires_presence_of :user_id). So we're only going to build
120
+ # the minimum requirements for each model.
121
+ instance.class.reflect_on_all_associations.each do |a|
122
+ # We only want to create the association if the user has required the id field.
123
+ # This will build the minimum valid requirements.
124
+ next unless has_required_id_validation?(instance, a.name)
125
+
126
+ if a.macro == :has_one || a.macro == :belongs_to
127
+ # If there's a two way association here (user has_one profile, profile belongs_to user)
128
+ # we only want to create one of those, or it'll recurse forever. That's what the
129
+ # recursed_from_model does.
130
+ unless recursed_from_model.to_s == instance.class.to_s.downcase
131
+ instance.send("#{a.name.to_s}=", build_model_instance(get_assocation_class_name(a), nil, a.name))
132
+ end
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ def get_assocation_class_name(assocation)
139
+ # This condition is used if the class_name option
140
+ # is passed to has_many or has_one. We'll definitely
141
+ # want to use that key instead of the name of the association.
142
+ if assocation.options.has_key?(:class_name)
143
+ klass = assocation.options[:class_name]
144
+ else
145
+ klass = assocation.name.to_s.singularize.classify
146
+ end
147
+ end
148
+
149
+ def generate_and_set_data(instance, validation, sequenced)
150
+ data_generator = DataGenerator.new(sequenced, validation)
151
+ data_generator.required_length = get_required_length(instance, validation)
152
+ if has_inclusion_validation?(instance, validation)
153
+ data_generator.inclusion_values = get_inclusion_values(instance, validation)
154
+ end
155
+ instance.send("#{validation.name}=", data_generator.generate_data_for_column_type)
156
+ end
157
+
158
+ def standard_required_attributes(instance, validation)
159
+ yield unless has_unique_validation?(instance, validation)
160
+ end
161
+
162
+ def non_required_attributes(instance, validation, attributes)
163
+ yield unless has_unique_validation?(instance, validation) || has_required_validation?(instance, validation) ||
164
+ overridden_attribute?(attributes, validation.name)
165
+ end
166
+
167
+ def overridden_attribute?(attributes, key)
168
+ attributes && attributes.has_key?(key.to_sym)
169
+ end
170
+
171
+ # This creates has_unique_validation?, has_length_validation? etc.
172
+ # We could probably make one method that takes a type, but I'd rather
173
+ # get separate methods for each. Cool?
174
+ %w(unique length required inclusion).each do |v|
175
+ define_method("has_#{v}_validation?") do |instance, validation|
176
+ !instance.class.send("#{v}_validations").select{|u| u.name == validation.name }.empty?
177
+ end
178
+ end
179
+
180
+ def get_required_length(instance, validation)
181
+ return unless has_length_validation?(instance, validation)
182
+
183
+ options = instance.class.length_validations.select{|v| v.name == validation.name }.first.options
184
+
185
+ # If the validation is validates_length_of :name, :within => 1..20 (or in, which is an alias),
186
+ # let's just return the minimum value of the range.
187
+ %w(within in).each { |o| return options[o.to_sym].entries.first if options.has_key?(o.to_sym) }
188
+ # These other validations should just return the value set.
189
+ %w(is minimum maximum).each { |o| return options[o.to_sym] if options.has_key?(o.to_sym) }
190
+
191
+ nil
192
+ end
193
+
194
+ def get_inclusion_values(instance, validation)
195
+ return unless has_inclusion_validation?(instance, validation)
196
+ options = instance.class.inclusion_validations.select{|v| v.name == validation.name }.first.options
197
+ return unless options.has_key?(:in)
198
+ options[:in]
199
+ end
200
+
201
+ def has_required_id_validation?(instance, name)
202
+ !instance.class.required_validations.select{|v| v.name.to_s == "#{name}_id" }.empty?
203
+ end
204
+
205
+ end
206
+
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,90 @@
1
+ module Koujou #:nodoc:
2
+ class DataGenerator
3
+
4
+ attr_accessor :required_length, :inclusion_values
5
+
6
+ def initialize(sequenced, validation)
7
+ @sequenced = sequenced
8
+ # Validation is actually a ActiveRecord::Reflection::MacroReflection
9
+ @validation = validation
10
+ end
11
+
12
+ def generate_data_for_column_type
13
+ # So if there was an inclusion passed in for validations_inclusion_of (which is just
14
+ # an enumberable object), let's just return the first element to ensure the value
15
+ # set is the correct one. Mmmkay?
16
+ return get_first_value_for_inclusion unless @inclusion_values.nil?
17
+ # Sometimes models have a validates_presence_of set, but there's no corresponding
18
+ # db column. The only example I can think of for this is a user model where the actual
19
+ # column is hashed_password but the model requires the presence of password. So we'll
20
+ # assume string. This could bite me in the ass later, but we'll see.
21
+ if @validation.active_record.columns_hash.has_key?("#{@validation.name}")
22
+ db_type = @validation.active_record.columns_hash["#{@validation.name}"].type
23
+ else
24
+ db_type = 'string'
25
+ end
26
+ # Since the method names are all based on the db types, we'll just go ahead and
27
+ # dynamically call the appropriate one.
28
+ send("generate_#{db_type}")
29
+ end
30
+
31
+ def generate_string
32
+ return format_if_sequenced(Faker::Internet.email) if @validation.name.to_s.match(/email/)
33
+ return format_if_sequenced(Faker::Name.first_name) if @validation.name.to_s == 'first_name'
34
+ return format_if_sequenced(Faker::Name.last_name) if @validation.name.to_s == 'last_name'
35
+ return format_if_sequenced(Faker::Internet.user_name) if @validation.name.to_s.match(/login|user_name/)
36
+ return format_if_sequenced(Faker::Address.city) if @validation.name.to_s.match(/city/)
37
+ return format_if_sequenced(Faker::Address.us_state) if @validation.name.to_s.match(/state|province/)
38
+ return format_if_sequenced(Faker::Address.zip_code) if @validation.name.to_s.match(/zip|postal/)
39
+
40
+ # If we don't match any standard stuff, just return a regular bs lorem string comprised of 10 words.
41
+ # 10 is sort of a "magic number" I might make a constant for that.
42
+ standard_text = format_if_sequenced(Faker::Lorem.words(10).to_s)
43
+ # So if there's a length validation set, we need to return just that amount of data.
44
+ standard_text = standard_text[0..@required_length - 1].to_s if @required_length
45
+ standard_text
46
+ end
47
+
48
+ def generate_text
49
+ if @required_length
50
+ # So if there's a length validation set, we need to return just that amount of data.
51
+ Faker::Lorem.paragraph(2).to_s[0..@required_length - 1]
52
+ else
53
+ Faker::Lorem.paragraph.to_s
54
+ end
55
+ end
56
+
57
+ def generate_integer
58
+ generate_number
59
+ end
60
+
61
+ def generate_float
62
+ generate_number.to_f
63
+ end
64
+
65
+ def generate_datetime
66
+ DateTime.now
67
+ end
68
+
69
+ def generate_boolean
70
+ true
71
+ end
72
+
73
+ protected
74
+ def generate_number
75
+ # If this is supposed to be sequenced (aka unique), we'll get the next int from the
76
+ # Sequence class, and randomize that.
77
+ random = rand(999)
78
+ @sequenced ? random + (Sequence.instance.next * rand(2)) : random
79
+ end
80
+
81
+ def format_if_sequenced(val)
82
+ @sequenced ? "#{Sequence.instance.next}#{val}" : val
83
+ end
84
+
85
+ def get_first_value_for_inclusion
86
+ @inclusion_values.first
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,11 @@
1
+ module Koujou #:nodoc:
2
+ class Sequence # :nodoc:
3
+ include Singleton
4
+ def initialize
5
+ @current_value = 1
6
+ end
7
+ def next
8
+ @current_value += 1
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,147 @@
1
+ #--
2
+ # Copyright (c) 2006-2008, Michael Schuerig, michael@schuerig.de
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+
25
+ require 'active_record/reflection'
26
+ require 'ostruct'
27
+
28
+ # Based on code by Sebastian Kanthak
29
+ # See http://dev.rubyonrails.org/ticket/861
30
+ module Koujou # :nodoc:
31
+ module ActiveRecordExtensions # :nodoc:
32
+ module ValidationReflection # :nodoc:
33
+
34
+ mattr_accessor :reflected_validations
35
+ Koujou::ActiveRecordExtensions::ValidationReflection.reflected_validations = %w(
36
+ validates_acceptance_of
37
+ validates_associated
38
+ validates_confirmation_of
39
+ validates_exclusion_of
40
+ validates_format_of
41
+ validates_inclusion_of
42
+ validates_length_of
43
+ validates_size_of
44
+ validates_numericality_of
45
+ validates_presence_of
46
+ validates_uniqueness_of
47
+ validate
48
+ )
49
+
50
+ mattr_accessor :in_ignored_subvalidation
51
+ Koujou::ActiveRecordExtensions::ValidationReflection.in_ignored_subvalidation = false
52
+
53
+ def self.included(base)
54
+ return if base.kind_of?(Koujou::ActiveRecordExtensions::ValidationReflection::ClassMethods)
55
+ base.extend(ClassMethods)
56
+ end
57
+
58
+ def self.install(base)
59
+ reflected_validations.freeze
60
+ reflected_validations.each do |validation_type|
61
+ next if base.respond_to?("#{validation_type}_with_reflection")
62
+ ignore_subvalidations = false
63
+ if validation_type.kind_of?(Hash)
64
+ ignore_subvalidations = validation_type[:ignore_subvalidations]
65
+ validation_type = validation_type[:method]
66
+ end
67
+ base.class_eval <<-"end_eval"
68
+ class << self
69
+ def #{validation_type}_with_reflection(*attr_names)
70
+ ignoring_subvalidations(#{ignore_subvalidations}) do
71
+ #{validation_type}_without_reflection(*attr_names)
72
+ remember_validation_metadata(:#{validation_type}, *attr_names)
73
+ end
74
+ end
75
+
76
+ alias_method_chain :#{validation_type}, :reflection
77
+ end
78
+ end_eval
79
+ end
80
+ end
81
+
82
+ module ClassMethods
83
+
84
+ # Returns an array of MacroReflection objects for all validations in the class
85
+ def reflect_on_all_validations
86
+ read_inheritable_attribute(:validations) || []
87
+ end
88
+
89
+ # Returns an array of MacroReflection objects for all validations defined for the field +attr_name+.
90
+ def reflect_on_validations_for(attr_name)
91
+ attr_name = attr_name.to_sym
92
+ reflect_on_all_validations.select do |reflection|
93
+ reflection.name == attr_name
94
+ end
95
+ end
96
+
97
+ # A few convenience methods added in by Mike. Why not loop and define_method these?
98
+
99
+ def unique_validations
100
+ reflect_on_all_validations.select{|v| v.macro == :validates_uniqueness_of }
101
+ end
102
+
103
+ def required_validations
104
+ reflect_on_all_validations.select{|v| v.macro == :validates_presence_of }
105
+ end
106
+
107
+ def confirmation_validations
108
+ reflect_on_all_validations.select{|v| v.macro == :validates_confirmation_of }
109
+ end
110
+
111
+ def length_validations
112
+ reflect_on_all_validations.select{|v| v.macro == :validates_length_of || v.macro == :validates_size_of }
113
+ end
114
+
115
+ def inclusion_validations
116
+ reflect_on_all_validations.select{|v| v.macro == :validates_inclusion_of }
117
+ end
118
+
119
+ def custom_validations
120
+ # We don't want to get anything like: validate_associated_records_for_posts.
121
+ reflect_on_all_validations.select{|v| v.macro == :validate && !v.macro.to_s.match("_associated_") }
122
+ end
123
+
124
+ private
125
+
126
+ def remember_validation_metadata(validation_type, *attr_names)
127
+ configuration = attr_names.last.is_a?(Hash) ? attr_names.pop : {}
128
+ attr_names.each do |attr_name|
129
+ write_inheritable_array :validations,
130
+ [ ActiveRecord::Reflection::MacroReflection.new(validation_type, attr_name.to_sym, configuration, self) ]
131
+ end
132
+ end
133
+
134
+ def ignoring_subvalidations(ignore)
135
+ save_ignore = Koujou::ActiveRecordExtensions::ValidationReflection.in_ignored_subvalidation
136
+ unless Koujou::ActiveRecordExtensions::ValidationReflection.in_ignored_subvalidation
137
+ Koujou::ActiveRecordExtensions::ValidationReflection.in_ignored_subvalidation = ignore
138
+ yield
139
+ end
140
+ ensure
141
+ Koujou::ActiveRecordExtensions::ValidationReflection.in_ignored_subvalidation = save_ignore
142
+ end
143
+ end
144
+
145
+ end
146
+ end
147
+ end
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/koujou.rb'}"
9
+ puts "Loading koujou gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(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)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(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,78 @@
1
+ # This let's us happily test AR.
2
+ require 'active_record'
3
+ # Require all our models.
4
+ Dir.glob(File.join(File.dirname(__FILE__), "..", "lib", "models", "*.rb")).each { |f| require f }
5
+
6
+
7
+ class ActiveRecordTestConnector
8
+ cattr_accessor :connected
9
+ cattr_accessor :able_to_connect
10
+
11
+ self.connected = false
12
+ self.able_to_connect = true
13
+
14
+ class << self
15
+
16
+ def setup
17
+ unless self.connected || !self.able_to_connect
18
+ setup_connection
19
+ load_schema
20
+ self.connected = true
21
+ end
22
+ rescue Exception => e # errors from ActiveRecord setup
23
+ $stderr.puts "\nSkipping ActiveRecord tests: #{e}\n\n"
24
+ self.able_to_connect = false
25
+ end
26
+
27
+ def setup_connection
28
+ ActiveRecord::Base.establish_connection({
29
+ :adapter => 'sqlite3',
30
+ :dbfile => 'test.sqlite3'
31
+ })
32
+ end
33
+
34
+ def load_schema
35
+ ActiveRecord::Schema.define do
36
+ create_table "users", :force => true do |t|
37
+ t.string "name", "email", "first_name", "last_name", "hashed_password"
38
+ t.integer "age"
39
+ t.float "salary"
40
+ t.datetime "hired_on"
41
+ t.boolean "terms_of_service"
42
+ end
43
+ create_table "posts", :force => true do |t|
44
+ t.string "name"
45
+ t.text "body"
46
+ t.integer "user_id"
47
+ end
48
+ create_table "comments", :force => true do |t|
49
+ t.text "bod"
50
+ t.integer "post_id"
51
+ end
52
+ create_table "profiles", :force => true do |t|
53
+ t.integer "user_id"
54
+ t.boolean "likes_cheese"
55
+ end
56
+ create_table "photos", :force => true do |t|
57
+ t.integer "profile_id"
58
+ end
59
+ create_table "messages", :force => true do |t|
60
+ t.string "subject"
61
+ t.text "body"
62
+ t.datetime "created_at"
63
+ t.datetime "updated_at"
64
+ t.integer "sender_id"
65
+ t.integer "receiver_id"
66
+ t.boolean "read"
67
+ end
68
+ create_table "cars", :force => true do |t|
69
+ t.integer "user_id", "year"
70
+ t.string "make", "model"
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+
78
+
@@ -0,0 +1,7 @@
1
+ class Comment < ActiveRecord::Base
2
+ validates_presence_of :bod
3
+ validates_length_of :bod, :maximum => 200
4
+
5
+ belongs_to :post
6
+
7
+ end
@@ -0,0 +1,15 @@
1
+ class Post < ActiveRecord::Base
2
+ validates_presence_of :name
3
+ validates_length_of :body, :is => 20
4
+
5
+ belongs_to :user
6
+ has_many :comments
7
+
8
+ validate :craziness
9
+
10
+ protected
11
+ def craziness
12
+ raise 'No way Jose' if "Matz" != "Guido van Rossum"
13
+ end
14
+
15
+ end
@@ -0,0 +1,9 @@
1
+ class Profile < ActiveRecord::Base
2
+ belongs_to :user
3
+ has_many :photos, :order => 'created_at DESC'
4
+
5
+ has_many :sent_messages, :class_name => 'Message', :order => 'created_at desc', :foreign_key => 'sender_id'
6
+
7
+ validates_presence_of :user_id
8
+
9
+ end
@@ -0,0 +1,38 @@
1
+ class User < ActiveRecord::Base
2
+ validates_presence_of :name, :age, :salary, :hired_on, :email, :first_name, :last_name
3
+ validates_uniqueness_of :name
4
+ validates_acceptance_of :terms_of_service
5
+
6
+ attr_accessor :password
7
+
8
+
9
+ # This is basically the main validation from restful_auth's user model.
10
+ validates_presence_of :password, :if => :password_required?
11
+ validates_presence_of :password_confirmation, :if => :password_required?
12
+ validates_length_of :password, :within => 4..40, :if => :password_required?
13
+ validates_confirmation_of :password, :if => :password_required?
14
+ validates_length_of :email, :within => 3..100
15
+
16
+ has_many :posts
17
+ has_one :profile
18
+
19
+ attr_accessible :email, :password, :password_confirmation
20
+
21
+ validate :custom_validation_method
22
+ validate :custom_private_method
23
+
24
+ def password_required?
25
+ true
26
+ end
27
+
28
+ def custom_validation_method
29
+ false
30
+ end
31
+
32
+ private
33
+ def custom_private_method
34
+ false
35
+ end
36
+
37
+
38
+ end
@@ -0,0 +1,143 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestBuilder < Test::Unit::TestCase
4
+
5
+ # Gives us DB access.
6
+ ActiveRecordTestConnector.setup
7
+
8
+ context 'ActiveRecord' do
9
+
10
+ should 'have the koujou method' do
11
+ assert User.respond_to?(:koujou)
12
+ end
13
+
14
+ should 'return an instance of User, with ActiveRecord::Base as an ancestor' do
15
+ u = User.koujou
16
+ assert_equal User, u.class
17
+ assert u.class.ancestors.include?(ActiveRecord::Base)
18
+ end
19
+ end
20
+
21
+ context 'on sending the koujou message' do
22
+
23
+ should 'persist the record to the db without any arguments' do
24
+ u = User.koujou
25
+ assert !u.new_record?
26
+ end
27
+
28
+ should 'return a new record when passing in false' do
29
+ u = User.koujou(false)
30
+ assert u.new_record?
31
+ end
32
+
33
+ should 'have unique values for multiple instances where validates_uniqueness_of is defined for a column' do
34
+ u1 = User.koujou
35
+ u2 = User.koujou
36
+ assert_not_equal u1.name, u2.name
37
+ end
38
+
39
+ should 'have a password confirmation automatically set' do
40
+ u = User.koujou
41
+ assert_not_nil u.password_confirmation
42
+ end
43
+
44
+ should 'allow me to override the model attributes' do
45
+ namae = 'One Factory to Rule them all'
46
+ p = Post.koujou(true, :name => namae)
47
+ assert_equal namae, p.name
48
+ end
49
+
50
+ should 'allow me to override select attributes, yet still generate data for the rest' do
51
+ # We have a validates_length_of :bod, :is => 20 set.
52
+ bod = "T" * 20
53
+ p = Post.koujou_create(:body => bod)
54
+ assert_not_nil p.name
55
+ assert_equal bod, p.body
56
+ end
57
+
58
+ end
59
+
60
+ context 'on sending the koujou_create message' do
61
+
62
+ should 'persist the record to the db' do
63
+ u = User.koujou_create
64
+ assert !u.new_record?
65
+ end
66
+
67
+ should 'allow me to override the model attributes' do
68
+ comment = 'your post is epic fail'
69
+ c = Comment.koujou_create(:bod => comment)
70
+ assert_equal comment, c.bod
71
+ end
72
+
73
+ should 'not be sequenced unless I say so' do
74
+ u = User.koujou
75
+ # The first digit should not be an integer.
76
+ assert_equal 0, u.first_name[0,1].to_i
77
+ end
78
+
79
+ end
80
+
81
+ context 'on sending the koujou_build message' do
82
+
83
+ should 'return a new record' do
84
+ u = User.koujou_build
85
+ assert u.new_record?
86
+ end
87
+
88
+ should 'allow me to override the model attributes' do
89
+ clever = 'Whatever\'s clever'
90
+ p = Post.koujou_build(:name => clever)
91
+ assert_equal clever, p.name
92
+ end
93
+
94
+ end
95
+
96
+ context 'associations' do
97
+
98
+ should 'not automatically create any assoications unless there\'s a validation for the id' do
99
+ u = User.koujou
100
+ assert_equal 0, u.posts.size
101
+ end
102
+
103
+ should 'automatically create a user for a profile when the profile has a required user_id validation' do
104
+ p = Profile.koujou
105
+ assert_not_nil p.user
106
+ end
107
+
108
+ should 'find custom validations' do
109
+ User.reflect_on_all_validations.each do |v|
110
+ puts v.inspect if v.macro == :validate
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ context 'custom validations' do
117
+
118
+ should 'totally override any custom validations, and thus not fail when we call koujou' do
119
+ p = Post.koujou
120
+ assert true
121
+ end
122
+
123
+ end
124
+
125
+ context 'inclusion_of_validation' do
126
+
127
+ should 'create the correct value for an attribute marked with validates_inclusion_of' do
128
+ c = Car.koujou
129
+ assert_equal 'Nissan', c.make
130
+ end
131
+
132
+ should 'create the correct values for inclusion when it\'s not also a required attribute' do
133
+ c = Car.koujou
134
+ assert_equal 1900, c.year
135
+ end
136
+
137
+ end
138
+
139
+
140
+ end
141
+
142
+
143
+
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestDataGenerator < Test::Unit::TestCase
4
+
5
+ context 'on generate' do
6
+
7
+ setup do
8
+ @validation = mock("ActiveRecord::Reflection::MacroReflection")
9
+ @validation.expects(:active_record).twice.returns(User)
10
+ end
11
+
12
+ should 'generate a valid int' do
13
+ @validation.expects(:name).twice.returns('age')
14
+ int = Koujou::DataGenerator.new(false, @validation).generate_data_for_column_type
15
+ assert_kind_of Fixnum, int
16
+ assert int > 0
17
+ end
18
+
19
+ should 'generate a valid float' do
20
+ @validation.expects(:name).twice.returns('salary')
21
+ float = Koujou::DataGenerator.new(false, @validation).generate_data_for_column_type
22
+ assert_kind_of Float, float
23
+ end
24
+
25
+ should 'generate a valid string' do
26
+ @validation.expects(:name).times(1..10).returns('name')
27
+ string = Koujou::DataGenerator.new(false, @validation).generate_data_for_column_type
28
+ assert_kind_of String, string
29
+ assert string.size > 0
30
+ end
31
+
32
+ should 'generate a valid email' do
33
+ @validation.expects(:name).times(1..10).returns('email')
34
+ email = Koujou::DataGenerator.new(false, @validation).generate_data_for_column_type
35
+ assert_match(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, email)
36
+ end
37
+
38
+ should 'generate a valid datetime' do
39
+ @validation.expects(:name).twice.returns('hired_on')
40
+ dt = Koujou::DataGenerator.new(false, @validation).generate_data_for_column_type
41
+ assert_kind_of DateTime, dt
42
+ end
43
+
44
+ should 'always generate true for booleans' do
45
+ @validation.expects(:name).twice.returns('terms_of_service')
46
+ assert Koujou::DataGenerator.new(false, @validation).generate_data_for_column_type
47
+ end
48
+
49
+ end
50
+
51
+ context 'on generate with a required length' do
52
+ setup do
53
+ @validation = mock("ActiveRecord::Reflection::MacroReflection")
54
+ @validation.expects(:active_record).twice.returns(Post)
55
+ end
56
+
57
+ should 'generate the correct size string with a length passed in' do
58
+ @validation.expects(:name).twice.returns('body')
59
+ data_generator = Koujou::DataGenerator.new(false, @validation)
60
+ data_generator.required_length = 20
61
+ body = data_generator.generate_data_for_column_type
62
+ assert_equal 20, body.size
63
+ end
64
+
65
+ end
66
+
67
+ context 'on generate with an inclusion' do
68
+
69
+ setup do
70
+ @validation = mock("ActiveRecord::Reflection::MacroReflection")
71
+ end
72
+
73
+ should 'generate the correct string with an inclusion_value passed in' do
74
+ data_generator = Koujou::DataGenerator.new(false, @validation)
75
+ data_generator.inclusion_values = %w(Nissan Subaru)
76
+ make = data_generator.generate_data_for_column_type
77
+ assert_equal 'Nissan', make
78
+ end
79
+
80
+ should 'generate the correct int with an inclusion_value passed in' do
81
+ data_generator = Koujou::DataGenerator.new(false, @validation)
82
+ data_generator.inclusion_values = (1..3)
83
+ num = data_generator.generate_data_for_column_type
84
+ assert_equal 1, num
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,11 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ gem 'thoughtbot-shoulda'
5
+ require 'shoulda'
6
+ gem 'mocha'
7
+ require 'mocha'
8
+ require File.dirname(__FILE__) + '/../lib/koujou'
9
+ require File.join(File.dirname(__FILE__), 'lib', 'active_record_test_connector')
10
+
11
+
data/test/test_kojo.rb ADDED
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestKoujou < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def test_truth
9
+ assert true
10
+ # assert User.respond_to?(:koujou)
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestSequence < Test::Unit::TestCase
4
+
5
+ context "sequence" do
6
+
7
+ should "implement the singleton pattern" do
8
+ assert !Koujou::Sequence.respond_to?(:new)
9
+ assert Koujou::Sequence.include?(Singleton)
10
+ end
11
+
12
+ should "generate successive numbers when calling next" do
13
+ first = Koujou::Sequence.instance.next
14
+ second = Koujou::Sequence.instance.next
15
+ assert_not_equal first, second
16
+ end
17
+
18
+ end
19
+
20
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mleung-koujou
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Michael Leung
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-19 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: faker
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.3.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.2
34
+ version:
35
+ description: "Koujou is a fixture replacement that requires no effort to use. You don't have to define a single thing in your test_helper or whatever. Just call the koujou method on your active record model, and you're off. check out: http://www.michaelleung.us/koujou for all the juicy details."
36
+ email:
37
+ - me@michaelleung.us
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - History.txt
44
+ - Manifest.txt
45
+ - PostInstall.txt
46
+ files:
47
+ - History.txt
48
+ - Manifest.txt
49
+ - PostInstall.txt
50
+ - README.rdoc
51
+ - Rakefile
52
+ - lib/koujou.rb
53
+ - lib/koujou/builder.rb
54
+ - lib/koujou/data_generator.rb
55
+ - lib/koujou/sequence.rb
56
+ - lib/koujou/validation_reflection.rb
57
+ - script/console
58
+ - script/destroy
59
+ - script/generate
60
+ - test/lib/active_record_test_connector.rb
61
+ - test/lib/models/comment.rb
62
+ - test/lib/models/post.rb
63
+ - test/lib/models/profile.rb
64
+ - test/lib/models/user.rb
65
+ - test/test_builder.rb
66
+ - test/test_data_generator.rb
67
+ - test/test_helper.rb
68
+ - test/test_kojo.rb
69
+ - test/test_sequence.rb
70
+ has_rdoc: true
71
+ homepage: http://github.com/mleung/koujou
72
+ post_install_message: PostInstall.txt
73
+ rdoc_options:
74
+ - --main
75
+ - README.rdoc
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: "0"
89
+ version:
90
+ requirements: []
91
+
92
+ rubyforge_project: koujou
93
+ rubygems_version: 1.2.0
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Koujou is a fixture replacement that requires no effort to use
97
+ test_files:
98
+ - test/test_builder.rb
99
+ - test/test_data_generator.rb
100
+ - test/test_helper.rb
101
+ - test/test_kojo.rb
102
+ - test/test_sequence.rb