rails_sql_views 0.6.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +5 -1
  2. data/LICENSE +7 -0
  3. data/Rakefile +30 -72
  4. data/TODO +2 -0
  5. data/lib/active_record/view.rb +76 -0
  6. data/lib/core_ext/module.rb +11 -9
  7. data/lib/rails_sql_views.rb +16 -21
  8. data/lib/rails_sql_views/connection_adapters/abstract/schema_definitions.rb +2 -6
  9. data/lib/rails_sql_views/connection_adapters/abstract/schema_statements.rb +26 -9
  10. data/lib/rails_sql_views/connection_adapters/abstract_adapter.rb +24 -3
  11. data/lib/rails_sql_views/connection_adapters/mysql_adapter.rb +36 -9
  12. data/lib/rails_sql_views/connection_adapters/oci_adapter.rb +5 -4
  13. data/lib/rails_sql_views/connection_adapters/oracle_adapter.rb +5 -4
  14. data/lib/rails_sql_views/connection_adapters/oracleenhanced_adapter.rb +39 -0
  15. data/lib/rails_sql_views/connection_adapters/oracleenhanced_adapter.rb.orig +72 -0
  16. data/lib/rails_sql_views/connection_adapters/postgresql_adapter.rb +28 -2
  17. data/lib/rails_sql_views/connection_adapters/postgresql_adapter.rb.orig +69 -0
  18. data/lib/rails_sql_views/connection_adapters/sqlite_adapter.rb +66 -0
  19. data/lib/rails_sql_views/connection_adapters/sqlserver_adapter.rb +11 -4
  20. data/lib/rails_sql_views/loader.rb +18 -0
  21. data/lib/rails_sql_views/schema_dumper.rb +53 -16
  22. data/lib/rails_sql_views/version.rb +2 -2
  23. data/test/adapter_test.rb +82 -0
  24. data/test/connection/native_mysql/connection.rb +32 -0
  25. data/test/connection/native_postgresql/connection.rb +31 -0
  26. data/test/connection/oracle_enhanced/connection.rb +29 -0
  27. data/test/models/item.rb +4 -0
  28. data/test/models/person.rb +5 -0
  29. data/test/models/person2.rb +3 -0
  30. data/test/models/place.rb +2 -0
  31. data/test/models/v_person.rb +4 -0
  32. data/test/models/v_profile.rb +3 -0
  33. data/test/schema.native_mysql.expected.rb +51 -0
  34. data/test/schema.native_postgresql.expected.rb +51 -0
  35. data/test/schema.oracle_enhanced.expected.rb +51 -0
  36. data/test/schema_dumper_test.rb +117 -0
  37. data/test/test_helper.rb +30 -0
  38. data/test/view_model_test.rb +63 -0
  39. data/test/view_operations_test.rb +36 -0
  40. metadata +88 -57
