rubaidh-rails_sql_views 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,25 @@
1
+ 0.1.0 - Released Dec 27, 2006
2
+ * Initial release
3
+
4
+ 0.5.0 - Released Dec 29, 2006
5
+ * Added support for PostgreSQL (Michael Schuerig)
6
+ * Fixed the schema dumper
7
+
8
+ 0.5.1 - Released Jan 10, 2007
9
+ * Patch by Clifford T. Matthews to use String.dump to dump out the view select statement
10
+
11
+ 0.6.0 - Released May 9, 2007
12
+ * Added support for SQL Server (Seth Ladd)-
13
+ * Added support for using views to map non-friendly database field names to AR-friendly names (Nathan Vack)
14
+ * Added support for Oracle (Alistair Davidson)
15
+
16
+ 0.6.1 - Released June 6, 2007
17
+ * Added test for union support
18
+ * Updated tests to include new table to support union test
19
+
20
+ 0.7.0
21
+ * Updated dependency versions to get on ActiveRecord 2.x. May not be compatible with Rails versions less than 2.x.
22
+ * Add nonview_tables support to *all* of the adapters (was missing from Postgres and SQLServer)
23
+
24
+ 0.7.1
25
+ * Strip the database name from the retrieved CREATE VIEW statements to support `rake db:test:prepare`, etc.
data/README ADDED
@@ -0,0 +1,51 @@
1
+ == Rails SQL Views
2
+
3
+ Library which adds SQL Views to Rails. Adds create_view and drop_view to the ActiveRecord::ConnectionAdapters::AbstractAdapter (which makes them available to migrations) and adds support for dumping views in the ActiveRecord::SchemaDumper.
4
+
5
+ == Installation
6
+
7
+ To install:
8
+
9
+ gem install rails_sql_views
10
+
11
+ Then add the following to your Rails config/environment.rb:
12
+
13
+ require_gem 'rails_sql_views'
14
+ require 'rails_sql_views'
15
+
16
+ == Usage
17
+
18
+ You can then use create_view and drop_view in your migrations. For example:
19
+
20
+ class CreatePersonView < ActiveRecord::Migration
21
+ def self.up
22
+ create_view :v_people, "select * from people" do |t|
23
+ t.column :id
24
+ t.column :name
25
+ t.column :social_security
26
+ end
27
+ end
28
+
29
+ def self.down
30
+ drop_view :v_people
31
+ end
32
+ end
33
+
34
+ This extension also adds support for views in the ActiveRecord::SchemaDumper class.
35
+
36
+ The following drivers are supported:
37
+
38
+ MySQL
39
+ PostgreSQL (Native and Pure Ruby)
40
+ Oracle
41
+ SQL Server
42
+
43
+ == Known Issues
44
+
45
+ * Drivers not mentioned above are not supported.
46
+
47
+ If you find any issues please send an email to anthonyeden@gmail.com .
48
+
49
+ == Contributing
50
+
51
+ If you would like to implement view support for other adapters then please drop me an email. Better yet, write up the adapter modifications and send them to me. :-)
data/Rakefile ADDED
@@ -0,0 +1,146 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/packagetask'
5
+ require 'rake/gempackagetask'
6
+ require 'rake/contrib/rubyforgepublisher'
7
+
8
+ require File.join(File.dirname(__FILE__), 'lib/rails_sql_views', 'version')
9
+
10
+ PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
11
+ PKG_FORK = ENV['PKG_FORK'] ? "#{ENV['PKG_FORK']}-" : ''
12
+
13
+ PKG_NAME = "#{PKG_FORK}rails_sql_views"
14
+ PKG_VERSION = RailsSqlViews::VERSION::STRING + PKG_BUILD
15
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
16
+ PKG_DESTINATION = ENV["PKG_DESTINATION"] || "../#{PKG_NAME}"
17
+
18
+ RELEASE_NAME = "REL #{PKG_VERSION}"
19
+
20
+ RUBY_FORGE_PROJECT = "activewarehouse"
21
+ RUBY_FORGE_USER = "aeden"
22
+
23
+ desc 'Default: run unit tests.'
24
+ task :default => :test
25
+
26
+ desc 'Test the library.'
27
+ Rake::TestTask.new(:test) do |t|
28
+ t.libs << 'lib'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = true
31
+ end
32
+
33
+ namespace :rcov do
34
+ desc 'Measures test coverage'
35
+ task :test do
36
+ rm_f 'coverage.data'
37
+ mkdir 'coverage' unless File.exist?('coverage')
38
+ rcov = "rcov --aggregate coverage.data --text-summary -Ilib"
39
+ system("#{rcov} test/*_test.rb test/**/*_test.rb")
40
+ system("open coverage/index.html") if PLATFORM['darwin']
41
+ end
42
+ end
43
+
44
+ desc 'Generate documentation library.'
45
+ Rake::RDocTask.new(:rdoc) do |rdoc|
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = 'Rails SQL Views'
48
+ rdoc.options << '--line-numbers' << '--inline-source'
49
+ rdoc.rdoc_files.include('README')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
52
+
53
+ PKG_FILES = FileList[
54
+ 'CHANGELOG',
55
+ 'README',
56
+ 'Rakefile',
57
+ 'bin/**/*',
58
+ 'lib/**/*',
59
+ ] - [ 'test' ]
60
+
61
+ spec = Gem::Specification.new do |s|
62
+ s.name = PKG_NAME
63
+ s.version = PKG_VERSION
64
+ s.summary = "Adds SQL Views to Rails."
65
+ s.description = <<-EOF
66
+ Library which adds SQL Views to Rails.
67
+ EOF
68
+
69
+ s.add_dependency('activerecord', '>= 2.1.0')
70
+ s.add_dependency('rake', '>= 0.8.3')
71
+
72
+ s.rdoc_options << '--exclude' << '.'
73
+ s.has_rdoc = false
74
+
75
+ s.files = PKG_FILES.to_a.delete_if {|f| f.include?('.svn')}
76
+ s.require_path = 'lib'
77
+
78
+ s.author = "Anthony Eden"
79
+ s.email = "anthonyeden@gmail.com"
80
+ s.homepage = "http://activewarehouse.rubyforge.org/rails_sql_views"
81
+ s.rubyforge_project = "activewarehouse"
82
+ end
83
+
84
+ Rake::GemPackageTask.new(spec) do |pkg|
85
+ pkg.gem_spec = spec
86
+ pkg.need_tar = true
87
+ pkg.need_zip = true
88
+ end
89
+
90
+ desc "Generate code statistics"
91
+ task :lines do
92
+ lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
93
+
94
+ for file_name in FileList["lib/**/*.rb"]
95
+ next if file_name =~ /vendor/
96
+ f = File.open(file_name)
97
+
98
+ while line = f.gets
99
+ lines += 1
100
+ next if line =~ /^\s*$/
101
+ next if line =~ /^\s*#/
102
+ codelines += 1
103
+ end
104
+ puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
105
+
106
+ total_lines += lines
107
+ total_codelines += codelines
108
+
109
+ lines, codelines = 0, 0
110
+ end
111
+
112
+ puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
113
+ end
114
+
115
+ desc "Publish the release files to RubyForge."
116
+ task :release => [ :package ] do
117
+ `rubyforge login`
118
+
119
+ for ext in %w( gem tgz zip )
120
+ release_command = "rubyforge add_release activewarehouse #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
121
+ puts release_command
122
+ system(release_command)
123
+ end
124
+ end
125
+
126
+ desc "Publish the API documentation"
127
+ task :pdoc => [:rdoc] do
128
+ Rake::SshDirPublisher.new("aeden@rubyforge.org", "/var/www/gforge-projects/activewarehouse/rails_sql_views/rdoc", "rdoc").upload
129
+ end
130
+
131
+ desc "Install the gem from a local generated package"
132
+ task :install => [:package] do
133
+ windows = RUBY_PLATFORM =~ /mswin/
134
+ sudo = windows ? '' : 'sudo'
135
+ gem = windows ? 'gem.bat' : 'gem'
136
+ `#{sudo} #{gem} install pkg/#{PKG_NAME}-#{PKG_VERSION}`
137
+ end
138
+
139
+ desc "Reinstall the gem from a local package copy"
140
+ task :reinstall => [:package] do
141
+ windows = RUBY_PLATFORM =~ /mswin/
142
+ sudo = windows ? '' : 'sudo'
143
+ gem = windows ? 'gem.bat' : 'gem'
144
+ `#{sudo} #{gem} uninstall #{PKG_NAME} -x`
145
+ `#{sudo} #{gem} install pkg/#{PKG_NAME}-#{PKG_VERSION}`
146
+ end
@@ -0,0 +1,76 @@
1
+
2
+ # A base class for database views.
3
+ # It is primarily useful for views that are centered around a single table/model.
4
+ module ActiveRecord # :nodoc:
5
+ class View < Base
6
+ self.abstract_class = true
7
+
8
+ def readonly?
9
+ true
10
+ end
11
+
12
+ class << self
13
+ # Clones all applicable associations from +model+ to this view
14
+ # and provides an instance method
15
+ # <tt>to_<em>model</em></tt>
16
+ # that casts a view object to an object of the kind view is
17
+ # based on. This latter object may be missing attributes; to fill
18
+ # them in, call #reload.
19
+ def based_on(model)
20
+ define_method("to_#{model.name.demodulize.underscore}") do
21
+ becomes(model)
22
+ end
23
+
24
+ model.reflect_on_all_associations.each do |assoc|
25
+ clone_association(model, assoc)
26
+ end
27
+ end
28
+
29
+ # Clone one or more associations from +model+ to this view class.
30
+ #
31
+ # NOTE: Currently only <tt>belongs_to</tt>, <tt>has_many</tt> (withouth
32
+ # <tt>:through</tt>), and <tt>has_and_belongs_to_many</tt> associations
33
+ # are supported.
34
+ def clone_association(model, *associations)
35
+ associations.each do |association|
36
+ r = case association
37
+ when String, Symbol
38
+ model.reflect_on_association(association.to_sym)
39
+ when ActiveRecord::Reflection::AssociationReflection
40
+ association
41
+ else
42
+ raise ArgumentError, "Unrecognized association #{association.inspect}; must be a Symbol, String, or AssociationReflection."
43
+ end
44
+ case r.macro
45
+ when :belongs_to
46
+ if self.column_names.include?(r.primary_key_name.to_s)
47
+ if !r.options[:foreign_type] || self.column_names.include?(r.options[:foreign_type])
48
+ options = r.options.merge(
49
+ :class_name => r.class_name,
50
+ :foreign_key => r.primary_key_name
51
+ )
52
+ belongs_to r.name, options
53
+ end
54
+ end
55
+ when :has_many
56
+ ### TODO :through assocications
57
+ options = r.options.merge(
58
+ :class_name => r.class_name,
59
+ :foreign_key => r.primary_key_name
60
+ )
61
+ has_many r.name, options
62
+ when :has_and_belongs_to_many
63
+ options = r.options.merge(
64
+ :class_name => r.class_name,
65
+ :foreign_key => r.primary_key_name,
66
+ :association_foreign_key => r.association_foreign_key
67
+ )
68
+ has_and_belongs_to_many r.name, options
69
+ when :has_one
70
+ ### TODO
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,13 @@
1
+ # This is required for 1.1.6 support
2
+ unless Module.respond_to?(:alias_method_chain)
3
+ class Module
4
+ def alias_method_chain(target, feature)
5
+ # Strip out punctuation on predicates or bang methods since
6
+ # e.g. target?_without_feature is not a valid method name.
7
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
8
+ yield(aliased_target, punctuation) if block_given?
9
+ alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
10
+ alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ #--
2
+ # Copyright (c) 2006 Anthony Eden
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ $:.unshift(File.dirname(__FILE__))
25
+
26
+ require 'active_record'
27
+
28
+ require 'core_ext/module'
29
+
30
+ require 'rails_sql_views/connection_adapters/abstract/schema_definitions'
31
+ require 'rails_sql_views/connection_adapters/abstract/schema_statements'
32
+ require 'rails_sql_views/connection_adapters/abstract_adapter'
33
+ require 'rails_sql_views/schema_dumper'
34
+ require 'rails_sql_views/loader'
35
+
36
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
37
+ include RailsSqlViews::ConnectionAdapters::SchemaStatements
38
+ def self.inherited(sub)
39
+ RailsSqlViews::Loader.load_extensions
40
+ end
41
+ end
42
+
43
+ ActiveRecord::SchemaDumper.class_eval do
44
+ include RailsSqlViews::SchemaDumper
45
+ end
46
+
47
+ RailsSqlViews::Loader.load_extensions
@@ -0,0 +1,63 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters #:nodoc:
3
+ # Abstract definition of a View
4
+ class ViewDefinition
5
+ attr_accessor :columns, :select_query
6
+
7
+ def initialize(base, select_query)
8
+ @columns = []
9
+ @base = base
10
+ @select_query = select_query
11
+ end
12
+
13
+ def column(name)
14
+ column = name.to_s
15
+ @columns << column unless @columns.include? column
16
+ self
17
+ end
18
+
19
+ def to_sql
20
+ @columns.collect { |c| @base.quote_column_name(c) } * ', '
21
+ end
22
+
23
+ end
24
+
25
+ class MappingDefinition
26
+
27
+ # Generates a hash of the form :old_column => :new_column
28
+ # Initially, it'll map column names to themselves.
29
+ # use map_column to modify the list.
30
+ def initialize(columns)
31
+ @columns = columns
32
+ @map = Hash.new()
33
+ columns.each do |c|
34
+ @map[c] = c
35
+ end
36
+
37
+ end
38
+
39
+ # Create a mapping from an old column name to a new one.
40
+ # If the new name is nil, specify that the old column shouldn't
41
+ # appear in this new view.
42
+ def map_column(old_name, new_name)
43
+ unless @map.include?(old_name)
44
+ raise ActiveRecord::ActiveRecordError, "column #{old_name} not found, can't be mapped"
45
+ end
46
+ if new_name.nil?
47
+ @map.delete old_name
48
+ @columns.delete old_name
49
+ else
50
+ @map[old_name] = new_name
51
+ end
52
+ end
53
+
54
+ def select_cols
55
+ @columns
56
+ end
57
+
58
+ def view_cols
59
+ @columns.map { |c| @map[c] }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,77 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters # :nodoc:
3
+ module SchemaStatements
4
+ def self.included(base)
5
+ base.alias_method_chain :drop_table, :cascade
6
+ end
7
+
8
+ # Create a view.
9
+ # The +options+ hash can include the following keys:
10
+ # [<tt>:check_option</tt>]
11
+ # Specify restrictions for inserts or updates in updatable views. ANSI SQL 92 defines two check option
12
+ # values: CASCADED and LOCAL. See your database documentation for allowed values.
13
+ def create_view(name, select_query, options={})
14
+ if supports_views?
15
+ view_definition = ViewDefinition.new(self, select_query)
16
+
17
+ yield view_definition
18
+
19
+ if options[:force]
20
+ drop_view(name) rescue nil
21
+ end
22
+
23
+ create_sql = "CREATE VIEW "
24
+ create_sql << "#{quote_table_name(name)} "
25
+ if supports_view_columns_definition? && !view_definition.to_sql.blank?
26
+ create_sql << "("
27
+ create_sql << view_definition.to_sql
28
+ create_sql << ") "
29
+ end
30
+ create_sql << "AS #{view_definition.select_query}"
31
+ create_sql << " WITH #{options[:check_option]} CHECK OPTION" if options[:check_option]
32
+ execute create_sql
33
+ end
34
+ end
35
+
36
+ # Also creates a view, with the specific purpose of remapping column names
37
+ # to make non-ActiveRecord tables friendly with the naming
38
+ # conventions, while maintaining legacy app compatibility.
39
+ def create_mapping_view(old_name, new_name, options = {})
40
+ return unless supports_views?
41
+
42
+ col_names = columns(old_name).collect { |col| col.name.to_sym }
43
+ mapper = MappingDefinition.new(col_names)
44
+
45
+ yield mapper
46
+
47
+ if options[:force]
48
+ drop_view(new_name) rescue nil
49
+ end
50
+
51
+ view_sql = "CREATE VIEW #{new_name} "
52
+ if supports_view_columns_definition?
53
+ view_sql << "(#{mapper.view_cols.collect { |c| quote_column_name(c) }.join(', ')}) "
54
+ end
55
+ view_sql << "AS SELECT #{mapper.select_cols.collect { |c| quote_column_name(c) }.join(', ')} FROM #{old_name}"
56
+ execute view_sql
57
+ end
58
+
59
+ def drop_table_with_cascade(table_name, options = {})
60
+ execute "DROP TABLE #{quote_table_name(table_name)} CASCADE"
61
+ end
62
+
63
+ # Drop a view.
64
+ # The +options+ hash can include the following keys:
65
+ # [<tt>:drop_behavior</tt>]
66
+ # Specify the drop behavior. ANSI SQL 92 defines two drop behaviors, CASCADE and RESTRICT. See your
67
+ # database documentation to determine what drop behaviors are available.
68
+ def drop_view(name, options={})
69
+ if supports_views?
70
+ drop_sql = "DROP VIEW #{name}"
71
+ drop_sql << " #{options[:drop_behavior]}" if options[:drop_behavior]
72
+ execute drop_sql
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,35 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module AbstractAdapter
4
+ def self.included(base)
5
+ base.alias_method_chain :disable_referential_integrity, :views_excluded
6
+ end
7
+
8
+ # Subclasses should override and return true if they support views.
9
+ def supports_views?
10
+ return false
11
+ end
12
+
13
+ def disable_referential_integrity_with_views_excluded(&block)
14
+ self.class.send(:alias_method, :tables, :base_tables)
15
+ disable_referential_integrity_without_views_excluded(&block)
16
+ ensure
17
+ self.class.send(:alias_method, :tables, :tables_with_views_included)
18
+ end
19
+
20
+ def supports_view_columns_definition?
21
+ true
22
+ end
23
+
24
+ # Get a list of all views for the current database
25
+ def views(name = nil)
26
+ raise NotImplementedError, "views is an abstract method"
27
+ end
28
+
29
+ # Get the select statement for the specified view
30
+ def view_select_statement(view, name=nil)
31
+ raise NotImplementedError, "view_select_statement is an abstract method"
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,58 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module MysqlAdapter
4
+ def self.included(base)
5
+ if base.private_method_defined?(:supports_views?)
6
+ base.send(:public, :supports_views?)
7
+ end
8
+ end
9
+
10
+ # Returns true as this adapter supports views.
11
+ def supports_views?
12
+ true
13
+ end
14
+
15
+ def base_tables(name = nil) #:nodoc:
16
+ tables = []
17
+ execute("SHOW FULL TABLES WHERE TABLE_TYPE='BASE TABLE'").each{|row| tables << row[0]}
18
+ tables
19
+ end
20
+ alias nonview_tables base_tables
21
+
22
+ def views(name = nil) #:nodoc:
23
+ views = []
24
+ execute("SHOW FULL TABLES WHERE TABLE_TYPE='VIEW'").each{|row| views << row[0]}
25
+ views
26
+ end
27
+
28
+ def structure_dump
29
+ structure = ""
30
+ base_tables.each do |table|
31
+ structure += select_one("SHOW CREATE TABLE #{quote_table_name(table)}")["Create Table"] + ";\n\n"
32
+ end
33
+
34
+ views.each do |view|
35
+ structure += select_one("SHOW CREATE VIEW #{quote_table_name(view)}")["Create View"] + ";\n\n"
36
+ end
37
+
38
+ return structure
39
+ end
40
+
41
+ # Get the view select statement for the specified table.
42
+ def view_select_statement(view, name=nil)
43
+ begin
44
+ execute("SHOW CREATE VIEW #{view}", name).each do |row|
45
+ return convert_statement(row[1]) if row[0] == view
46
+ end
47
+ rescue ActiveRecord::StatementInvalid => e
48
+ raise "No view called #{view} found"
49
+ end
50
+ end
51
+
52
+ private
53
+ def convert_statement(s)
54
+ s.gsub(/.* AS (select .*)/, '\1').gsub(/#{quote_table_name(ActiveRecord::Base.configurations[Rails.env]["database"])}\./, '')
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module OciAdapter
4
+ # Returns true as this adapter supports views.
5
+ def supports_views?
6
+ true
7
+ end
8
+
9
+ def base_tables(name = nil) #:nodoc:
10
+ tables = []
11
+ execute("SELECT TABLE_NAME FROM USER_TABLES", name).each { |row| tables << row[0] }
12
+ tables
13
+ end
14
+ alias nonview_tables base_tables
15
+
16
+ def views(name = nil) #:nodoc:
17
+ views = []
18
+ execute("SELECT VIEW_NAME FROM USER_VIEWS", name).each { |row| views << row[0] }
19
+ views
20
+ end
21
+
22
+ # Get the view select statement for the specified table.
23
+ def view_select_statement(view, name=nil)
24
+ row = execute("SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = '#{view}'", name).each do |row|
25
+ return row[0]
26
+ end
27
+ raise "No view called #{view} found"
28
+ end
29
+
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module OracleAdapter
4
+ # Returns true as this adapter supports views.
5
+ def supports_views?
6
+ true
7
+ end
8
+
9
+ def base_tables(name = nil) #:nodoc:
10
+ tables = []
11
+ execute("SELECT TABLE_NAME FROM USER_TABLES", name).each { |row| tables << row[0] }
12
+ tables
13
+ end
14
+ alias nonview_tables base_tables
15
+
16
+ def views(name = nil) #:nodoc:
17
+ views = []
18
+ execute("SELECT VIEW_NAME FROM USER_VIEWS", name).each { |row| views << row[0] }
19
+ views
20
+ end
21
+
22
+ # Get the view select statement for the specified table.
23
+ def view_select_statement(view, name=nil)
24
+ row = execute("SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = '#{view}'", name).each do |row|
25
+ return row[0]
26
+ end
27
+ raise "No view called #{view} found"
28
+ end
29
+
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module PostgreSQLAdapter
4
+ def self.included(base)
5
+ base.alias_method_chain :tables, :views_included
6
+ end
7
+ # Returns true as this adapter supports views.
8
+ def supports_views?
9
+ true
10
+ end
11
+
12
+ def tables_with_views_included(name = nil)
13
+ q = <<-SQL
14
+ SELECT table_name, table_type
15
+ FROM information_schema.tables
16
+ WHERE table_schema IN (#{schemas})
17
+ AND table_type IN ('BASE TABLE', 'VIEW')
18
+ SQL
19
+
20
+ query(q, name).map { |row| row[0] }
21
+ end
22
+
23
+ def base_tables(name = nil)
24
+ q = <<-SQL
25
+ SELECT table_name, table_type
26
+ FROM information_schema.tables
27
+ WHERE table_schema IN (#{schemas})
28
+ AND table_type = 'BASE TABLE'
29
+ SQL
30
+
31
+ query(q, name).map { |row| row[0] }
32
+ end
33
+ alias nonview_tables base_tables
34
+
35
+ def views(name = nil) #:nodoc:
36
+ q = <<-SQL
37
+ SELECT table_name, table_type
38
+ FROM information_schema.tables
39
+ WHERE table_schema IN (#{schemas})
40
+ AND table_type = 'VIEW'
41
+ SQL
42
+
43
+ query(q, name).map { |row| row[0] }
44
+ end
45
+
46
+ def view_select_statement(view, name = nil)
47
+ q = <<-SQL
48
+ SELECT view_definition
49
+ FROM information_schema.views
50
+ WHERE table_catalog = (SELECT catalog_name FROM information_schema.information_schema_catalog_name)
51
+ AND table_schema IN (#{schemas})
52
+ AND table_name = '#{view}'
53
+ SQL
54
+
55
+ select_value(q, name) or raise "No view called #{view} found"
56
+ end
57
+
58
+ private
59
+
60
+ def schemas
61
+ schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,62 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module SQLiteAdapter
4
+ def supports_views?
5
+ true
6
+ end
7
+
8
+ def tables(name = nil) #:nodoc:
9
+ sql = <<-SQL
10
+ SELECT name
11
+ FROM sqlite_master
12
+ WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
13
+ SQL
14
+
15
+ execute(sql, name).map do |row|
16
+ row[0]
17
+ end
18
+ end
19
+
20
+ def base_tables(name = nil)
21
+ sql = <<-SQL
22
+ SELECT name
23
+ FROM sqlite_master
24
+ WHERE (type = 'table') AND NOT name = 'sqlite_sequence'
25
+ SQL
26
+
27
+ execute(sql, name).map do |row|
28
+ row[0]
29
+ end
30
+ end
31
+ alias nonview_tables base_tables
32
+
33
+ def views(name = nil)
34
+ sql = <<-SQL
35
+ SELECT name
36
+ FROM sqlite_master
37
+ WHERE type = 'view' AND NOT name = 'sqlite_sequence'
38
+ SQL
39
+
40
+ execute(sql, name).map do |row|
41
+ row[0]
42
+ end
43
+ end
44
+
45
+ # Get the view select statement for the specified table.
46
+ def view_select_statement(view, name = nil)
47
+ sql = <<-SQL
48
+ SELECT sql
49
+ FROM sqlite_master
50
+ WHERE name = '#{view}' AND NOT name = 'sqlite_sequence'
51
+ SQL
52
+
53
+ select_value(sql, name) or raise "No view called #{view} found"
54
+ end
55
+
56
+ def supports_view_columns_definition?
57
+ false
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,43 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module SQLServerAdapter
4
+ # Returns true as this adapter supports views.
5
+ def supports_views?
6
+ true
7
+ end
8
+
9
+ # Get all of the non-view tables from the currently connected schema
10
+ def base_tables(name = nil)
11
+ # this is untested
12
+ select_values("SELECT table_name FROM information_schema.tables", name)
13
+ end
14
+ alias nonview_tables base_tables
15
+
16
+ # Returns all the view names from the currently connected schema.
17
+ def views(name = nil)
18
+ select_values("SELECT table_name FROM information_schema.views", name)
19
+ end
20
+
21
+ # Get the view select statement for the specified view.
22
+ def view_select_statement(view, name=nil)
23
+ q =<<-ENDSQL
24
+ SELECT view_definition FROM information_schema.views
25
+ WHERE table_name = '#{view}'
26
+ ENDSQL
27
+
28
+ view_def = select_value(q, name)
29
+
30
+ if view_def
31
+ return convert_statement(view_def)
32
+ else
33
+ raise "No view called #{view} found"
34
+ end
35
+ end
36
+
37
+ private
38
+ def convert_statement(s)
39
+ s.sub(/^CREATE.* AS (select .*)/i, '\1').gsub(/\n/, '')
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,18 @@
1
+
2
+ module RailsSqlViews
3
+ module Loader
4
+ SUPPORTED_ADAPTERS = %w( Mysql PostgreSQL SQLServer SQLite )
5
+
6
+ def self.load_extensions
7
+ SUPPORTED_ADAPTERS.each do |db|
8
+ if ActiveRecord::ConnectionAdapters.const_defined?("#{db}Adapter")
9
+ require "rails_sql_views/connection_adapters/#{db.downcase}_adapter"
10
+ ActiveRecord::ConnectionAdapters.const_get("#{db}Adapter").class_eval do
11
+ include RailsSqlViews::ConnectionAdapters::AbstractAdapter
12
+ include RailsSqlViews::ConnectionAdapters.const_get("#{db}Adapter")
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,98 @@
1
+ module RailsSqlViews
2
+ module SchemaDumper
3
+ def self.included(base)
4
+ base.alias_method_chain :trailer, :views
5
+ base.alias_method_chain :dump, :views
6
+ base.alias_method_chain :tables, :views_excluded
7
+
8
+ # A list of views which should not be dumped to the schema.
9
+ # Acceptable values are strings as well as regexp.
10
+ # This setting is only used if ActiveRecord::Base.schema_format == :ruby
11
+ base.cattr_accessor :ignore_views
12
+ base.ignore_views = []
13
+ end
14
+
15
+ def trailer_with_views(stream)
16
+ # do nothing...we'll call this later
17
+ end
18
+
19
+ # Add views to the end of the dump stream
20
+ def dump_with_views(stream)
21
+ dump_without_views(stream)
22
+ begin
23
+ if @connection.supports_views?
24
+ views(stream)
25
+ end
26
+ rescue => e
27
+ if ActiveRecord::Base.logger
28
+ ActiveRecord::Base.logger.error "Unable to dump views: #{e}"
29
+ else
30
+ raise e
31
+ end
32
+ end
33
+ trailer_without_views(stream)
34
+ stream
35
+ end
36
+
37
+ # Add views to the stream
38
+ def views(stream)
39
+ @connection.views.sort.each do |v|
40
+ next if [ActiveRecord::Migrator.schema_migrations_table_name, ignore_views].flatten.any? do |ignored|
41
+ case ignored
42
+ when String then v == ignored
43
+ when Symbol then v == ignored.to_s
44
+ when Regexp then v =~ ignored
45
+ else
46
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_views accepts an array of String and / or Regexp values.'
47
+ end
48
+ end
49
+ view(v, stream)
50
+ end
51
+ end
52
+
53
+ # Add the specified view to the stream
54
+ def view(view, stream)
55
+ columns = @connection.columns(view).collect { |c| c.name }
56
+ begin
57
+ v = StringIO.new
58
+
59
+ v.print " create_view #{view.inspect}"
60
+ v.print ", #{@connection.view_select_statement(view).dump}"
61
+ v.print ", :force => true"
62
+ v.puts " do |v|"
63
+
64
+ columns.each do |column|
65
+ v.print " v.column :#{column}"
66
+ v.puts
67
+ end
68
+
69
+ v.puts " end"
70
+ v.puts
71
+
72
+ v.rewind
73
+ stream.print v.read
74
+ rescue => e
75
+ stream.puts "# Could not dump view #{view.inspect} because of following #{e.class}"
76
+ stream.puts "# #{e.message}"
77
+ stream.puts
78
+ end
79
+
80
+ stream
81
+ end
82
+
83
+ def tables_with_views_excluded(stream)
84
+ @connection.base_tables.sort.each do |tbl|
85
+ next if [ActiveRecord::Migrator.schema_migrations_table_name, ignore_tables].flatten.any? do |ignored|
86
+ case ignored
87
+ when String then tbl == ignored
88
+ when Regexp then tbl =~ ignored
89
+ else
90
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
91
+ end
92
+ end
93
+ table(tbl, stream)
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,9 @@
1
+ module RailsSqlViews
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 7
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubaidh-rails_sql_views
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.1
5
+ platform: ruby
6
+ authors:
7
+ - Anthony Eden
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-10 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.8.3
34
+ version:
35
+ description: " Library which adds SQL Views to Rails.\n"
36
+ email: anthonyeden@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - CHANGELOG
45
+ - README
46
+ - Rakefile
47
+ - lib/active_record/view.rb
48
+ - lib/core_ext/module.rb
49
+ - lib/rails_sql_views/connection_adapters/abstract/schema_definitions.rb
50
+ - lib/rails_sql_views/connection_adapters/abstract/schema_statements.rb
51
+ - lib/rails_sql_views/connection_adapters/abstract_adapter.rb
52
+ - lib/rails_sql_views/connection_adapters/mysql_adapter.rb
53
+ - lib/rails_sql_views/connection_adapters/oci_adapter.rb
54
+ - lib/rails_sql_views/connection_adapters/oracle_adapter.rb
55
+ - lib/rails_sql_views/connection_adapters/postgresql_adapter.rb
56
+ - lib/rails_sql_views/connection_adapters/sqlite_adapter.rb
57
+ - lib/rails_sql_views/connection_adapters/sqlserver_adapter.rb
58
+ - lib/rails_sql_views/loader.rb
59
+ - lib/rails_sql_views/schema_dumper.rb
60
+ - lib/rails_sql_views/version.rb
61
+ - lib/rails_sql_views.rb
62
+ has_rdoc: false
63
+ homepage: http://activewarehouse.rubyforge.org/rails_sql_views
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --exclude
69
+ - .
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project: activewarehouse
87
+ rubygems_version: 1.3.5
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Adds SQL Views to Rails.
91
+ test_files: []
92
+