spectifly-sequel 0.0.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 (50) hide show
  1. data/.gitignore +3 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +55 -0
  5. data/README.md +99 -0
  6. data/Rakefile +13 -0
  7. data/lib/spectifly/sequel/builder.rb +30 -0
  8. data/lib/spectifly/sequel/config.rb +53 -0
  9. data/lib/spectifly/sequel/erb/create_association_tables.erb +4 -0
  10. data/lib/spectifly/sequel/erb/create_table.erb +9 -0
  11. data/lib/spectifly/sequel/erb/field/many_to_many_association.erb +9 -0
  12. data/lib/spectifly/sequel/erb/field/multiple_value_simple_type_field.erb +8 -0
  13. data/lib/spectifly/sequel/erb/field/single_value_simple_type_field.erb +1 -0
  14. data/lib/spectifly/sequel/erb/new_migration.erb +6 -0
  15. data/lib/spectifly/sequel/field.rb +22 -0
  16. data/lib/spectifly/sequel/field_for_migration.rb +45 -0
  17. data/lib/spectifly/sequel/migration_generator.rb +39 -0
  18. data/lib/spectifly/sequel/model.rb +47 -0
  19. data/lib/spectifly/sequel/relationship/base.rb +14 -0
  20. data/lib/spectifly/sequel/relationship/belongs_to.rb +17 -0
  21. data/lib/spectifly/sequel/relationship/belongs_to_many.rb +8 -0
  22. data/lib/spectifly/sequel/relationship/has_a.rb +8 -0
  23. data/lib/spectifly/sequel/relationship/has_and_belongs_to_many.rb +21 -0
  24. data/lib/spectifly/sequel/relationship/has_many.rb +12 -0
  25. data/lib/spectifly/sequel/relationship/has_one.rb +8 -0
  26. data/lib/spectifly/sequel/relationship/one_to_one.rb +17 -0
  27. data/lib/spectifly/sequel/tasks.rb +2 -0
  28. data/lib/spectifly/sequel/types.rb +22 -0
  29. data/lib/spectifly/sequel/version.rb +5 -0
  30. data/lib/spectifly/sequel.rb +24 -0
  31. data/lib/tasks/spectifly-sequel.rake +12 -0
  32. data/spec/expectations/animal_multiple_value_string_field.migration +8 -0
  33. data/spec/expectations/cow.migration +19 -0
  34. data/spec/expectations/group.migration +10 -0
  35. data/spec/expectations/individual.migration +21 -0
  36. data/spec/fixtures/config_file.yml +7 -0
  37. data/spec/fixtures/cow.entity +34 -0
  38. data/spec/fixtures/group.entity +17 -0
  39. data/spec/fixtures/individual.entity +37 -0
  40. data/spec/fixtures/invalid_config_file.yml +2 -0
  41. data/spec/spec_helper.rb +29 -0
  42. data/spec/spectifly/sequel/builder_spec.rb +27 -0
  43. data/spec/spectifly/sequel/config_spec.rb +60 -0
  44. data/spec/spectifly/sequel/field_spec.rb +61 -0
  45. data/spec/spectifly/sequel/migration_generator_spec.rb +45 -0
  46. data/spec/spectifly/sequel/model_spec.rb +17 -0
  47. data/spec/spectifly/sequel/types_spec.rb +10 -0
  48. data/spec/tmp/migrations/README +2 -0
  49. data/spectifly-sequel.gemspec +30 -0
  50. metadata +253 -0
