active_schema 0.1.0

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 (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