dataset 1.3.1

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 (46) hide show
  1. data/CHANGELOG +59 -0
  2. data/LICENSE +19 -0
  3. data/README +111 -0
  4. data/Rakefile +31 -0
  5. data/TODO +15 -0
  6. data/VERSION.yml +4 -0
  7. data/lib/dataset.rb +128 -0
  8. data/lib/dataset/base.rb +157 -0
  9. data/lib/dataset/collection.rb +19 -0
  10. data/lib/dataset/database/base.rb +30 -0
  11. data/lib/dataset/database/mysql.rb +34 -0
  12. data/lib/dataset/database/postgresql.rb +34 -0
  13. data/lib/dataset/database/sqlite3.rb +32 -0
  14. data/lib/dataset/extensions/cucumber.rb +20 -0
  15. data/lib/dataset/extensions/rspec.rb +21 -0
  16. data/lib/dataset/extensions/test_unit.rb +60 -0
  17. data/lib/dataset/instance_methods.rb +10 -0
  18. data/lib/dataset/load.rb +47 -0
  19. data/lib/dataset/record/fixture.rb +73 -0
  20. data/lib/dataset/record/meta.rb +66 -0
  21. data/lib/dataset/record/model.rb +50 -0
  22. data/lib/dataset/resolver.rb +110 -0
  23. data/lib/dataset/session.rb +51 -0
  24. data/lib/dataset/session_binding.rb +317 -0
  25. data/lib/dataset/version.rb +9 -0
  26. data/plugit/descriptor.rb +25 -0
  27. data/spec/dataset/cucumber_spec.rb +54 -0
  28. data/spec/dataset/database/base_spec.rb +21 -0
  29. data/spec/dataset/record/meta_spec.rb +14 -0
  30. data/spec/dataset/resolver_spec.rb +110 -0
  31. data/spec/dataset/rspec_spec.rb +133 -0
  32. data/spec/dataset/session_binding_spec.rb +198 -0
  33. data/spec/dataset/session_spec.rb +299 -0
  34. data/spec/dataset/test_unit_spec.rb +210 -0
  35. data/spec/fixtures/datasets/constant_not_defined.rb +0 -0
  36. data/spec/fixtures/datasets/ending_with_dataset.rb +2 -0
  37. data/spec/fixtures/datasets/exact_name.rb +2 -0
  38. data/spec/fixtures/datasets/not_a_dataset_base.rb +2 -0
  39. data/spec/fixtures/more_datasets/in_another_directory.rb +2 -0
  40. data/spec/models.rb +18 -0
  41. data/spec/schema.rb +26 -0
  42. data/spec/spec_helper.rb +47 -0
  43. data/spec/stubs/mini_rails.rb +18 -0
  44. data/spec/stubs/test_help.rb +1 -0
  45. data/tasks/dataset.rake +19 -0
  46. metadata +120 -0
