mleung-koujou 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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