active_schema 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/.autotest +1 -0
  2. data/.document +11 -0
  3. data/.gitignore +48 -0
  4. data/.rspec +3 -0
  5. data/Gemfile +21 -0
  6. data/Gemfile.lock +63 -0
  7. data/LICENSE +20 -0
  8. data/README.textile +80 -0
  9. data/Rakefile +59 -0
  10. data/VERSION +1 -0
  11. data/autotest/discover.rb +1 -0
  12. data/lib/active_schema.rb +30 -0
  13. data/lib/active_schema/active_record/base.rb +46 -0
  14. data/lib/active_schema/associations/by_foreign_key.rb +50 -0
  15. data/lib/active_schema/associations/generator.rb +25 -0
  16. data/lib/active_schema/configuration.rb +20 -0
  17. data/lib/active_schema/feeder.rb +45 -0
  18. data/lib/active_schema/in_advance_feeder.rb +10 -0
  19. data/lib/active_schema/on_the_fly_feeder.rb +26 -0
  20. data/lib/active_schema/schema_feeder.rb +41 -0
  21. data/lib/active_schema/table.rb +30 -0
  22. data/lib/active_schema/table_hub.rb +42 -0
  23. data/lib/active_schema/validations/by_column.rb +41 -0
  24. data/lib/active_schema/validations/by_index.rb +5 -0
  25. data/lib/active_schema/validations/generator.rb +45 -0
  26. data/nbproject/project.properties +7 -0
  27. data/nbproject/project.xml +15 -0
  28. data/spec/.rspec +1 -0
  29. data/spec/active_schema/active_record/base_spec.rb +118 -0
  30. data/spec/active_schema/associations/by_foreign_key_spec.rb +73 -0
  31. data/spec/active_schema/associations/generator_spec.rb +5 -0
  32. data/spec/active_schema/in_advance_feeder_spec.rb +25 -0
  33. data/spec/active_schema/on_the_fly_feeder_spec.rb +34 -0
  34. data/spec/active_schema/schema_feeder_spec.rb +111 -0
  35. data/spec/active_schema/table_hub_spec.rb +70 -0
  36. data/spec/active_schema/table_spec.rb +13 -0
  37. data/spec/active_schema/validations/by_column_spec.rb +47 -0
  38. data/spec/active_schema/validations/by_index_spec.rb +15 -0
  39. data/spec/active_schema/validations/generator_spec.rb +23 -0
  40. data/spec/active_schema_spec.rb +14 -0
  41. data/spec/spec_helper.rb +31 -0
  42. data/spec/support/establish_connection.rb +8 -0
  43. data/spec/support/model_macros.rb +31 -0
  44. data/spec/support/test_models.rb +12 -0
  45. metadata +366 -0