@@ -0,0 +1,8 @@
1
+
2
+ create_table(:individuals_animals) do
3
+ primary_key :id
4
+ foreign_key :individual_id, :individuals, :null => false
5
+ String :name, :null => false
6
+ end
7
+
8
+ add_index [:individual_id, :name], :individuals_animals, :unique => true
@@ -0,0 +1,19 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:kine) do
4
+ primary_key :id
5
+ String :name, :null => false
6
+ Integer :age
7
+ Numeric :cost, :null => false
8
+ String :color
9
+ foreign_key :owner_id, :individuals
10
+ end
11
+
12
+ create_table(:calves_kine) do
13
+ foreign_key :cow_id, :kine, :null => false
14
+ foreign_key :calf_id, :kine, :null => false
15
+ end
16
+
17
+ add_index [:cow_id, :calf_id], :calves_kine, :unique => true
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:groups) do
4
+ primary_key :id
5
+ String :name, :null => false, :unique => true
6
+ Boolean :associated_organization
7
+ foreign_key :leader_id, :individuals
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ Sequel.migration do
2
+ change do
3
+ create_table(:individuals) do
4
+ primary_key :id
5
+ String :name, :null => false
6
+ String :ssn, :null => false, :unique => true
7
+ Integer :age
8
+ Boolean :happy
9
+ DateTime :created_at
10
+ foreign_key :group_id, :groups, :null => false
11
+ end
12
+
13
+ create_table(:individuals_favorites) do
14
+ primary_key :id
15
+ foreign_key :individual_id, :individuals, :null => false
16
+ String :name, :null => false
17
+ end
18
+
19
+ add_index [:individual_id, :name], :individuals_favorites, :unique => true
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ Spectifly: &default
2
+ entity_definition_path: ./spec/fixtures/
3
+ Sequel:
4
+ <<: *default
5
+ migration_path: ./spec/tmp/migrations/
6
+ migration_version_type: Integer
7
+
@@ -0,0 +1,34 @@
1
+ Cow:
2
+ Fields:
3
+ Name*:
4
+ Description: My Cow's name
5
+
6
+ Age:
7
+ Type: Integer
8
+
9
+ Cost*:
10
+ Type: Currency
11
+
12
+ Color:
13
+ Valid values:
14
+ - Brown
15
+ - Black
16
+ - Grey
17
+ - White
18
+ - Other
19
+
20
+ Related Entities:
21
+ Has Many:
22
+ Calves:
23
+ Description: Baby cow
24
+ Type: Cow
25
+
26
+ Belongs To Many:
27
+ Cow*:
28
+ Description: Parent cows
29
+ Type: Cow
30
+
31
+ Belongs To:
32
+ Owner:
33
+ Type: Individual
34
+
@@ -0,0 +1,17 @@
1
+ Group:
2
+ Fields:
3
+ Name*:
4
+ Description: A thing
5
+ Validations: must be unique
6
+
7
+ Associated Organization?:
8
+ Description: Is this an organized group or is it hodge-podge?
9
+
10
+ Related Entities:
11
+ Has Many:
12
+ Individuals:
13
+ Type: Individual
14
+
15
+ Has One:
16
+ Leader:
17
+ Type: Individual
@@ -0,0 +1,37 @@
1
+ Individual:
2
+ Description: Person-y thing
3
+ Fields:
4
+ Name*:
5
+ Description: The thing you call the person
6
+
7
+ Ssn*:
8
+ Description: We're assuming individuals are US citizens, I guess.
9
+ Unique: true
10
+
11
+ Age:
12
+ Type: Integer
13
+
14
+ Happy?:
15
+ Description: Is the individual happy?
16
+
17
+ Created At:
18
+ Type: DateTime
19
+
20
+ Favorites:
21
+ Description: Random things the individual likes
22
+ Multiple: true
23
+ Valid Values:
24
+ - Strawberries
25
+ - Monkeys
26
+ - Dogma
27
+ - Creationism
28
+
29
+ Related Entities:
30
+ Belongs To:
31
+ Group*:
32
+ Type: Group
33
+
34
+ Has Many:
35
+ Cows:
36
+ Description: Cows owned by an individual
37
+ Type: Cow
@@ -0,0 +1,2 @@
1
+ entity_definition_path: ./
2
+ migration_path: ../tmp/migrations/
@@ -0,0 +1,29 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'spectifly'
5
+ require_relative '../lib/spectifly/sequel'
6
+
7
+ def spec_path
8
+ File.dirname(__FILE__)
9
+ end
10
+
11
+ def migration_output_path
12
+ File.join(spec_path, 'tmp', 'migrations')
13
+ end
14
+
15
+ def base_expectation_path
16
+ File.join(spec_path, 'expectations')
17
+ end
18
+
19
+ def expectation_path(expectation)
20
+ File.join(base_expectation_path, "#{expectation}.migration")
21
+ end
22
+
23
+ def base_fixture_path
24
+ File.join(spec_path, 'fixtures')
25
+ end
26
+
27
+ def fixture_path(fixture)
28
+ File.join(base_fixture_path, "#{fixture}.entity")
29
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ describe Spectifly::Sequel::Builder do
5
+ describe '.build' do
6
+ it 'works for a simple case' do
7
+ path_builder = Spectifly::Sequel::Builder.from_path(fixture_path('group'))
8
+ migration_path = expectation_path('group')
9
+ migration = path_builder.build
10
+ migration.should == File.read(migration_path)
11
+ end
12
+
13
+ it 'works for simple, multiple simple value associations' do
14
+ path_builder = Spectifly::Sequel::Builder.from_path(fixture_path('individual'))
15
+ migration_path = expectation_path('individual')
16
+ migration = path_builder.build
17
+ migration.should == File.read(migration_path)
18
+ end
19
+
20
+ it 'works for many-to-many associations' do
21
+ path_builder = Spectifly::Sequel::Builder.from_path(fixture_path('cow'))
22
+ migration_path = expectation_path('cow')
23
+ migration = path_builder.build
24
+ migration.should == File.read(migration_path)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spectifly::Sequel do
4
+ let(:path_to_config_file) { File.join(base_fixture_path, 'config_file.yml') }
5
+ let(:path_to_invalid_config_file) { File.join(base_fixture_path, 'invalid_config_file.yml') }
6
+ let(:config_hash) {
7
+ {
8
+ 'migration_path' => 'foo/bar/baz.yml',
9
+ 'entity_definition_path' => 'blah/blah/',
10
+ 'migration_version_type' => 'Timestamp',
11
+ }
12
+ }
13
+ let(:friendly_config_instructions) {
14
+ <<-INSTRUCTIONS
15
+ Please format config files in the following manner:
16
+ ``- begin YAML
17
+ Sequel:
18
+ Spectifly:
19
+ migration_path: PATH_TO_MIGRATION_DIRECTORY
20
+ entity_definition_path: PATH_TO_ENTITY_DEFINITION_DIRECTORY
21
+ ``- end YAML
22
+ INSTRUCTIONS
23
+ }
24
+
25
+ it 'should allow config to be read from yaml in a path specified by the user' do
26
+ described_class.configure_with path_to_config_file
27
+ described_class.migration_path.should == './spec/tmp/migrations/'
28
+ described_class.entity_definition_path.should == './spec/fixtures/'
29
+ described_class.migration_version_type.should == 'Integer'
30
+ end
31
+
32
+ it 'should accept a config in hash format' do
33
+ described_class.configure config_hash
34
+ described_class.migration_path.should == 'foo/bar/baz.yml'
35
+ described_class.entity_definition_path.should == 'blah/blah/'
36
+ described_class.migration_version_type.should == 'Timestamp'
37
+ end
38
+
39
+ it 'gives reasonable, instructive error if YAML is misconfigured' do
40
+ lambda {
41
+ described_class.configure_with path_to_invalid_config_file
42
+ }.should raise_error(friendly_config_instructions)
43
+ end
44
+
45
+ it 'gives reasonable, instructive error if either path is not set' do
46
+ described_class.configure({:migration_path => nil, :entity_definition_path => nil, :migration_version_type => 'IDoNotKnow'})
47
+ lambda {
48
+ described_class.migration_path
49
+ }.should raise_error('Spectify::Sequel is not configured properly. "migration_path" must be set via YAML or a hash.')
50
+ lambda {
51
+ described_class.entity_definition_path
52
+ }.should raise_error('Spectify::Sequel is not configured properly. "entity_definition_path" must be set via YAML or a hash.')
53
+ described_class.migration_version_type.should == 'Timestamp'
54
+ end
55
+
56
+ it 'defaults to Timestamp migration versions if not set' do
57
+ described_class.configure({:migration_version_type => nil})
58
+ described_class.migration_version_type.should == 'Timestamp'
59
+ end
60
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require_relative '../../../lib/spectifly/sequel/field'
3
+
4
+ describe Spectifly::Sequel::Field do
5
+ describe '#type' do
6
+ it 'defaults to String if no type is given' do
7
+ field = described_class.new('I cannot spell rhubarb')
8
+ field.type.should == 'String'
9
+ end
10
+
11
+ it 'returns Boolean if field has ? token' do
12
+ field = described_class.new('Can I spell misspelled?')
13
+ field.type.should == 'Boolean'
14
+ end
15
+
16
+ it 'returns the base type of an extended type' do
17
+ field = described_class.new('OneMillionDollars', { 'Type' => 'Currency' })
18
+ field.type.should == 'Numeric'
19
+ end
20
+ end
21
+
22
+ describe 'uniqueness restriction' do
23
+ it 'unique should be false by default and there should be no unique restriction' do
24
+ field = described_class.new("Mini me")
25
+ field.should_not be_unique
26
+ field.restrictions.keys.should_not be_include('unique')
27
+ end
28
+
29
+ it 'adds a restriction and returns true for unique? if there is a uniqueness validation' do
30
+ field = described_class.new("Little Snowflake", {"Validations" => "must be unique"})
31
+ field.should be_unique
32
+ field.restrictions.keys.include?('unique').should be_true
33
+ end
34
+
35
+ it 'adds a restriction and returns true for unique? if there is an attribute Unique set to true' do
36
+ field = described_class.new("Little Snowflake", {"Unique" => "true"})
37
+ field.should be_unique
38
+ field.restrictions.keys.include?('unique').should be_true
39
+ end
40
+
41
+ it 'throws an error if the two ways of setting uniqueness contradict each other' do
42
+ lambda {
43
+ field = described_class.new("Little Snowflake?", {"Validations" => "must be unique", "Unique" => false})
44
+ }.should raise_error
45
+ end
46
+ end
47
+
48
+ describe '#for_new_migration' do
49
+ Model = Struct.new(:entity_name, :table_name, :name_as_foreign_key)
50
+ let(:individual_model) { Model.new('individual', 'individuals', 'individual_id') }
51
+ it 'returns a column definition with options for a required field' do
52
+ field = described_class.new('Why am I required*')
53
+ field.for_new_migration(individual_model).should == 'String :why_am_i_required, :null => false'
54
+ end
55
+
56
+ it 'returns a basic association table for a field with multiple native-type values' do
57
+ field = described_class.new('Animals', {'Valid Values' => ['Giraffe', 'Puffin', 'Dog'], 'Multiple' => true})
58
+ field.for_new_migration(individual_model, []).should == File.read(expectation_path('animal_multiple_value_string_field'))
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spectifly::Sequel::MigrationGenerator do
4
+ before :each do
5
+ Spectifly::Sequel.configure_with File.join(base_fixture_path, 'config_file.yml')
6
+ cleanup_files
7
+ end
8
+
9
+ after :each do
10
+ cleanup_files
11
+ end
12
+
13
+ def cleanup_files
14
+ Dir.glob("#{migration_output_path}/*.rb") do |rb_file|
15
+ File.delete(rb_file)
16
+ end
17
+ end
18
+
19
+ it 'reads a Spectifly entity definition file and outputs a migration that can generate a new table for that entity' do
20
+ generator = Spectifly::Sequel::MigrationGenerator.new('individual')
21
+ generator.run!
22
+ expected_migration_path = File.join(migration_output_path, '001_create_individuals.rb')
23
+ File.should exist(expected_migration_path)
24
+ File.read(expected_migration_path).should == File.read(expectation_path('individual'))
25
+ end
26
+
27
+ it 'determines what the next migration version should be' do
28
+ File.open(File.join(migration_output_path, '001_create_cookies.rb'), 'w') { |f| f.write('Hi!') }
29
+
30
+ generator = Spectifly::Sequel::MigrationGenerator.new('individual')
31
+ generator.run!
32
+
33
+ expected_migration_path = File.join(migration_output_path, '002_create_individuals.rb')
34
+ File.should exist(expected_migration_path)
35
+ end
36
+
37
+ it 'uses current datetime if user is assigning timestamps' do
38
+ DateTime.stub(:now).and_return DateTime.new(2013,01,01)
39
+ generator = Spectifly::Sequel::MigrationGenerator.new('individual', migration_output_path, base_fixture_path, 'Timestamp')
40
+ generator.run!
41
+
42
+ expected_migration_path = File.join(migration_output_path, '20130101000000_create_individuals.rb')
43
+ File.should exist(expected_migration_path)
44
+ end
45
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+ require_relative '../../../lib/spectifly/sequel/model'
3
+
4
+ describe Spectifly::Sequel::Model do
5
+ describe '#table_name' do
6
+ def model_for(entity_type)
7
+ entity = Spectifly::Entity.new(fixture_path(entity_type))
8
+ described_class.new(entity, [])
9
+ end
10
+
11
+ it 'pluralizes and tokenizes an entity\'s root' do
12
+ model_for('individual').table_name.should == 'individuals'
13
+
14
+ model_for('cow').table_name.should == 'kine'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+ require_relative '../../../lib/spectifly/sequel/types'
3
+
4
+ describe Spectifly::Sequel::Types do
5
+ describe 'Native types' do
6
+ it 'includes String, DateTime, Integer, Numeric, etc' do
7
+ (%w(String DateTime Integer Numeric) - Spectifly::Sequel::Types::Native).should == []
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,2 @@
1
+ This is here in order to check in the directory structure. Migrations
2
+ generated from tests will end up here.
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'spectifly/sequel/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "spectifly-sequel"
8
+ spec.version = Spectifly::Sequel::VERSION
9
+ spec.authors = ["Maher Hawash","Ravi Gadad", "Laurie Kemmerer", "David Miller"]
10
+ spec.email = ["mhawash@renewfund.com", "ravi@renewfund.com", "laurie@renewfund.com", "dave.miller@renewfund.com"]
11
+ spec.description = %q{An add-on to the Spectifly gem that generates Sequel migrations andd models based on Spectifly}
12
+ spec.summary = %q{A Sequel add-on to Spectifly https://github.com/renewablefunding/spectifly}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "spectifly"
22
+ spec.add_runtime_dependency "sequel"
23
+ spec.add_runtime_dependency "activesupport"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "debugger"
29
+ spec.add_development_dependency "simplecov"
30
+ end