be9-dataset 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/CHANGELOG +63 -0
  2. data/LICENSE +19 -0
  3. data/README +111 -0
  4. data/Rakefile +32 -0
  5. data/TODO +15 -0
  6. data/VERSION.yml +5 -0
  7. data/lib/dataset.rb +129 -0
  8. data/lib/dataset/base.rb +157 -0
  9. data/lib/dataset/collection.rb +19 -0
  10. data/lib/dataset/database/base.rb +43 -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/heirarchy.rb +65 -0
  21. data/lib/dataset/record/meta.rb +30 -0
  22. data/lib/dataset/record/model.rb +50 -0
  23. data/lib/dataset/resolver.rb +110 -0
  24. data/lib/dataset/session.rb +51 -0
  25. data/lib/dataset/session_binding.rb +319 -0
  26. data/lib/dataset/version.rb +9 -0
  27. data/plugit/descriptor.rb +25 -0
  28. data/spec/dataset/cucumber_spec.rb +54 -0
  29. data/spec/dataset/database/base_spec.rb +21 -0
  30. data/spec/dataset/record/heirarchy_spec.rb +14 -0
  31. data/spec/dataset/resolver_spec.rb +110 -0
  32. data/spec/dataset/rspec_spec.rb +133 -0
  33. data/spec/dataset/session_binding_spec.rb +203 -0
  34. data/spec/dataset/session_spec.rb +299 -0
  35. data/spec/dataset/test_unit_spec.rb +210 -0
  36. data/spec/fixtures/datasets/constant_not_defined.rb +0 -0
  37. data/spec/fixtures/datasets/ending_with_dataset.rb +2 -0
  38. data/spec/fixtures/datasets/exact_name.rb +2 -0
  39. data/spec/fixtures/datasets/not_a_dataset_base.rb +2 -0
  40. data/spec/fixtures/more_datasets/in_another_directory.rb +2 -0
  41. data/spec/models.rb +18 -0
  42. data/spec/schema.rb +26 -0
  43. data/spec/spec_helper.rb +47 -0
  44. data/spec/stubs/mini_rails.rb +18 -0
  45. data/spec/stubs/test_help.rb +1 -0
  46. data/tasks/dataset.rake +19 -0
  47. metadata +163 -0