@@ -0,0 +1,73 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+ include ActiveSchema::Associations
3
+
4
+ describe ActiveSchema::Associations::ByForeignKey do
5
+ def given_foreign_key(from_table, to_table, key_name, unique = false)
6
+ @from_model = mock("FakeFromModel",
7
+ :columns => [ActiveRecord::ConnectionAdapters::Column.new(key_name, nil, 'int(11)')],
8
+ :name => from_table.singularize.camelize
9
+ )
10
+ @to_model = mock("FakeToModel",
11
+ :name => to_table.singularize.camelize
12
+ )
13
+ @key_name = key_name
14
+ @unique = unique
15
+ end
16
+ after {
17
+ described_class.new(@from_model, @to_model, @key_name, @unique).generate
18
+ }
19
+
20
+ describe ActiveSchema::Associations::ByForwardForeignKey do
21
+ it "sets belongs_to" do
22
+ given_foreign_key('prisoners', 'facilities', 'facility_id')
23
+ @from_model.should_receive(:belongs_to)\
24
+ .with("facility", { :class_name => "Facility", :foreign_key => 'facility_id'})
25
+ end
26
+ end
27
+
28
+ describe ActiveSchema::Associations::ByReverseForeignKey do
29
+ context "Key column not unique" do
30
+ it "sets has_many" do
31
+ given_foreign_key('prisoners', 'facilities', 'facility_id')
32
+ @to_model.should_receive(:has_many)\
33
+ .with("prisoners", { :class_name => "Prisoner", :foreign_key => 'facility_id'})
34
+ end
35
+ end
36
+ context "Key column unique" do
37
+ it "sets has_one" do
38
+ given_foreign_key('prisoners', 'facilities', 'facility_id', true)
39
+ @to_model.should_receive(:has_one)\
40
+ .with("prisoner", { :class_name => "Prisoner", :foreign_key => 'facility_id'})
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+
47
+ describe ActiveSchema::Associations::Naming do
48
+ before {
49
+ @naming = Class.new.send(:include, Naming).new
50
+ }
51
+
52
+ def given_model(model_name)
53
+ @model = mock(model_name, :name => model_name)
54
+ end
55
+ context "name_for" do
56
+ it "underscores camelized names" do
57
+ given_model("ABumpyBackModel")
58
+ @naming.name_for(@model).should == "a_bumpy_back_model"
59
+ end
60
+
61
+ it "strips module names" do
62
+ given_model("Hideously::Complicated::And::Convoluted::ClassHierarchy")
63
+ @naming.name_for(@model).should == "class_hierarchy"
64
+ end
65
+ end
66
+
67
+ context "plural_name_for" do
68
+ it "pluralizes name_for" do
69
+ given_model("BumpyBackModel")
70
+ @naming.plural_name_for(@model).should == "bumpy_back_models"
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,5 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe ActiveSchema::Associations::Generator do
4
+ end
5
+
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ include ActiveSchema
3
+
4
+ describe InAdvanceFeeder do
5
+ before(:each) do
6
+ @prisoner_model = prisoner_model
7
+ @in_advance_feeder = InAdvanceFeeder.new
8
+ @in_advance_feeder.stub!(:dispatch_attachments)
9
+ end
10
+
11
+ context "when a model is loaded" do
12
+ after { @in_advance_feeder.model_loaded(@prisoner_model) }
13
+
14
+ it "adds the model" do
15
+ @in_advance_feeder.table_hub.should_receive(:add_model).with(@prisoner_model)
16
+ end
17
+
18
+ it "attaches associations and validations" do
19
+ @in_advance_feeder.should_receive(:dispatch_attachments)
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+
@@ -0,0 +1,34 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
5
+
6
+ describe ActiveSchema::OnTheFlyFeeder do
7
+ before(:each) do
8
+ @on_the_fly_feeder = ActiveSchema::OnTheFlyFeeder.new
9
+ @prisoner_model = prisoner_model
10
+ @table_hub = @on_the_fly_feeder.table_hub
11
+ end
12
+
13
+ context "when a model is loaded" do
14
+ after { @on_the_fly_feeder.model_loaded(@prisoner_model) }
15
+
16
+ it "adds indexes to TableHub" do
17
+ @table_hub.should_receive(:add_index)\
18
+ .with(@prisoner_model.table_name, instance_of(ActiveRecord::ConnectionAdapters::IndexDefinition))
19
+ end
20
+
21
+ it "adds foreign keys to TableHub" do
22
+ @table_hub.should_receive(:add_foreign_key).with('prisoners', 'facilities', 'facility_id')
23
+ end
24
+ #
25
+ it "adds model to TableHub" do
26
+ @on_the_fly_feeder.stub!(:dispatch_attachments)
27
+ @table_hub.should_receive(:add_model).with(@prisoner_model)
28
+ end
29
+
30
+ it "attaches associations and validations" do
31
+ @on_the_fly_feeder.should_receive(:dispatch_attachments)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,111 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ include ActiveSchema
4
+ describe SchemaFeeder do
5
+ before(:each) do
6
+ @table_hub = ActiveSchema::TableHub.new
7
+ @schema_feeder = SchemaFeeder.new
8
+ @schema_feeder.stub!(:table_hub).and_return(@table_hub)
9
+ end
10
+
11
+ context "add_index" do
12
+ after do
13
+ @schema_feeder.add_index "facilities", ["warden_id"], :name => "warden_id", :unique => true
14
+ end
15
+
16
+ it "adds an IndexDefinition to TableHub" do
17
+ @table_hub.should_receive(:add_index).with('facilities', instance_of(ActiveRecord::ConnectionAdapters::IndexDefinition))
18
+ end
19
+ end
20
+
21
+ context "add_foreign_key" do
22
+ after do
23
+ @schema_feeder.add_foreign_key "facilities", "wardens", :name => "facilities_warden_id"
24
+ end
25
+
26
+ it "guesses the foreign key column name if not supplied" do
27
+ @table_hub.should_receive(:add_foreign_key).with(anything, anything, "warden_id")
28
+ end
29
+
30
+ it "adds a foreign key to TableHub" do
31
+ @table_hub.should_receive(:add_foreign_key).with('facilities', 'wardens', "warden_id")
32
+ end
33
+
34
+ end
35
+
36
+ context "reading the contents of a schema file" do
37
+ it "receives appropriate callbacks" do
38
+ @schema_feeder.should_receive(:add_index).exactly(4).times
39
+ @schema_feeder.should_receive(:add_foreign_key).exactly(3).times
40
+ @schema_feeder.read(EXAMPLE_SCHEMA)
41
+ end
42
+
43
+ end
44
+ end
45
+
46
+ describe FilteredSchemaReader do
47
+ before do
48
+ @call_back = mock("SchemaFeeder")
49
+ @filtered_schema_reader = FilteredSchemaReader.new(@call_back, EXAMPLE_SCHEMA)
50
+ end
51
+
52
+ it "contains only lines matching filter" do
53
+ @filtered_schema_reader.filtered_lines.size.should == 7
54
+ end
55
+
56
+ it "makes callbacks to the supplied object" do
57
+ @call_back.should_receive(:add_index).exactly(4).times
58
+ @call_back.should_receive(:add_foreign_key).exactly(3).times
59
+ @filtered_schema_reader.evaluate
60
+ end
61
+
62
+ end
63
+ #ActiveRecord::Schema.define(:version => 0)
64
+
65
+ EXAMPLE_SCHEMA = <<FILE
66
+ # This file is auto-generated from the current state of the database. Instead
67
+ # of editing this file, please use the migrations feature of Active Record to
68
+ # incrementally modify your database, and then regenerate this schema definition.
69
+ #
70
+ # Note that this schema.rb definition is the authoritative source for your
71
+ # database schema. If you need to create the application database on another
72
+ # system, you should be using db:schema:load, not running all the migrations
73
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
74
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
75
+ #
76
+ # It's strongly recommended to check this file into your version control system.
77
+
78
+ ActiveRecord::Schema.define(:version => 0) do
79
+
80
+ create_table "facilities", :force => true do |t|
81
+ t.text "name", :null => false
82
+ t.integer "warden_id", :null => false
83
+ end
84
+
85
+ add_index "facilities", ["warden_id"], :name => "warden_id", :unique => true
86
+
87
+ create_table "prisoners", :force => true do |t|
88
+ t.text "name", :null => false
89
+ t.integer "facility_id", :null => false
90
+ end
91
+
92
+ add_index "prisoners", ["facility_id"], :name => "facility_id"
93
+
94
+ create_table "wardens", :primary_key => "facility_id", :force => true do |t|
95
+ t.integer "id", :null => false
96
+ t.text "name", :null => false
97
+ end
98
+
99
+ add_index "wardens", ["facility_id"], :name => "facility_id", :unique => true
100
+ add_index "wardens", ["id"], :name => "id"
101
+
102
+ add_foreign_key "facilities", "wardens", :name => "facilities_warden_id"
103
+
104
+ add_foreign_key "prisoners", "facilities", :name => "prisoners_facility_id"
105
+
106
+ add_foreign_key "wardens", "facilities", :name => "wardens_facility_id"
107
+
108
+ end
109
+ FILE
110
+
111
+
@@ -0,0 +1,70 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe ActiveSchema::TableHub do
4
+ before(:each) do
5
+ @table_hub = ActiveSchema::TableHub.new
6
+ end
7
+
8
+ context "when adding a model" do
9
+ it "should have a table by the model's table name" do
10
+ @table_hub.add_model(prisoner_model)
11
+ @table_hub.tables.should have_key("prisoners")
12
+ end
13
+
14
+ context "and a table already exists" do
15
+ it "should not change the identity of the existing table object" do
16
+ @table_hub.add_model(prisoner_model)
17
+ @table_hub.add_foreign_key('prisoners', 'facilities', 'facility_id') # Makes a Table object for the 'facilities' table
18
+ facility_table = @table_hub.tables['facilities']
19
+ @table_hub.add_model(facility_model)
20
+ @table_hub.tables['facilities'].should equal(facility_table)
21
+ end
22
+
23
+ it "links the model with the existing table" do
24
+ @table_hub.add_model(prisoner_model)
25
+ @table_hub.add_foreign_key('prisoners', 'facilities', 'facility_id') # Makes a Table object for the 'facilities' table
26
+ facility = facility_model
27
+ @table_hub.add_model(facility)
28
+ @table_hub.tables['facilities'].model.should == facility
29
+ end
30
+ end
31
+ end
32
+
33
+ context "when adding a foreign key" do
34
+ it "should contain an entry for the destination table" do
35
+ @table_hub.add_model(prisoner_model)
36
+ @table_hub.add_foreign_key('prisoners', 'facilities', 'facility_id')
37
+ @table_hub.tables.should have_key("facilities")
38
+ end
39
+
40
+ it "adds forward and reverse relations between tables" do
41
+ @table_hub.add_foreign_key('prisoners', 'facilities', 'facility_id')
42
+ @table_hub.relations['prisoners'].should include(@table_hub.tables['facilities'])
43
+ @table_hub.relations['facilities'].should include(@table_hub.tables['prisoners'])
44
+ end
45
+
46
+ it "adds a foreign key to the source table" do
47
+ @table_hub.add_model(prisoner_model)
48
+ prisoner_table = @table_hub.tables['prisoners']
49
+ prisoner_table.should_receive(:add_foreign_key)
50
+ @table_hub.add_foreign_key('prisoners', 'facilities', 'facility_id')
51
+ end
52
+
53
+ end
54
+
55
+ context "when adding an index" do
56
+ it "should contain an entry for the indexed table" do
57
+ @table_hub.add_index('prisoners', mock("IndexObject"))
58
+ @table_hub.tables.should have_key("prisoners")
59
+ end
60
+
61
+ it "adds an index entry to the indexed table" do
62
+ @table_hub.add_model(prisoner_model)
63
+ index_obj = mock("IndexObject")
64
+ @table_hub.tables['prisoners'].should_receive(:add_index).with(index_obj)
65
+ @table_hub.add_index('prisoners', index_obj)
66
+ end
67
+ end
68
+
69
+ end
70
+
@@ -0,0 +1,13 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
5
+
6
+ describe ActiveSchema::Table do
7
+ describe "unique_index_on?" do
8
+ it "should be true if unique index" do
9
+ Warden.table_with_indexes.unique_index_on?('facility_id').should be_true
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe ActiveSchema::Validations::ByColumn do
4
+ after { described_class.new(@model, @column).generate }
5
+
6
+ def given_column(name, sql_type, null = true)
7
+ @model = double("FakeModel")
8
+ @column = ActiveRecord::ConnectionAdapters::Column.new(name, nil, sql_type, null)
9
+ end
10
+
11
+ describe ActiveSchema::Validations::ByDataType do
12
+
13
+ it "accepts only integers when int(11)" do
14
+ given_column('digit', 'int(11)')
15
+ @model.should_receive(:validates_numericality_of)\
16
+ .with(:digit, {:allow_nil => true, :only_integer => true})
17
+ end
18
+
19
+ it "accepts only numbers when decimal(8,2)" do
20
+ given_column('number', 'decimal(8,2)')
21
+ @model.should_receive(:validates_numericality_of)\
22
+ .with(:number, {:allow_nil => true})
23
+ end
24
+
25
+ it "validates text length when varchar(255)" do
26
+ given_column('text', 'varchar(255)')
27
+ @model.should_receive(:validates_length_of)\
28
+ .with(:text, {:allow_nil => true, :maximum => 255})
29
+ end
30
+ end
31
+
32
+ describe ActiveSchema::Validations::ByNullability do
33
+
34
+ it "validates presence when varchar(255)" do
35
+ given_column('text', 'varchar(255)', false)
36
+ @model.should_receive(:validates_presence_of)\
37
+ .with(:text, {})
38
+ end
39
+
40
+ it "validates inclusion of when boolean" do
41
+ given_column('yes_or_no', 'boolean', false)
42
+ @model.should_receive(:validates_inclusion_of)\
43
+ .with(:yes_or_no, {:in => [true, false]})
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,15 @@
1
+ # To change this template, choose Tools | Templates
2
+ # and open the template in the editor.
3
+
4
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
5
+
6
+ describe ActiveSchema::Validations::ByIndex do
7
+ before(:each) do
8
+ @by_index = ActiveSchema::Validations::ByIndex
9
+ end
10
+
11
+ it "should desc" do
12
+ # TODO
13
+ end
14
+ end
15
+
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe ActiveSchema::Validations::Generator do
4
+ before {
5
+ @prisoner_table = prisoner_table
6
+ @prisoner_model = @prisoner_table.model
7
+ }
8
+
9
+ it "is a healthy model" do
10
+ @prisoner_model.columns.should_not be_nil
11
+ end
12
+
13
+ it "skips validation for columns in given proc" do
14
+ @prisoner_model.should_not be_nil
15
+ skip_proc = proc{|column| column.name == "id" }
16
+ generator = ActiveSchema::Validations::Generator.new(@prisoner_table, skip_proc)
17
+ generator.generate
18
+ @prisoner_model.validators_on(:id).should be_empty
19
+ @prisoner_model.validators_on(:name).should_not be_empty
20
+
21
+ end
22
+ end
23
+
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ActiveSchema" do
4
+ context "configuration" do
5
+ it "has configuration method" do
6
+ ActiveSchema.should respond_to :configuration
7
+ end
8
+
9
+ it "defaults to OnTheFlyFeeder" do
10
+ ActiveSchema.configuration.feeder.should be_instance_of(ActiveSchema::OnTheFlyFeeder)
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'spork'
3
+
4
+ Spork.prefork do
5
+ # Loading more in this block will cause your tests to run faster. However,
6
+ # if you change any configuration or code from libraries loaded here, you'll
7
+ # need to restart spork for it take effect.
8
+ require 'bundler'
9
+ begin
10
+ Bundler.setup(:default, :development)
11
+ rescue Bundler::BundlerError => e
12
+ $stderr.puts e.message
13
+ $stderr.puts "Run `bundle install` to install missing gems"
14
+ exit e.status_code
15
+ end
16
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
17
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
+ require 'rspec'
19
+ end
20
+
21
+ Spork.each_run do
22
+ require 'active_schema'
23
+ end
24
+
25
+ # Requires supporting files with custom matchers and macros, etc,
26
+ # in ./support/ and its subdirectories.
27
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
28
+
29
+ RSpec.configure do |c|
30
+ c.include(ModelMacros)
31
+ end