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.
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +55 -0
- data/README.md +99 -0
- data/Rakefile +13 -0
- data/lib/spectifly/sequel/builder.rb +30 -0
- data/lib/spectifly/sequel/config.rb +53 -0
- data/lib/spectifly/sequel/erb/create_association_tables.erb +4 -0
- data/lib/spectifly/sequel/erb/create_table.erb +9 -0
- data/lib/spectifly/sequel/erb/field/many_to_many_association.erb +9 -0
- data/lib/spectifly/sequel/erb/field/multiple_value_simple_type_field.erb +8 -0
- data/lib/spectifly/sequel/erb/field/single_value_simple_type_field.erb +1 -0
- data/lib/spectifly/sequel/erb/new_migration.erb +6 -0
- data/lib/spectifly/sequel/field.rb +22 -0
- data/lib/spectifly/sequel/field_for_migration.rb +45 -0
- data/lib/spectifly/sequel/migration_generator.rb +39 -0
- data/lib/spectifly/sequel/model.rb +47 -0
- data/lib/spectifly/sequel/relationship/base.rb +14 -0
- data/lib/spectifly/sequel/relationship/belongs_to.rb +17 -0
- data/lib/spectifly/sequel/relationship/belongs_to_many.rb +8 -0
- data/lib/spectifly/sequel/relationship/has_a.rb +8 -0
- data/lib/spectifly/sequel/relationship/has_and_belongs_to_many.rb +21 -0
- data/lib/spectifly/sequel/relationship/has_many.rb +12 -0
- data/lib/spectifly/sequel/relationship/has_one.rb +8 -0
- data/lib/spectifly/sequel/relationship/one_to_one.rb +17 -0
- data/lib/spectifly/sequel/tasks.rb +2 -0
- data/lib/spectifly/sequel/types.rb +22 -0
- data/lib/spectifly/sequel/version.rb +5 -0
- data/lib/spectifly/sequel.rb +24 -0
- data/lib/tasks/spectifly-sequel.rake +12 -0
- data/spec/expectations/animal_multiple_value_string_field.migration +8 -0
- data/spec/expectations/cow.migration +19 -0
- data/spec/expectations/group.migration +10 -0
- data/spec/expectations/individual.migration +21 -0
- data/spec/fixtures/config_file.yml +7 -0
- data/spec/fixtures/cow.entity +34 -0
- data/spec/fixtures/group.entity +17 -0
- data/spec/fixtures/individual.entity +37 -0
- data/spec/fixtures/invalid_config_file.yml +2 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/spectifly/sequel/builder_spec.rb +27 -0
- data/spec/spectifly/sequel/config_spec.rb +60 -0
- data/spec/spectifly/sequel/field_spec.rb +61 -0
- data/spec/spectifly/sequel/migration_generator_spec.rb +45 -0
- data/spec/spectifly/sequel/model_spec.rb +17 -0
- data/spec/spectifly/sequel/types_spec.rb +10 -0
- data/spec/tmp/migrations/README +2 -0
- data/spectifly-sequel.gemspec +30 -0
- metadata +253 -0
@@ -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,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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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,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
|