sample_models 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +4 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.markdown +345 -0
  4. data/Rakefile +53 -0
  5. data/VERSION +1 -0
  6. data/init.rb +2 -0
  7. data/install.rb +1 -0
  8. data/lib/sample_models/creation.rb +116 -0
  9. data/lib/sample_models/finder.rb +81 -0
  10. data/lib/sample_models/model.rb +136 -0
  11. data/lib/sample_models/sampler.rb +110 -0
  12. data/lib/sample_models.rb +117 -0
  13. data/sample_models.gemspec +89 -0
  14. data/spec/sample_models_spec.rb +11 -0
  15. data/spec_or_test/database.yml +7 -0
  16. data/spec_or_test/setup.rb +249 -0
  17. data/spec_or_test/specs_or_test_cases.rb +569 -0
  18. data/spec_or_test/vendor/validates_email_format_of/CHANGELOG +11 -0
  19. data/spec_or_test/vendor/validates_email_format_of/MIT-LICENSE +20 -0
  20. data/spec_or_test/vendor/validates_email_format_of/README +28 -0
  21. data/spec_or_test/vendor/validates_email_format_of/TODO +1 -0
  22. data/spec_or_test/vendor/validates_email_format_of/init.rb +1 -0
  23. data/spec_or_test/vendor/validates_email_format_of/lib/validates_email_format_of.rb +41 -0
  24. data/spec_or_test/vendor/validates_email_format_of/rakefile +28 -0
  25. data/spec_or_test/vendor/validates_email_format_of/test/database.yml +3 -0
  26. data/spec_or_test/vendor/validates_email_format_of/test/fixtures/people.yml +3 -0
  27. data/spec_or_test/vendor/validates_email_format_of/test/fixtures/person.rb +3 -0
  28. data/spec_or_test/vendor/validates_email_format_of/test/schema.rb +5 -0
  29. data/spec_or_test/vendor/validates_email_format_of/test/test_helper.rb +35 -0
  30. data/spec_or_test/vendor/validates_email_format_of/test/validates_email_format_of_test.rb +82 -0
  31. data/tasks/sample_models_tasks.rake +4 -0
  32. data/test/test_sample_models.rb +28 -0
  33. data/uninstall.rb +1 -0
  34. data/vendor/ar_query/MIT-LICENSE +20 -0
  35. data/vendor/ar_query/README +0 -0
  36. data/vendor/ar_query/ar_query.gemspec +16 -0
  37. data/vendor/ar_query/init.rb +1 -0
  38. data/vendor/ar_query/install.rb +1 -0
  39. data/vendor/ar_query/lib/ar_query.rb +146 -0
  40. data/vendor/ar_query/spec/ar_query_spec.rb +318 -0
  41. data/vendor/ar_query/tasks/ar_query_tasks.rake +0 -0
  42. data/vendor/ar_query/uninstall.rb +1 -0
  43. metadata +117 -0