data/CHANGELOG ADDED
@@ -0,0 +1,59 @@
1
+ *1.3.0 [Rails 2.3.2] (??)
2
+
3
+ * Supporting Rails 2.3.2 [mhawkins, underlog]
4
+ * Added tasks to load path so folks can require 'dataset.rake' [aiwilliams]
5
+ * Updated gemspec to include dependencies [aiwilliams]
6
+
7
+ *1.2.0 [Cucumber] (April 8, 2009)
8
+
9
+ * Support for cucumber [jgarber, seancribbs]
10
+
11
+ *1.1.0 [STI, belongs_to] (February 14, 2009)
12
+
13
+ * STI is better supported for inserting, naming and finding records [aiwilliams]
14
+
15
+ class Place < ActiveRecord::Base; end
16
+ class State < Place; end
17
+ class NorthCarolina < State; end
18
+
19
+ create_record(NorthCarolina, :state) # no need to define the 'type' column value
20
+ states(:state) == places(:state) == north_carolinas(:state) # read with the class names pluralized
21
+
22
+ * Moved to jeweler for much cleaner, github embracing gem building [aiwilliams]
23
+ * Support for simple belongs to associations [aiwilliams]
24
+
25
+ class Person < ActiveRecord::Base; end
26
+ class Note < ActiveRecord::Base
27
+ belongs_to :person
28
+ end
29
+
30
+ person_id = create_record Person, :myguy
31
+ create_record Note, :person => :myguy
32
+ Note.last.person_id == person_id
33
+
34
+ * Models inside modules are supported a little more conveniently [aiwilliams]
35
+
36
+ module MList
37
+ class Message < ActiveRecord::Base
38
+ end
39
+ end
40
+
41
+ # We'll get rid of the underscore in 'm_list_messages'
42
+ create_record MList::Message, :test
43
+ mlist_messages(:test)
44
+
45
+ * Helper method for converting strings to useful symbols for finding records [siannopollo]
46
+
47
+ This is useful if you write creator methods of your own.
48
+
49
+ def create_person(attributes)
50
+ create_record Person, name_to_sym(attributes[:name]), attributes
51
+ end
52
+
53
+ create_person(:name => 'Little John')
54
+ people(:little_john)
55
+
56
+
57
+ *1.0.0 [Scenarios Replacement] (December 15, 2008)
58
+
59
+ * Drop-in replacement for Scenarios plugin of old [aiwilliams]
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008-2009, Adam Williams
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README ADDED
@@ -0,0 +1,111 @@
1
+ = Dataset
2
+
3
+ Dataset provides a simple API for creating and finding sets of data in your database. Check out Dataset::RecordMethods and Dataset::ModelFinders.
4
+
5
+ Dataset loads data intelligently if you use 'nested contexts' in your tests (RSpec, anything that uses Test::Unit::TestCase subclassing for creating nested contexts):
6
+
7
+ describe Something do
8
+ dataset :a => Dataset :a is loaded (at the right time)
9
+
10
+ it 'should whatever'
11
+ end
12
+
13
+ describe More do
14
+ dataset :b => Dataset :b is loaded. :a data is still there
15
+
16
+ it 'should'
17
+ end
18
+ end
19
+
20
+ describe Another do => Database is restored to :a, without re-running :a logic
21
+ it 'should'
22
+ end
23
+ end
24
+ end
25
+
26
+ The goal is to see a marked improvement in overall test run speed, basing this on the assumption that it is faster to have the OS copy a file or mySQL dump and load. Of course, we may find this to be a false assumption, but there were plenty of bugs in the former 'Scenarios' - addressing that afforded the opportunity to test the assumption.
27
+
28
+
29
+ Dataset does not prevent you from using other libraries like Machinist or factory_girl. If you were to used either of those, you could have a dataset like this:
30
+
31
+ require 'faker'
32
+
33
+ class OrganizationsDataset < Dataset::Base
34
+ Sham.name { Faker::Name.name }
35
+
36
+ Organization.blueprint do
37
+ name { Sham.name }
38
+ end
39
+
40
+ def load
41
+ name_model Organization.make, :org_one
42
+ end
43
+ end
44
+
45
+ The benefit is that you can reuse interesting sets of data, without sacrificing the utility of those other libraries.
46
+
47
+ describe Organization, 'stuff' do
48
+ dataset :organizations
49
+ end
50
+
51
+ describe Organization, 'other stuff' do
52
+ dataset :organizations
53
+ end
54
+
55
+
56
+ Get things installed, then read more in the Dataset documentation at http://aiwilliams.github.com/dataset
57
+
58
+
59
+ == Installation
60
+
61
+ Install the plugin:
62
+
63
+ ./script/plugin install git://github.com/aiwilliams/dataset.git
64
+
65
+ In your test_helper.rb/spec_helper.rb:
66
+
67
+ require 'dataset'
68
+ class Test::Unit::TestCase
69
+ include Dataset
70
+ datasets_directory "#{RAILS_ROOT}/spec/datasets"
71
+ end
72
+
73
+ If you don't use rspec_on_rails, or you have specs that aren't of the RailsExampleGroup type, you should do this in spec_helper.rb:
74
+
75
+ require 'dataset'
76
+ class Spec::Example::ExampleGroup
77
+ include Dataset
78
+ datasets_directory "#{RAILS_ROOT}/spec/datasets"
79
+ end
80
+
81
+ If you were a user of the Scenarios plugin, and want to do as little as possible to get going (assumes you are using rspec_on_rails):
82
+
83
+ require 'dataset'
84
+ Scenario = Scenarios = Dataset
85
+ class Test::Unit::TestCase
86
+ include Dataset
87
+ class << self
88
+ alias_method :scenario, :dataset
89
+ end
90
+ end
91
+ class ScenariosResolver < Dataset::DirectoryResolver
92
+ def suffix
93
+ @suffix ||= 'Scenario'
94
+ end
95
+ end
96
+ Dataset::Resolver.default = ScenariosResolver.new("#{RAILS_ROOT}/spec/scenarios")
97
+
98
+
99
+ == Credits
100
+
101
+ Written by [Adam Williams](http://github.com/aiwilliams).
102
+
103
+ Contributors:
104
+
105
+ - [Saturn Flyer](http://www.saturnflyer.com) [github](http://github.com/saturnflyer)
106
+ - [Steve Iannopollo](http://github.com/siannopollo)
107
+ - [John Long](http://github.com/jlong)
108
+
109
+ ---
110
+
111
+ Dataset is released under the MIT-License and is Copyright (c)2008 Adam Williams.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
2
+
3
+ require File.join(File.dirname(__FILE__), 'plugit/descriptor')
4
+ require 'rubygems'
5
+ require 'spec/rake/spectask'
6
+
7
+ task :default => :spec
8
+
9
+ desc "Run all specs"
10
+ Spec::Rake::SpecTask.new do |t|
11
+ t.spec_files = FileList['spec/**/*_spec.rb']
12
+ t.spec_opts = ['--options', 'spec/spec.opts']
13
+ end
14
+
15
+ begin
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |s|
18
+ s.name = 'dataset'
19
+ s.summary = 'A simple API for creating and finding sets of data in your database, built on ActiveRecord.'
20
+ s.email = 'adam@thewilliams.ws'
21
+ s.files = FileList["[A-Z]*", "{lib,tasks}/**/*", "plugit/descriptor.rb"].exclude("tmp")
22
+ s.require_paths = ["lib", "tasks"]
23
+ s.add_dependency('activesupport', '>= 2.3.0')
24
+ s.add_dependency('activerecord', '>= 2.3.0')
25
+ s.homepage = "http://github.com/aiwilliams/dataset"
26
+ s.description = s.summary
27
+ s.authors = ['Adam Williams']
28
+ end
29
+ rescue LoadError
30
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
31
+ end
data/TODO ADDED
@@ -0,0 +1,15 @@
1
+ take any instance variables already in context and make them available to dataset blocks - this is for nested describes
2
+ I'm not sure about this one. It can be very frustrating to lose context of when the state of an iv is modified.
3
+
4
+ add ability to clear the database (some tests wanted to guarantee a clear db)
5
+ This is acheived with "dataset {}"
6
+
7
+ clear database completely at beginning of session, only tables where data was created within a session??
8
+
9
+ clear all dumps on new run of tests
10
+ be sure we are capturing a dataset if it has already be captured before during a run
11
+ describe what happens when someone has a fixtures file - they get loaded after our datasets, thereby causing all the data in the table of the fixture file (like things.yml) to be deleted - the fixtures are then loaded
12
+ look into truncating database instead individual table deletes
13
+ allow configuration of dataset
14
+ * permatable / global scope
15
+ re-evaluation location of some tests that depend on TestCase in non-test/unit tests
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 1
3
+ :major: 1
4
+ :minor: 3
data/lib/dataset.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'activesupport'
2
+ require 'activerecord'
3
+
4
+ require 'dataset/version'
5
+ require 'dataset/instance_methods'
6
+ require 'dataset/base'
7
+ require 'dataset/database/base'
8
+ require 'dataset/database/mysql'
9
+ require 'dataset/database/sqlite3'
10
+ require 'dataset/database/postgresql'
11
+ require 'dataset/collection'
12
+ require 'dataset/load'
13
+ require 'dataset/resolver'
14
+ require 'dataset/session'
15
+ require 'dataset/session_binding'
16
+ require 'dataset/record/meta'
17
+ require 'dataset/record/fixture'
18
+ require 'dataset/record/model'
19
+
20
+ # == Quick Start
21
+ #
22
+ # Write a test. If you want some data in your database, create a dataset.
23
+ # Start simple.
24
+ #
25
+ # describe States do
26
+ # dataset do
27
+ # [%w(Colorado CO), %w(North\ Carolina NC), %w(South\ Carolina SC)].each do |name,abbrev|
28
+ # create_record :state, abbrev.downcase, :name => name, :abbrev => abbrev
29
+ # end
30
+ # end
31
+ #
32
+ # it 'should have an abbreviated name'
33
+ # states(:nc).abbrev.should be('NC')
34
+ # end
35
+ #
36
+ # it 'should have a name'
37
+ # states(:nc).name.should be('North Carolin')
38
+ # end
39
+ # end
40
+ #
41
+ # Notice that you won't be using _find_id_ or _find_model_ in your tests. You
42
+ # use methods like _states_ and _state_id_, as in the example above.
43
+ #
44
+ # When you find that you're seeing patterns in the data you are creating, pull it into a class.
45
+ #
46
+ # spec/datasets/states.rb
47
+ # class StatesDataset < Dataset::Base
48
+ # def load
49
+ # # create useful data
50
+ # end
51
+ # end
52
+ #
53
+ # spec/models/state.rb
54
+ # describe State do
55
+ # dataset :states
56
+ # end
57
+ #
58
+ # == Installation
59
+ #
60
+ # Dataset is installed into your testing environment by requiring the library,
61
+ # then including it into the class that will be the context of your test
62
+ # methods.
63
+ #
64
+ # require 'dataset'
65
+ # class Test::Unit::TestCase
66
+ # include Dataset
67
+ # datasets_directory "#{RAILS_ROOT}/test/datasets"
68
+ # end
69
+ #
70
+ # Note that should you desire your Dataset::Base subclasses be
71
+ # auto-discovered, you can set the _datasets_directory_.
72
+ #
73
+ module Dataset
74
+ def self.included(test_context) # :nodoc:
75
+ if test_context.name =~ /World\Z/
76
+ require 'dataset/extensions/cucumber'
77
+ elsif test_context.name =~ /TestCase\Z/
78
+ require 'dataset/extensions/test_unit'
79
+ elsif test_context.name =~ /ExampleGroup\Z/
80
+ require 'dataset/extensions/rspec'
81
+ else
82
+ raise "I don't understand your test framework"
83
+ end
84
+
85
+ test_context.extend ContextClassMethods
86
+ end
87
+
88
+ # Methods that are added to the class that Dataset is included in (the test
89
+ # context class).
90
+ #
91
+ module ContextClassMethods
92
+ def self.extended(context_class) # :nodoc:
93
+ context_class.module_eval do
94
+ include InstanceMethods
95
+ superclass_delegating_accessor :dataset_session
96
+ end
97
+ end
98
+
99
+ mattr_accessor :datasets_database_dump_path
100
+ self.datasets_database_dump_path = File.expand_path(RAILS_ROOT + '/tmp/dataset') if defined?(RAILS_ROOT)
101
+
102
+ # Replaces the default Dataset::Resolver with one that will look for
103
+ # dataset class definitions in the specified directory. Captures of the
104
+ # database will be stored in a subdirectory 'tmp' (see
105
+ # Dataset::Database::Base).
106
+ def datasets_directory(path)
107
+ Dataset::Resolver.default = Dataset::DirectoryResolver.new(path)
108
+ Dataset::ContextClassMethods.datasets_database_dump_path = File.join(path, '/tmp/dataset')
109
+ end
110
+
111
+ def add_dataset(*datasets, &block) # :nodoc:
112
+ dataset_session = dataset_session_in_hierarchy
113
+ datasets.each { |dataset| dataset_session.add_dataset(self, dataset) }
114
+ dataset_session.add_dataset(self, Class.new(Dataset::Block) {
115
+ define_method :doload, block
116
+ }) unless block.nil?
117
+ end
118
+
119
+ def dataset_session_in_hierarchy # :nodoc:
120
+ self.dataset_session ||= begin
121
+ database_spec = ActiveRecord::Base.configurations['test'].with_indifferent_access
122
+ database_class = Dataset::Database.const_get(database_spec[:adapter].classify)
123
+ database = database_class.new(database_spec, Dataset::ContextClassMethods.datasets_database_dump_path)
124
+ Dataset::Session.new(database)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,157 @@
1
+ module Dataset
2
+
3
+ # The superclass of your Dataset classes.
4
+ #
5
+ # It is recommended that you create a dataset using the Dataset::Block
6
+ # method first, then grow into using classes as you recognize patterns in
7
+ # your test data creation. This will help you to keep simple things simple.
8
+ #
9
+ class Base
10
+ class << self
11
+ # Allows a subclass to define helper methods that should be made
12
+ # available to instances of this dataset, to datasets that use this
13
+ # dataset, and to tests that use this dataset.
14
+ #
15
+ # This feature is great for providing any kind of method that would help
16
+ # test the code around the data your dataset creates. Be careful,
17
+ # though, to keep from adding business logic to these methods! That
18
+ # belongs in your production code.
19
+ #
20
+ def helpers(&method_definitions)
21
+ @helper_methods ||= begin
22
+ mod = Module.new
23
+ include mod
24
+ mod
25
+ end
26
+ @helper_methods.module_eval &method_definitions
27
+ end
28
+
29
+ def helper_methods # :nodoc:
30
+ @helper_methods
31
+ end
32
+
33
+ # Allows a subsclass to declare which datasets it uses.
34
+ #
35
+ # Dataset is designed to promote 'design by composition', rather than
36
+ # 'design by inheritance'. You should not use class hiearchies to share
37
+ # data and code in your datasets. Instead, you can write something like
38
+ # this:
39
+ #
40
+ # class PeopleDataset < Dataset::Base; end
41
+ # class DepartmentsDataset < Dataset::Base; end
42
+ # class OrganizationsDataset < Dataset::Base
43
+ # uses :people, :departments
44
+ # end
45
+ #
46
+ # When the OrganizationsDataset is loaded, it will have all the data
47
+ # from the datasets is uses, as well as all of the helper methods
48
+ # defined by those datasets.
49
+ #
50
+ # When a dataset uses other datasets, and those datasets themselves use
51
+ # datasets, things will be loaded in the order of dependency you would
52
+ # expect:
53
+ #
54
+ # C uses B
55
+ # A uses C
56
+ # B, C, A is the load order
57
+ #
58
+ def uses(*datasets)
59
+ @used_datasets = datasets
60
+ end
61
+
62
+ def used_datasets # :nodoc:
63
+ @used_datasets
64
+ end
65
+ end
66
+
67
+ # Invoked once before a collection of tests is run. If you use a dataset
68
+ # in multiple test classes, it will be called once for each of them -
69
+ # remember that the database will be cleared at the beginning of running a
70
+ # 'suite' or 'group' of tests, unless you are using nested contexts (as in
71
+ # nested describe blocks in RSpec).
72
+ #
73
+ # Override this method in your subclasses.
74
+ #
75
+ def load; end
76
+ end
77
+
78
+ # The easiest way to create some data before a suite of tests is run is by
79
+ # using a Dataset::Block. An example works wonders:
80
+ #
81
+ # class PeopleTest < Test::Unit::TestCase
82
+ # dataset do
83
+ # create_record :person, :billy, :name => 'Billy'
84
+ # end
85
+ #
86
+ # def test_name
87
+ # assert_equal 'Billy', people(:billy).name
88
+ # end
89
+ # end
90
+ #
91
+ # The database will be cleared and billy will be inserted once before
92
+ # running each of the tests within a transaction. All the normal transaction
93
+ # fixtures stuff will still work.
94
+ #
95
+ # One of the great features of Dataset, at least when things get really
96
+ # interesting in your data needs, is that nested contexts will be additive.
97
+ # Consider this:
98
+ #
99
+ # describe Something do
100
+ # dataset :a => Dataset :a is loaded (at the right time)
101
+ #
102
+ # it 'should whatever'
103
+ # end
104
+ #
105
+ # describe More do
106
+ # dataset :b => Dataset :b is loaded. :a data is still there
107
+ #
108
+ # it 'should'
109
+ # end
110
+ # end
111
+ #
112
+ # describe Another do => Database is restored to :a, without re-running :a logic
113
+ # it 'should'
114
+ # end
115
+ # end
116
+ # end
117
+ #
118
+ # == Instance Variables
119
+ #
120
+ # You may also assign instance variables in a dataset block, and they will
121
+ # be available to your test methods. You have to be careful with this in a
122
+ # similar way that you must with an RSpec before :all block. Since the
123
+ # instance variables are pointing to the same instances accross all tests,
124
+ # things can get weird if you intend to change their state. It's best use is
125
+ # for loading objects that you want to read a lot without loading over and
126
+ # over again for each test.
127
+ #
128
+ # == Building on Other Datasets
129
+ #
130
+ # You may pass any number of Dataset::Base subclasses - or better yet, their
131
+ # names - to the dataset method. When you use a block, this adds a lot of
132
+ # clarity:
133
+ #
134
+ # class PersonTest < Test::Unit::TestCase
135
+ # dataset :organization, :people do
136
+ # id = create_record :person, :second_admin, :name => 'Admin Three'
137
+ # create_record :organization_administratorship, :organization_id => organization_id(:first_bank), :person_id => id
138
+ # end
139
+ #
140
+ # def test_admins
141
+ # assert organizations(:first_bank).admins.include?(people(:second_admin))
142
+ # end
143
+ # end
144
+ #
145
+ # == Reusing a Dataset
146
+ #
147
+ # When you need to go beyond the block, create a Dataset::Base subclass!
148
+ class Block < Base
149
+ include Dataset::InstanceMethods
150
+
151
+ def load # :nodoc:
152
+ dataset_session_binding.install_block_variables(self)
153
+ doload
154
+ dataset_session_binding.copy_block_variables(self)
155
+ end
156
+ end
157
+ end