@@ -1,8 +1,8 @@
1
1
  module RailsSqlViews
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
- MINOR = 6
5
- TINY = 1
4
+ MINOR = 7
5
+ TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -0,0 +1,82 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+
3
+ class AdapterTest < Test::Unit::TestCase
4
+ def test_current_database
5
+ if ActiveRecord::Base.connection.respond_to?(:current_database)
6
+ assert_equal 'rails_sql_views_unittest', ActiveRecord::Base.connection.current_database
7
+ end
8
+ end
9
+ def test_tables
10
+ create_view
11
+ found = ActiveRecord::Base.connection.tables.sort
12
+ found.delete(ActiveRecord::Migrator.schema_migrations_table_name)
13
+ assert_equal ["items", "items_people", "people", "people2", "places", "v_people"], found
14
+ end
15
+ def test_base_tables
16
+ create_view
17
+ found = ActiveRecord::Base.connection.base_tables.sort
18
+ found.delete(ActiveRecord::Migrator.schema_migrations_table_name)
19
+ assert_equal ["items", "items_people", "people", "people2", "places"], found
20
+ end
21
+ def test_views
22
+ create_view
23
+ assert_equal ['v_people'], ActiveRecord::Base.connection.views
24
+ end
25
+ def test_columns
26
+ create_view
27
+ assert_equal ["f_name", "l_name", "social_security"], ActiveRecord::Base.connection.columns('v_people').collect { |c| c.name }
28
+ end
29
+ def test_supports_views
30
+ assert ActiveRecord::Base.connection.supports_views?
31
+ end
32
+
33
+ def test_mapped_views
34
+ create_mapping
35
+ assert_equal ['v_people'], ActiveRecord::Base.connection.views
36
+ end
37
+ def test_mapped_columns
38
+ create_mapping
39
+ assert_equal ["f_name", "l_name", "address_id"], ActiveRecord::Base.connection.columns('v_people').collect { |c| c.name }
40
+ end
41
+
42
+ def test_view_select_statement
43
+ case ActiveRecord::Base.connection.adapter_name
44
+ when "MySQL"
45
+ assert_equal "select `people`.`first_name` AS `f_name`,`people`.`last_name` AS `l_name`,`people`.`ssn` AS `social_security` from `people`", ActiveRecord::Base.connection.view_select_statement('v_people')
46
+ when "PostgreSQL"
47
+ assert_equal "SELECT people.first_name AS f_name, people.last_name AS l_name, people.ssn AS social_security FROM people;", ActiveRecord::Base.connection.view_select_statement('v_people')
48
+ end
49
+ end
50
+
51
+ def test_old_name_not_found_error_during_mapping
52
+ assert_raise ActiveRecord::ActiveRecordError do
53
+ ActiveRecord::Base.connection.create_mapping_view(:people, :v_people, :force => true) do |v|
54
+ v.map_column :foo, :bar
55
+ end
56
+ end
57
+ end
58
+
59
+ ### TODO
60
+ # def test_only_base_table_triggers_are_dropped_for_disabled_ref_integrity
61
+ # ActiveRecord::Base.connection.disable_referential_integrity do
62
+ # end
63
+ # end
64
+
65
+ private
66
+ def create_view
67
+ ActiveRecord::Base.connection.create_view(:v_people, 'select first_name, last_name, ssn from people', :force => true) do |v|
68
+ v.column :f_name
69
+ v.column :l_name
70
+ v.column :social_security
71
+ end
72
+ end
73
+
74
+ def create_mapping
75
+ ActiveRecord::Base.connection.create_mapping_view(:people, :v_people, :force => true) do |v|
76
+ v.map_column :id, nil
77
+ v.map_column :first_name, :f_name
78
+ v.map_column :last_name, :l_name
79
+ v.map_column :ssn, nil
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,32 @@
1
+ print "Using native MySQL\n"
2
+
3
+ adapter_name = 'mysql'
4
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '/../../connection.yml'))[adapter_name]
5
+
6
+ #require 'logger'
7
+ #ActiveRecord::Base.logger = Logger.new("debug.log")
8
+
9
+ ActiveRecord::Base.silence do
10
+ ActiveRecord::Base.configurations = {
11
+ config['database'] => {
12
+ :adapter => adapter_name,
13
+ :username => config['username'],
14
+ :password => config['password'],
15
+ :host => config['host'],
16
+ :database => config['database'],
17
+ :encoding => config['encoding'],
18
+ :schema_file => config['schema_file'],
19
+ }
20
+ }
21
+
22
+ ActiveRecord::Base.establish_connection config['database']
23
+ ActiveRecord::Migration.verbose = false
24
+
25
+ puts "Resetting database"
26
+ conn = ActiveRecord::Base.connection
27
+ conn.recreate_database(conn.current_database)
28
+ conn.reconnect!
29
+ lines = open(File.join(File.dirname(__FILE__), ActiveRecord::Base.configurations[config['database']][:schema_file])).readlines
30
+ lines.join.split(';').each { |line| conn.execute(line) }
31
+ conn.reconnect!
32
+ end
@@ -0,0 +1,31 @@
1
+ print "Using native PostgreSQL\n"
2
+
3
+ adapter_name = 'postgresql'
4
+ config = YAML.load_file(File.join(File.dirname(__FILE__), '/../../connection.yml'))[adapter_name]
5
+
6
+ #require 'logger'
7
+ #ActiveRecord::Base.logger = Logger.new("debug.log")
8
+
9
+ ActiveRecord::Base.silence do
10
+ ActiveRecord::Base.configurations = {
11
+ 'rails_sql_views_unittest' => {
12
+ :adapter => adapter_name,
13
+ :username => config['username'],
14
+ :password => config['password'],
15
+ :host => config['host'],
16
+ :database => config['database'],
17
+ :encoding => config['encoding'],
18
+ :schema_file => config['schema_file'],
19
+ }
20
+ }
21
+
22
+ ActiveRecord::Base.establish_connection config['database']
23
+
24
+ puts "Resetting database"
25
+ conn = ActiveRecord::Base.connection
26
+ #conn.recreate_database(conn.current_database)
27
+ conn.reconnect!
28
+ lines = open(File.join(File.dirname(__FILE__), ActiveRecord::Base.configurations[config['database']][:schema_file])).readlines
29
+ lines.join.split(';').each { |line| conn.execute(line) }
30
+ conn.reconnect!
31
+ end
@@ -0,0 +1,29 @@
1
+ print "Using Oracle Enhanced\n"
2
+
3
+ #require 'logger'
4
+ #ActiveRecord::Base.logger = Logger.new("debug.log")
5
+
6
+ ActiveRecord::Base.configurations = {
7
+ 'rails_sql_views_unittest' => {
8
+ :adapter => :oracle_enhanced,
9
+ :username => 'rails_sql_views_unittest',
10
+ :password => 'rails',
11
+ :host => 'localhost',
12
+ :database => 'mydev',
13
+ :encoding => 'utf8',
14
+ :procedures_file => 'procedures.sql',
15
+ :schema_file => 'schema.sql',
16
+ }
17
+ }
18
+
19
+ ActiveRecord::Base.establish_connection 'rails_sql_views_unittest'
20
+
21
+ puts "Resetting database"
22
+ conn = ActiveRecord::Base.connection
23
+ #conn.recreate_database(conn.current_database)
24
+ conn.reconnect!
25
+ [:procedures_file, :schema_file].each do |file|
26
+ lines = open(File.join(File.dirname(__FILE__), ActiveRecord::Base.configurations['rails_sql_views_unittest'][file])).readlines
27
+ conn.execute(lines.to_s)
28
+ end
29
+ conn.reconnect!
@@ -0,0 +1,4 @@
1
+ class Item < ActiveRecord::Base
2
+ belongs_to :person
3
+ has_and_belongs_to_many :people
4
+ end
@@ -0,0 +1,5 @@
1
+ class Person < ActiveRecord::Base
2
+ belongs_to :address, :class_name => 'Place', :foreign_key => :address_id
3
+ has_many :owned_items, :class_name => 'Item'
4
+ has_and_belongs_to_many :shared_items, :class_name => 'Item'
5
+ end
@@ -0,0 +1,3 @@
1
+ class Person2 < ActiveRecord::Base
2
+ set_table_name :people2
3
+ end
@@ -0,0 +1,2 @@
1
+ class Place < ActiveRecord::Base
2
+ end
@@ -0,0 +1,4 @@
1
+ require 'active_record/view'
2
+
3
+ class VPerson < ActiveRecord::View
4
+ end
@@ -0,0 +1,3 @@
1
+ class VProfile < ActiveRecord::Base
2
+
3
+ end
@@ -0,0 +1,51 @@
1
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
+ # then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6
+ # to create the application database on another system, you should be using db:schema:load, not running
7
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
9
+ #
10
+ # It's strongly recommended to check this file into your version control system.
11
+
12
+ ActiveRecord::Schema.define(:version => 0) do
13
+
14
+ create_table "items", :force => true do |t|
15
+ t.integer "person_id"
16
+ end
17
+
18
+ create_table "items_people", :id => false, :force => true do |t|
19
+ t.integer "person_id"
20
+ t.integer "item_id"
21
+ end
22
+
23
+ create_table "people", :force => true do |t|
24
+ t.string "first_name"
25
+ t.string "last_name"
26
+ t.string "ssn", :limit => 64
27
+ t.integer "address_id"
28
+ end
29
+
30
+ create_table "people2", :force => true do |t|
31
+ t.string "first_name"
32
+ t.string "last_name"
33
+ t.string "ssn", :limit => 64
34
+ end
35
+
36
+ create_table "places", :force => true do |t|
37
+ t.text "address"
38
+ t.string "city"
39
+ t.string "cstate"
40
+ t.string "country", :limit => 2
41
+ end
42
+
43
+ create_view "v_people", "select `people`.`id` AS `id`,`people`.`first_name` AS `f_name`,`people`.`last_name` AS `l_name`,`people`.`ssn` AS `social_security`,`people`.`address_id` AS `address_id` from `people`", :force => true do |v|
44
+ v.column :id
45
+ v.column :f_name
46
+ v.column :l_name
47
+ v.column :social_security
48
+ v.column :address_id
49
+ end
50
+
51
+ end
@@ -0,0 +1,51 @@
1
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
+ # then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6
+ # to create the application database on another system, you should be using db:schema:load, not running
7
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
9
+ #
10
+ # It's strongly recommended to check this file into your version control system.
11
+
12
+ ActiveRecord::Schema.define(:version => 0) do
13
+
14
+ create_table "items", :force => true do |t|
15
+ t.integer "person_id"
16
+ end
17
+
18
+ create_table "items_people", :id => false, :force => true do |t|
19
+ t.integer "person_id"
20
+ t.integer "item_id"
21
+ end
22
+
23
+ create_table "people", :force => true do |t|
24
+ t.string "first_name"
25
+ t.string "last_name"
26
+ t.string "ssn", :limit => 64
27
+ t.integer "address_id"
28
+ end
29
+
30
+ create_table "people2", :force => true do |t|
31
+ t.string "first_name"
32
+ t.string "last_name"
33
+ t.string "ssn", :limit => 64
34
+ end
35
+
36
+ create_table "places", :force => true do |t|
37
+ t.text "address"
38
+ t.string "city"
39
+ t.string "cstate"
40
+ t.string "country", :limit => 2
41
+ end
42
+
43
+ create_view "v_people", "SELECT people.id, people.first_name AS f_name, people.last_name AS l_name, people.ssn AS social_security, people.address_id FROM people;", :force => true do |v|
44
+ v.column :id
45
+ v.column :f_name
46
+ v.column :l_name
47
+ v.column :social_security
48
+ v.column :address_id
49
+ end
50
+
51
+ end
@@ -0,0 +1,51 @@
1
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
+ # then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6
+ # to create the application database on another system, you should be using db:schema:load, not running
7
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
9
+ #
10
+ # It's strongly recommended to check this file into your version control system.
11
+
12
+ ActiveRecord::Schema.define(:version => 0) do
13
+
14
+ create_table "items", :force => true do |t|
15
+ t.integer "person_id", :precision => 38, :scale => 0
16
+ end
17
+
18
+ create_table "items_people", :id => false, :force => true do |t|
19
+ t.integer "person_id", :precision => 38, :scale => 0
20
+ t.integer "item_id", :precision => 38, :scale => 0
21
+ end
22
+
23
+ create_table "people", :force => true do |t|
24
+ t.string "first_name"
25
+ t.string "last_name"
26
+ t.string "ssn", :limit => 64
27
+ t.integer "address_id", :precision => 38, :scale => 0
28
+ end
29
+
30
+ create_table "people2", :force => true do |t|
31
+ t.string "first_name"
32
+ t.string "last_name"
33
+ t.string "ssn", :limit => 64
34
+ end
35
+
36
+ create_table "places", :force => true do |t|
37
+ t.string "address", :limit => 2000
38
+ t.string "city"
39
+ t.string "cstate"
40
+ t.string "country", :limit => 2
41
+ end
42
+
43
+ create_view "v_people", "select id, first_name, last_name, ssn, address_id from people", :force => true do |v|
44
+ v.column :id
45
+ v.column :f_name
46
+ v.column :l_name
47
+ v.column :social_security
48
+ v.column :address_id
49
+ end
50
+
51
+ end
@@ -0,0 +1,117 @@
1
+ require "#{File.dirname(__FILE__)}/test_helper"
2
+ require 'active_record/schema_dumper'
3
+
4
+ class SchemaDumperTest < Test::Unit::TestCase
5
+ def setup
6
+ teardown
7
+ end
8
+ def teardown
9
+ ['V_PEOPLE', 'V_PROFILE'].each do |view|
10
+ if ActiveRecord::Base.connection.adapter_name == 'OracleEnhanced'
11
+ ActiveRecord::Base.connection.execute("
12
+ DECLARE
13
+ CURSOR C1 is SELECT view_name FROM user_views where view_name = '#{view}';
14
+ BEGIN
15
+ FOR I IN C1 LOOP
16
+ EXECUTE IMMEDIATE 'DROP VIEW '||I.view_name||'';
17
+ END LOOP;
18
+ END;
19
+ ");
20
+ else
21
+ ActiveRecord::Base.connection.execute("drop view if exists #{view}")
22
+ end
23
+ end
24
+ end
25
+ def test_view
26
+ create_people_view
27
+ stream = StringIO.new
28
+ dumper = ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
29
+ stream.rewind
30
+ assert_equal File.open(File.dirname(__FILE__) + "/schema.#{$connection}.expected.rb", 'r').readlines, stream.readlines
31
+ end
32
+ def test_dump_and_load
33
+ create_people_view
34
+ assert_dump_and_load_succeed
35
+ end
36
+ def test_union
37
+ Person.create(:first_name => 'Joe', :last_name => 'User', :ssn => '123456789')
38
+ Person2.create(:first_name => 'Jane', :last_name => 'Doe', :ssn => '222334444')
39
+
40
+ select_stmt = <<-HERE
41
+ select first_name, last_name, ssn from people
42
+ UNION
43
+ select first_name, last_name, ssn from people2
44
+ HERE
45
+
46
+ ActiveRecord::Base.connection.create_view(:v_profile, select_stmt, :force => true) do |v|
47
+ v.column :first_name
48
+ v.column :last_name
49
+ v.column :ssn
50
+ end
51
+
52
+ assert_dump_and_load_succeed
53
+ end
54
+ def test_view_creation_order
55
+ ActiveRecord::SchemaDumper.view_creation_order << :v_people
56
+ create_people_view
57
+ assert_dump_and_load_succeed
58
+ ActiveRecord::SchemaDumper.view_creation_order.pop
59
+ end
60
+ def test_symbol_ignore
61
+ ActiveRecord::SchemaDumper.ignore_views << :v_people
62
+ create_people_view
63
+ assert_dump_and_load_succeed
64
+ ActiveRecord::SchemaDumper.ignore_views.pop
65
+ end
66
+ def test_regex_ignore
67
+ ActiveRecord::SchemaDumper.ignore_views << Regexp.new(/v_people/)
68
+ create_people_view
69
+ assert_dump_and_load_succeed
70
+ ActiveRecord::SchemaDumper.ignore_views.pop
71
+ end
72
+ def test_non_allowed_object_raises_error
73
+ create_people_view
74
+ ActiveRecord::SchemaDumper.ignore_views << 0
75
+ begin
76
+ schema_file = File.dirname(__FILE__) + "/schema.#{$connection}.out.rb"
77
+ File.open(schema_file, "w") do |file|
78
+ assert_raise(StandardError) do
79
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
80
+ end
81
+ end
82
+ ensure
83
+ ActiveRecord::SchemaDumper.ignore_views.pop
84
+ end
85
+ end
86
+
87
+ def test_logging_error
88
+ ActiveRecord::SchemaDumper.ignore_views << 0
89
+ old_logger = ActiveRecord::Base.logger
90
+
91
+ begin
92
+ mock_logger = flexmock('logger', :error => nil)
93
+ mock_logger.should_receive(:error)
94
+ ActiveRecord::Base.logger = mock_logger
95
+ schema_file = File.dirname(__FILE__) + "/schema.#{$connection}.out.rb"
96
+ File.open(schema_file, "w") do |file|
97
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
98
+ end
99
+ ensure
100
+ ActiveRecord::SchemaDumper.ignore_views.pop
101
+ ActiveRecord::Base.logger = old_logger
102
+ end
103
+ end
104
+
105
+ def assert_dump_and_load_succeed
106
+ schema_file = File.dirname(__FILE__) + "/schema.#{$connection}.out.rb"
107
+ assert_nothing_raised do
108
+ File.open(schema_file, "w") do |file|
109
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
110
+ end
111
+ end
112
+
113
+ assert_nothing_raised do
114
+ load(schema_file)
115
+ end
116
+ end
117
+ end