sample_models 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+