@@ -0,0 +1,136 @@
1
+ require 'delegate'
2
+
3
+ module SampleModels
4
+ class Model < Delegator
5
+ attr_reader :validation_collections
6
+
7
+ def initialize(model_class)
8
+ @model_class = model_class
9
+ @validation_collections = Hash.new { |h, field|
10
+ h[field] = ValidationCollection.new(self, field)
11
+ }
12
+ end
13
+
14
+ def __getobj__
15
+ @model_class
16
+ end
17
+
18
+ def associations(klass)
19
+ @model_class.reflect_on_all_associations.select { |a|
20
+ begin
21
+ a.klass == klass
22
+ rescue NameError
23
+ false
24
+ end
25
+ }
26
+ end
27
+
28
+ def belongs_to_associations
29
+ @model_class.reflect_on_all_associations.select { |assoc|
30
+ assoc.macro == :belongs_to
31
+ }
32
+ end
33
+
34
+ def construct_finder_sql(*args)
35
+ @model_class.send(:construct_finder_sql, *args)
36
+ end
37
+
38
+ def has_many_associations
39
+ @model_class.reflect_on_all_associations.select { |assoc|
40
+ assoc.macro == :has_many
41
+ }
42
+ end
43
+
44
+ def record_validation(*args)
45
+ type = args.shift
46
+ config = args.extract_options!
47
+ fields = args
48
+ fields.each do |field|
49
+ @validation_collections[field].add(type, config)
50
+ end
51
+ end
52
+
53
+ def validates_presence_of?(attr)
54
+ @validation_collections[attr].includes_presence?
55
+ end
56
+
57
+ class ValidationCollection
58
+ def initialize(model, field)
59
+ @model, @field = model, field
60
+ @sequence_number = 0
61
+ @validations = {}
62
+ end
63
+
64
+ def add(type, config)
65
+ @validations[type] = config
66
+ end
67
+
68
+ def association
69
+ @model.belongs_to_associations.detect { |a|
70
+ a.association_foreign_key.to_sym == @field.to_sym
71
+ }
72
+ end
73
+
74
+ def column
75
+ @model.columns.detect { |c| c.name == @field.to_s }
76
+ end
77
+
78
+ def includes_presence?
79
+ @validations.has_key?(:validates_presence_of)
80
+ end
81
+
82
+ def includes_uniqueness?
83
+ @validations.has_key?(:validates_uniqueness_of)
84
+ end
85
+
86
+ def satisfying_present_associated_value
87
+ value = if includes_uniqueness?
88
+ association.klass.create_sample
89
+ else
90
+ association.klass.first || association.klass.sample
91
+ end
92
+ value = value.id if value
93
+ value
94
+ end
95
+
96
+ def satisfying_present_value(prev_value)
97
+ if association
98
+ satisfying_present_associated_value
99
+ else
100
+ if prev_value.present?
101
+ prev_value
102
+ elsif column && column.type == :date
103
+ Date.today
104
+ else
105
+ "#{@field} #{@sequence_number}"
106
+ end
107
+ end
108
+ end
109
+
110
+ def satisfying_value
111
+ @sequence_number += 1 if includes_uniqueness?
112
+ value = nil
113
+ @validations.each do |type, config|
114
+ case type
115
+ when :validates_email_format_of
116
+ value = "john.doe#{@sequence_number}@example.com"
117
+ when :validates_inclusion_of
118
+ value = config[:in].first
119
+ when :validates_presence_of
120
+ value = satisfying_present_value(value)
121
+ end
122
+ end
123
+ value = unique_value if value.nil? && includes_uniqueness?
124
+ value
125
+ end
126
+
127
+ def unique_value
128
+ if column.type == :string
129
+ "#{@field.to_s.capitalize} #{@sequence_number}"
130
+ elsif column.type == :datetime
131
+ Time.utc(1970, 1, 1) + @sequence_number.days
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,110 @@
1
+ module SampleModels
2
+ class Sampler
3
+ def self.reified_belongs_to_associations(model, attrs)
4
+ assocs = {}
5
+ model.belongs_to_associations.each do |assoc|
6
+ if value = attrs[assoc.name]
7
+ if value.is_a?(Hash)
8
+ assocs[assoc.name] = assoc.klass.sample(value)
9
+ elsif value.is_a?(Array)
10
+ assocs[assoc.name] = assoc.klass.sample(*value)
11
+ end
12
+ end
13
+ end
14
+ assocs
15
+ end
16
+
17
+ def self.reify_association_hashes(model, attrs)
18
+ a = attrs.clone
19
+ reified_belongs_to_associations(model, attrs).each do |name, value|
20
+ a[name] = value
21
+ end
22
+ model.has_many_associations.each do |assoc|
23
+ if values = a[assoc.name]
24
+ a[assoc.name] = values.map { |value|
25
+ value.is_a?(Hash) ? assoc.klass.sample(value) : value
26
+ }
27
+ end
28
+ end
29
+ a
30
+ end
31
+
32
+ attr_accessor :before_save
33
+ attr_reader :configured_default_attrs, :model_class, :named_sample_attrs,
34
+ :polymorphic_default_classes
35
+
36
+ def initialize(model_class)
37
+ @model_class = model_class
38
+ @configured_default_attrs = {}
39
+ @named_sample_attrs = HashWithIndifferentAccess.new
40
+ @polymorphic_default_classes = HashWithIndifferentAccess.new
41
+ end
42
+
43
+ def create_sample(*args)
44
+ attrs = preprocessed_attributes(*args)
45
+ Creation.new(self, attrs).run
46
+ end
47
+
48
+ def fix_deleted_associations(instance, orig_attrs)
49
+ needs_save = false
50
+ model.belongs_to_associations.each do |assoc|
51
+ if instance.send(assoc.primary_key_name) &&
52
+ !instance.send(assoc.name)
53
+ instance.send("#{assoc.name}=", assoc.klass.sample)
54
+ needs_save = true
55
+ end
56
+ end
57
+ save!(instance, orig_attrs) if needs_save
58
+ end
59
+
60
+ def model
61
+ SampleModels.models[@model_class]
62
+ end
63
+
64
+ def preprocessed_attributes(*args)
65
+ if args.first.is_a?(Symbol)
66
+ preprocessed_named_sample_attrs(args)
67
+ else
68
+ attrs = args.last.is_a?(Hash) ? args.pop : {}
69
+ args.each do |associated_value|
70
+ assocs = model.associations(associated_value.class)
71
+ if assocs.size == 1
72
+ attrs[assocs.first.name] = associated_value
73
+ else
74
+ raise "Not sure what to do with associated value #{associated_value.inspect}"
75
+ end
76
+ end
77
+ attrs
78
+ end
79
+ end
80
+
81
+ def preprocessed_named_sample_attrs(args)
82
+ attrs = named_sample_attrs[args.shift]
83
+ attrs = attrs.merge(args.first) unless args.empty?
84
+ attrs
85
+ end
86
+
87
+ def sample(*args)
88
+ attrs = preprocessed_attributes(*args)
89
+ instance = Finder.new(model, attrs).instance
90
+ if instance
91
+ fix_deleted_associations(instance, attrs)
92
+ else
93
+ instance = create_sample attrs
94
+ end
95
+ instance
96
+ end
97
+
98
+ def save!(instance, orig_attrs)
99
+ if @before_save
100
+ if @before_save.arity == 1
101
+ @before_save.call instance
102
+ else
103
+ @before_save.call instance, orig_attrs
104
+ end
105
+ end
106
+ instance.save!
107
+ end
108
+ end
109
+ end
110
+
@@ -0,0 +1,117 @@
1
+ if RAILS_ENV == 'test' # no reason to run this code outside of test mode
2
+
3
+ require "#{File.dirname(__FILE__)}/sample_models/creation"
4
+ require "#{File.dirname(__FILE__)}/sample_models/finder"
5
+ require "#{File.dirname(__FILE__)}/sample_models/model"
6
+ require "#{File.dirname(__FILE__)}/sample_models/sampler"
7
+ require "#{File.dirname(__FILE__)}/../vendor/ar_query/lib/ar_query"
8
+
9
+ module SampleModels
10
+ mattr_reader :models
11
+ @@models = Hash.new { |h, model_class|
12
+ h[model_class] = Model.new(model_class)
13
+ }
14
+
15
+ mattr_reader :samplers
16
+ @@samplers = Hash.new { |h, model_class|
17
+ h[model_class] = Sampler.new(model_class)
18
+ }
19
+
20
+ def self.configure(model_class, opts ={})
21
+ yield ConfigureRecipient.new(model_class) if block_given?
22
+ end
23
+
24
+ protected
25
+
26
+ def self.included( mod )
27
+ mod.extend ARClassMethods
28
+ super
29
+ end
30
+
31
+ class ConfigureRecipient
32
+ def initialize(model_class)
33
+ @model_class = model_class
34
+ end
35
+
36
+ def before_save(&proc)
37
+ sampler.before_save = proc
38
+ end
39
+
40
+ def method_missing(meth, *args)
41
+ if meth.to_s =~ /(.*)_sample$/
42
+ sampler.named_sample_attrs[$1] = args.first
43
+ else
44
+ Attribute.new(sampler, meth)
45
+ end
46
+ end
47
+
48
+ def sampler
49
+ SampleModels.samplers[@model_class]
50
+ end
51
+
52
+ class Attribute
53
+ def initialize(sampler, attribute)
54
+ @sampler, @attribute = sampler, attribute
55
+ end
56
+
57
+ def default_class(dc)
58
+ @sampler.polymorphic_default_classes[@attribute] = dc
59
+ end
60
+
61
+ def default(default)
62
+ if default.blank? and model.validates_presence_of?(@attribute)
63
+ raise "#{model.name} requires #{@attribute} to not be blank"
64
+ else
65
+ @sampler.configured_default_attrs[@attribute] = default
66
+ end
67
+ end
68
+
69
+ def force_unique
70
+ model.record_validation :validates_uniqueness_of, @attribute
71
+ end
72
+
73
+ def model
74
+ SampleModels.models[@sampler.model_class]
75
+ end
76
+ end
77
+ end
78
+
79
+ module ARClassMethods
80
+ def create_sample(*args)
81
+ sampler = SampleModels.samplers[self]
82
+ sampler.create_sample(*args)
83
+ end
84
+
85
+ def sample(*args)
86
+ sampler = SampleModels.samplers[self]
87
+ sampler.sample(*args)
88
+ end
89
+ end
90
+ end
91
+
92
+ module ActiveRecord
93
+ class Base
94
+ include SampleModels
95
+ end
96
+
97
+ module Validations
98
+ module ClassMethods
99
+ [:validates_email_format_of,
100
+ :validates_inclusion_of, :validates_presence_of,
101
+ :validates_uniqueness_of].each do |validation|
102
+ if method_defined?(validation)
103
+ define_method "#{validation}_with_sample_models".to_sym do |*args|
104
+ send "#{validation}_without_sample_models".to_sym, *args
105
+ SampleModels.models[self].record_validation(
106
+ validation, *args
107
+ )
108
+ end
109
+ alias_method_chain validation, :sample_models
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ end # if RAILS_ENV == 'test'
117
+
@@ -0,0 +1,89 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{sample_models}
8
+ s.version = "0.9.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Francis Hwang"]
12
+ s.date = %q{2010-11-06}
13
+ s.description = %q{
14
+ A library for making it extremely fast for Rails developers to set up and save ActiveRecord instances when writing test cases. It aims to:
15
+
16
+ * meet all your validations automatically
17
+ * only make you specify the attributes you care about
18
+ * give you a rich set of features so you can specify associated values as concisely as possible
19
+ * do this with as little configuration as possible
20
+ }
21
+ s.email = %q{francis.hwang@profitably.com}
22
+ s.extra_rdoc_files = [
23
+ "README.markdown"
24
+ ]
25
+ s.files = [
26
+ ".gitignore",
27
+ "MIT-LICENSE",
28
+ "README.markdown",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "init.rb",
32
+ "install.rb",
33
+ "lib/sample_models.rb",
34
+ "lib/sample_models/creation.rb",
35
+ "lib/sample_models/finder.rb",
36
+ "lib/sample_models/model.rb",
37
+ "lib/sample_models/sampler.rb",
38
+ "sample_models.gemspec",
39
+ "spec/sample_models_spec.rb",
40
+ "spec_or_test/database.yml",
41
+ "spec_or_test/setup.rb",
42
+ "spec_or_test/specs_or_test_cases.rb",
43
+ "spec_or_test/vendor/validates_email_format_of/CHANGELOG",
44
+ "spec_or_test/vendor/validates_email_format_of/MIT-LICENSE",
45
+ "spec_or_test/vendor/validates_email_format_of/README",
46
+ "spec_or_test/vendor/validates_email_format_of/TODO",
47
+ "spec_or_test/vendor/validates_email_format_of/init.rb",
48
+ "spec_or_test/vendor/validates_email_format_of/lib/validates_email_format_of.rb",
49
+ "spec_or_test/vendor/validates_email_format_of/rakefile",
50
+ "spec_or_test/vendor/validates_email_format_of/test/database.yml",
51
+ "spec_or_test/vendor/validates_email_format_of/test/fixtures/people.yml",
52
+ "spec_or_test/vendor/validates_email_format_of/test/fixtures/person.rb",
53
+ "spec_or_test/vendor/validates_email_format_of/test/schema.rb",
54
+ "spec_or_test/vendor/validates_email_format_of/test/test_helper.rb",
55
+ "spec_or_test/vendor/validates_email_format_of/test/validates_email_format_of_test.rb",
56
+ "tasks/sample_models_tasks.rake",
57
+ "test/test_sample_models.rb",
58
+ "uninstall.rb",
59
+ "vendor/ar_query/MIT-LICENSE",
60
+ "vendor/ar_query/README",
61
+ "vendor/ar_query/ar_query.gemspec",
62
+ "vendor/ar_query/init.rb",
63
+ "vendor/ar_query/install.rb",
64
+ "vendor/ar_query/lib/ar_query.rb",
65
+ "vendor/ar_query/spec/ar_query_spec.rb",
66
+ "vendor/ar_query/tasks/ar_query_tasks.rake",
67
+ "vendor/ar_query/uninstall.rb"
68
+ ]
69
+ s.homepage = %q{http://github.com/fhwang/sample_models}
70
+ s.rdoc_options = ["--charset=UTF-8"]
71
+ s.require_paths = ["lib"]
72
+ s.rubygems_version = %q{1.3.7}
73
+ s.summary = %q{A library for making it extremely fast for Rails developers to set up and save ActiveRecord instances when writing test cases}
74
+ s.test_files = [
75
+ "spec/sample_models_spec.rb",
76
+ "test/test_sample_models.rb"
77
+ ]
78
+
79
+ if s.respond_to? :specification_version then
80
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
81
+ s.specification_version = 3
82
+
83
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
84
+ else
85
+ end
86
+ else
87
+ end
88
+ end
89
+
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + "/../spec_or_test/setup"
2
+ require 'test/unit/assertions'
3
+
4
+ class Spec::Example::ExampleGroup
5
+ include Test::Unit::Assertions
6
+ end
7
+
8
+ initialize_db
9
+
10
+ require File.dirname(__FILE__) + "/../spec_or_test/specs_or_test_cases"
11
+
@@ -0,0 +1,7 @@
1
+ mysql:
2
+ adapter: mysql
3
+ host: localhost
4
+ username: test
5
+ password: test
6
+ database: sample_models_test
7
+