@@ -0,0 +1,19 @@
1
+ require 'set'
2
+
3
+ module Dataset
4
+ class Collection < Array # :nodoc:
5
+ def initialize(parent)
6
+ concat parent
7
+ end
8
+
9
+ def <<(dataset)
10
+ super
11
+ uniq!
12
+ self
13
+ end
14
+
15
+ def subset?(other)
16
+ Set.new(self).subset?(Set.new(other))
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ require 'fileutils'
2
+
3
+ module Dataset
4
+ module Database # :nodoc:
5
+
6
+ # Provides Dataset a way to clear, dump and load databases.
7
+ class Base
8
+ include FileUtils
9
+
10
+ def clear
11
+ connection = ActiveRecord::Base.connection
12
+ ActiveRecord::Base.silence do
13
+ connection.tables.each do |table_name|
14
+ connection.delete "DELETE FROM #{connection.quote_table_name(table_name)}",
15
+ "Dataset::Database#clear" unless table_name == ActiveRecord::Migrator.schema_migrations_table_name
16
+ end
17
+ end
18
+ end
19
+
20
+ def record_heirarchy(record_class)
21
+ base_class = record_class.base_class
22
+ record_heirarchies[base_class] ||= Dataset::Record::Heirarchy.new(base_class)
23
+ end
24
+
25
+ def record_meta(record_class)
26
+ record_metas[record_class] ||= begin
27
+ heirarchy = record_heirarchy(record_class)
28
+ heirarchy.update(record_class)
29
+ Dataset::Record::Meta.new(heirarchy, record_class)
30
+ end
31
+ end
32
+
33
+ protected
34
+ def record_metas
35
+ @record_metas ||= Hash.new
36
+ end
37
+
38
+ def record_heirarchies
39
+ @record_heirarchies ||= Hash.new
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,34 @@
1
+ module Dataset
2
+ module Database # :nodoc:
3
+
4
+ # The interface to a mySQL database, this will capture by creating a dump
5
+ # file and restore by loading one of the same.
6
+ #
7
+ class Mysql < Base
8
+ def initialize(database_spec, storage_path)
9
+ @database = database_spec[:database]
10
+ @username = database_spec[:username]
11
+ @password = database_spec[:password]
12
+ @storage_path = storage_path
13
+ FileUtils.mkdir_p(@storage_path)
14
+ end
15
+
16
+ def capture(datasets)
17
+ return if datasets.nil? || datasets.empty?
18
+ `mysqldump -u #{@username} --password=#{@password} --compact --extended-insert --no-create-db --add-drop-table --quick --quote-names #{@database} > #{storage_path(datasets)}`
19
+ end
20
+
21
+ def restore(datasets)
22
+ store = storage_path(datasets)
23
+ if File.file?(store)
24
+ `mysql -u #{@username} --password=#{@password} --database=#{@database} < #{store}`
25
+ true
26
+ end
27
+ end
28
+
29
+ def storage_path(datasets)
30
+ "#{@storage_path}/#{datasets.collect {|c| c.__id__}.join('_')}.sql"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ module Dataset
2
+ module Database # :nodoc:
3
+
4
+ # The interface to a PostgreSQL database, this will capture by creating a dump
5
+ # file and restore by loading one of the same.
6
+ #
7
+ class Postgresql < Base
8
+ def initialize(database_spec, storage_path)
9
+ @database = database_spec[:database]
10
+ @username = database_spec[:username]
11
+ @password = database_spec[:password]
12
+ @storage_path = storage_path
13
+ FileUtils.mkdir_p(@storage_path)
14
+ end
15
+
16
+ def capture(datasets)
17
+ return if datasets.nil? || datasets.empty?
18
+ `pg_dump -c #{@database} > #{storage_path(datasets)}`
19
+ end
20
+
21
+ def restore(datasets)
22
+ store = storage_path(datasets)
23
+ if File.file?(store)
24
+ `psql -U #{@username} -p #{@password} -e #{@database} < #{store}`
25
+ true
26
+ end
27
+ end
28
+
29
+ def storage_path(datasets)
30
+ "#{@storage_path}/#{datasets.collect {|c| c.__id__}.join('_')}.sql"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
1
+ module Dataset
2
+ module Database # :nodoc:
3
+
4
+ # The interface to a sqlite3 database, this will capture by copying the db
5
+ # file and restore by replacing and reconnecting to one of the same.
6
+ #
7
+ class Sqlite3 < Base
8
+ def initialize(database_spec, storage_path)
9
+ @database_path, @storage_path = database_spec[:database], storage_path
10
+ FileUtils.mkdir_p(@storage_path)
11
+ end
12
+
13
+ def capture(datasets)
14
+ return if datasets.nil? || datasets.empty?
15
+ cp @database_path, storage_path(datasets)
16
+ end
17
+
18
+ def restore(datasets)
19
+ store = storage_path(datasets)
20
+ if File.file?(store)
21
+ mv store, @database_path
22
+ ActiveRecord::Base.establish_connection 'test'
23
+ true
24
+ end
25
+ end
26
+
27
+ def storage_path(datasets)
28
+ "#{@storage_path}/#{datasets.collect {|c| c.__id__}.join('_')}.sqlite3.db"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ module Dataset
2
+ module Extensions # :nodoc:
3
+
4
+ module CucumberWorld # :nodoc:
5
+ def dataset(*datasets, &block)
6
+ add_dataset(*datasets, &block)
7
+
8
+ load = nil
9
+ $__cucumber_toplevel.Before do
10
+ load = dataset_session.load_datasets_for(self.class)
11
+ extend_from_dataset_load(load)
12
+ end
13
+ # Makes sure the datasets are reloaded after each scenario
14
+ Cucumber::Rails.use_transactional_fixtures
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ Cucumber::Rails::World.extend Dataset::Extensions::CucumberWorld
@@ -0,0 +1,21 @@
1
+ module Dataset
2
+ module Extensions # :nodoc:
3
+
4
+ module RSpecExampleGroup # :nodoc:
5
+ def dataset(*datasets, &block)
6
+ add_dataset(*datasets, &block)
7
+
8
+ load = nil
9
+ before(:all) do
10
+ load = dataset_session.load_datasets_for(self.class)
11
+ extend_from_dataset_load(load)
12
+ end
13
+ before(:each) do
14
+ extend_from_dataset_load(load)
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
21
+ Spec::Example::ExampleGroup.extend Dataset::Extensions::RSpecExampleGroup
@@ -0,0 +1,60 @@
1
+ module Dataset
2
+ class TestSuite # :nodoc:
3
+ def initialize(suite, test_class)
4
+ @suite = suite
5
+ @test_class = test_class
6
+ end
7
+
8
+ def dataset_session
9
+ @test_class.dataset_session
10
+ end
11
+
12
+ def run(result, &progress_block)
13
+ if dataset_session
14
+ load = dataset_session.load_datasets_for(@test_class)
15
+ @suite.tests.each { |e| e.extend_from_dataset_load(load) }
16
+ end
17
+ @suite.run(result, &progress_block)
18
+ end
19
+
20
+ def method_missing(method_symbol, *args)
21
+ @suite.send(method_symbol, *args)
22
+ end
23
+ end
24
+
25
+ module Extensions # :nodoc:
26
+
27
+ module TestUnitTestCase # :nodoc:
28
+ def self.extended(test_case)
29
+ class << test_case
30
+ alias_method_chain :suite, :dataset
31
+ end
32
+ end
33
+
34
+ def suite_with_dataset
35
+ Dataset::TestSuite.new(suite_without_dataset, self)
36
+ end
37
+
38
+ def dataset(*datasets, &block)
39
+ add_dataset(*datasets, &block)
40
+
41
+ # Unfortunately, if we have rspec loaded, TestCase has it's suite method
42
+ # modified for the test/unit runners, but uses a different mechanism to
43
+ # collect tests if the rspec runners are used.
44
+ if included_modules.find {|m| m.name =~ /ExampleMethods\Z/}
45
+ load = nil
46
+ before(:all) do
47
+ load = dataset_session.load_datasets_for(self.class)
48
+ extend_from_dataset_load(load)
49
+ end
50
+ before(:each) do
51
+ extend_from_dataset_load(load)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+
60
+ Test::Unit::TestCase.extend Dataset::Extensions::TestUnitTestCase
@@ -0,0 +1,10 @@
1
+ module Dataset
2
+ module InstanceMethods # :nodoc:
3
+ def extend_from_dataset_load(load)
4
+ load.dataset_binding.install_block_variables(self)
5
+ self.extend load.dataset_binding.record_methods
6
+ self.extend load.dataset_binding.model_finders
7
+ self.extend load.helper_methods
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ module Dataset
2
+ class Load # :nodoc:
3
+ attr_reader :datasets, :dataset_binding, :helper_methods
4
+
5
+ def initialize(datasets, parent_binding)
6
+ @datasets = datasets
7
+ @dataset_binding = SessionBinding.new(parent_binding)
8
+ @helper_methods = Module.new
9
+ end
10
+
11
+ def execute(loaded_datasets, dataset_resolver)
12
+ (datasets - loaded_datasets).each do |dataset|
13
+ instance = dataset.new
14
+ instance.extend dataset_binding.record_methods
15
+ instance.extend dataset_binding.model_finders
16
+ used_datasets(dataset, dataset_resolver).each do |ds|
17
+ next unless ds.helper_methods
18
+ instance.extend ds.helper_methods
19
+ helper_methods.module_eval do
20
+ include ds.helper_methods
21
+ end
22
+ end
23
+ instance.load
24
+ end
25
+ end
26
+
27
+ def used_datasets(dataset, dataset_resolver, collector = [])
28
+ dataset.used_datasets.each do |used|
29
+ ds = dataset_resolver.resolve(used)
30
+ used_datasets(ds, dataset_resolver, collector)
31
+ collector << ds
32
+ end if dataset.used_datasets
33
+ collector << dataset
34
+ collector.uniq
35
+ end
36
+ end
37
+
38
+ class Reload # :nodoc:
39
+ attr_reader :dataset_binding, :load
40
+ delegate :datasets, :helper_methods, :to => :load
41
+
42
+ def initialize(load)
43
+ @load = load
44
+ @dataset_binding = SessionBinding.new(@load.dataset_binding)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,73 @@
1
+ require 'active_record/fixtures'
2
+
3
+ module Dataset
4
+ module Record # :nodoc:
5
+
6
+ class Fixture # :nodoc:
7
+ attr_reader :meta, :symbolic_name, :session_binding
8
+
9
+ def initialize(meta, attributes, symbolic_name, session_binding)
10
+ @meta = meta
11
+ @attributes = attributes.stringify_keys
12
+ @symbolic_name = symbolic_name || object_id
13
+ @session_binding = session_binding
14
+
15
+ install_default_attributes!
16
+ end
17
+
18
+ def create
19
+ record_class.connection.insert_fixture to_fixture, meta.table_name
20
+ id
21
+ end
22
+
23
+ def id
24
+ @attributes['id']
25
+ end
26
+
27
+ def record_class
28
+ meta.record_class
29
+ end
30
+
31
+ def to_fixture
32
+ ::Fixture.new(to_hash, meta.class_name)
33
+ end
34
+
35
+ def to_hash
36
+ hash = @attributes.dup
37
+ hash[meta.inheritance_column] = meta.sti_name if meta.inheriting_record?
38
+ record_class.reflections.each do |name, reflection|
39
+ name = name.to_s
40
+ add_reflection_attributes(hash, name, reflection) if hash[name]
41
+ end
42
+ hash
43
+ end
44
+
45
+ def install_default_attributes!
46
+ @attributes['id'] ||= symbolic_name.to_s.hash.abs
47
+ install_timestamps!
48
+ end
49
+
50
+ def install_timestamps!
51
+ meta.timestamp_columns.each do |column|
52
+ @attributes[column.name] = now(column) unless @attributes.key?(column.name)
53
+ end
54
+ end
55
+
56
+ def now(column)
57
+ (ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now).to_s(:db)
58
+ end
59
+
60
+ private
61
+ def add_reflection_attributes(hash, name, reflection)
62
+ value = hash.delete(name)
63
+ case value
64
+ when Symbol
65
+ hash[reflection.primary_key_name] = session_binding.find_id(reflection.klass, value)
66
+ else
67
+ hash[reflection.primary_key_name] = value
68
+ end
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,65 @@
1
+ module Dataset
2
+ module Record # :nodoc:
3
+
4
+ class Heirarchy # :nodoc:
5
+ attr_reader :base_class, :class_name, :columns, :table_name
6
+
7
+ delegate :inheritance_column, :to => :base_class
8
+
9
+ def initialize(base_class)
10
+ @base_class = base_class
11
+ @class_name = base_class.name
12
+ @table_name = base_class.table_name
13
+ @columns = base_class.columns
14
+ end
15
+
16
+ def id_cache_key
17
+ @id_cache_key ||= table_name
18
+ end
19
+
20
+ def id_finder_names
21
+ @id_finder_names ||= [id_finder_name(base_class)]
22
+ end
23
+
24
+ def model_finder_names
25
+ @model_finder_names ||= [model_finder_name(base_class)]
26
+ end
27
+
28
+ def to_s
29
+ "#<Heirarchy: #{table_name}>"
30
+ end
31
+
32
+ def update(record_class)
33
+ record_class.ancestors.each do |c|
34
+ finder_name = model_finder_name(c)
35
+ unless model_finder_names.include?(finder_name)
36
+ model_finder_names << finder_name
37
+ id_finder_names << id_finder_name(c)
38
+ end
39
+ end
40
+ end
41
+
42
+ def finder_name(klass)
43
+ klass.name.underscore.gsub('/', '_').sub(/^(\w)_/, '\1').gsub(/_(\w)_/, '_\1')
44
+ end
45
+
46
+ def id_finder_name(klass)
47
+ "#{finder_name(klass)}_id".to_sym
48
+ end
49
+
50
+ def model_finder_name(klass)
51
+ finder_name(klass).pluralize.to_sym
52
+ end
53
+
54
+ def timestamp_columns
55
+ @timestamp_columns ||= begin
56
+ timestamps = %w(created_at created_on updated_at updated_on)
57
+ columns.select do |column|
58
+ timestamps.include?(column.name)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end