lunks-rails_sql_views 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
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)
data/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2006-2008 Anthony Eden
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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. :-)
@@ -0,0 +1,78 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/packagetask'
5
+ require 'rake/gempackagetask'
6
+
7
+ require File.join(File.dirname(__FILE__), 'lib/rails_sql_views', 'version')
8
+
9
+ PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
10
+ PKG_NAME = 'rails_sql_views'
11
+ PKG_VERSION = RailsSqlViews::VERSION::STRING + PKG_BUILD
12
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
13
+ PKG_DESTINATION = ENV["PKG_DESTINATION"] || "../#{PKG_NAME}"
14
+
15
+ RELEASE_NAME = "REL #{PKG_VERSION}"
16
+
17
+ RUBY_FORGE_PROJECT = "activewarehouse"
18
+ RUBY_FORGE_USER = "aeden"
19
+
20
+ desc 'Default: run unit tests.'
21
+ task :default => :test
22
+
23
+ desc 'Test the library.'
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.pattern = 'test/**/*_test.rb'
27
+ t.verbose = true
28
+ end
29
+
30
+ namespace :rcov do
31
+ desc 'Measures test coverage'
32
+ task :test do
33
+ rm_f 'coverage.data'
34
+ mkdir 'coverage' unless File.exist?('coverage')
35
+ rcov = "rcov --aggregate coverage.data --text-summary -Ilib"
36
+ system("#{rcov} test/*_test.rb test/**/*_test.rb")
37
+ system("open coverage/index.html") if PLATFORM['darwin']
38
+ end
39
+ end
40
+
41
+ desc 'Generate documentation library.'
42
+ Rake::RDocTask.new(:rdoc) do |rdoc|
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = 'Rails SQL Views'
45
+ rdoc.options << '--line-numbers' << '--inline-source'
46
+ rdoc.rdoc_files.include('README')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
49
+
50
+ begin
51
+ require 'jeweler'
52
+ Jeweler::Tasks.new do |s|
53
+ s.name = "ctreatma-rails_sql_views"
54
+ s.summary = "Library which adds SQL Views to ActiveRecord."
55
+ s.email = "charles.treatman@gmail.com"
56
+ s.homepage = "http://github.com/ctreatma/rails_sql_views"
57
+ s.description = "Adds support for using SQL views within ActiveRecord"
58
+ s.authors = ["Charles Treatman", "Anthony Eden"]
59
+ s.files = FileList[
60
+ "CHANGELOG",
61
+ "README",
62
+ "Rakefile",
63
+ "{bin,lib}/**/*"
64
+ ]
65
+ s.add_dependency 'activerecord'
66
+ s.add_development_dependency 'flexmock'
67
+ s.add_development_dependency 'pg'
68
+ s.add_development_dependency 'mysql'
69
+ s.add_development_dependency 'mysql2'
70
+ end
71
+ rescue LoadError
72
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
73
+ end
74
+
75
+ desc "Publish the API documentation"
76
+ task :pdoc => [:rdoc] do
77
+ Rake::SshDirPublisher.new("aeden@rubyforge.org", "/var/www/gforge-projects/activewarehouse/rails_sql_views/rdoc", "rdoc").upload
78
+ end
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+
2
+ - Use pre-existing Adapter#supports_views? where possible
@@ -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,51 @@
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
+ $rails_sql_views_included = false
37
+
38
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
39
+ include RailsSqlViews::ConnectionAdapters::SchemaStatements
40
+ def self.inherited(sub)
41
+ unless $rails_sql_views_included && (Rails.env.test? || Rails.env.cucumber?)
42
+ RailsSqlViews::Loader.load_extensions
43
+ end
44
+ end
45
+ end
46
+
47
+ ActiveRecord::SchemaDumper.class_eval do
48
+ include RailsSqlViews::SchemaDumper
49
+ end
50
+
51
+ 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,71 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters # :nodoc:
3
+ module SchemaStatements
4
+ # Create a view.
5
+ # The +options+ hash can include the following keys:
6
+ # [<tt>:check_option</tt>]
7
+ # Specify restrictions for inserts or updates in updatable views. ANSI SQL 92 defines two check option
8
+ # values: CASCADED and LOCAL. See your database documentation for allowed values.
9
+ def create_view(name, select_query, options={})
10
+ if supports_views?
11
+ view_definition = ViewDefinition.new(self, select_query)
12
+
13
+ if block_given?
14
+ yield view_definition
15
+ end
16
+
17
+ if options[:force]
18
+ drop_view(name) rescue nil
19
+ end
20
+
21
+ create_sql = "CREATE VIEW "
22
+ create_sql << "#{quote_table_name(name)} "
23
+ if supports_view_columns_definition? && !view_definition.to_sql.blank?
24
+ create_sql << "("
25
+ create_sql << view_definition.to_sql
26
+ create_sql << ") "
27
+ end
28
+ create_sql << "AS #{view_definition.select_query}"
29
+ create_sql << " WITH #{options[:check_option]} CHECK OPTION" if options[:check_option]
30
+ execute create_sql
31
+ end
32
+ end
33
+
34
+ # Also creates a view, with the specific purpose of remapping column names
35
+ # to make non-ActiveRecord tables friendly with the naming
36
+ # conventions, while maintaining legacy app compatibility.
37
+ def create_mapping_view(old_name, new_name, options = {})
38
+ return unless supports_views?
39
+
40
+ col_names = columns(old_name).collect { |col| col.name.to_sym }
41
+ mapper = MappingDefinition.new(col_names)
42
+
43
+ yield mapper
44
+
45
+ if options[:force]
46
+ drop_view(new_name) rescue nil
47
+ end
48
+
49
+ view_sql = "CREATE VIEW #{new_name} "
50
+ if supports_view_columns_definition?
51
+ view_sql << "(#{mapper.view_cols.collect { |c| quote_column_name(c) }.join(', ')}) "
52
+ end
53
+ view_sql << "AS SELECT #{mapper.select_cols.collect { |c| quote_column_name(c) }.join(', ')} FROM #{old_name}"
54
+ execute view_sql
55
+ end
56
+
57
+ # Drop a view.
58
+ # The +options+ hash can include the following keys:
59
+ # [<tt>:drop_behavior</tt>]
60
+ # Specify the drop behavior. ANSI SQL 92 defines two drop behaviors, CASCADE and RESTRICT. See your
61
+ # database documentation to determine what drop behaviors are available.
62
+ def drop_view(name, options={})
63
+ if supports_views?
64
+ drop_sql = "DROP VIEW #{quote_table_name(name)}"
65
+ drop_sql << " #{options[:drop_behavior]}" if options[:drop_behavior]
66
+ execute drop_sql
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,36 @@
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, :original_tables_method, :tables)
15
+ self.class.send(:alias_method, :tables, :base_tables)
16
+ disable_referential_integrity_without_views_excluded(&block)
17
+ ensure
18
+ self.class.send(:alias_method, :tables, :original_tables_method)
19
+ end
20
+
21
+ def supports_view_columns_definition?
22
+ true
23
+ end
24
+
25
+ # Get a list of all views for the current database
26
+ def views(name = nil)
27
+ raise NotImplementedError, "views is an abstract method"
28
+ end
29
+
30
+ # Get the select statement for the specified view
31
+ def view_select_statement(view, name=nil)
32
+ raise NotImplementedError, "view_select_statement is an abstract method"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,66 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module MysqlAdapter
4
+ REQUIRED_METHODS = [:supports_views?]
5
+
6
+ def self.included(base)
7
+ base.class_eval do
8
+ def self.method_added(method)
9
+ public(method) if REQUIRED_METHODS.include?(method) && !self.public_method_defined?(method)
10
+ end
11
+ end
12
+ end
13
+
14
+ # Returns true as this adapter supports views.
15
+ def supports_views?
16
+ true
17
+ end
18
+
19
+ def base_tables(name = nil) #:nodoc:
20
+ tables = []
21
+ execute("SHOW FULL TABLES WHERE TABLE_TYPE='BASE TABLE'").each{|row| tables << row[0]}
22
+ tables
23
+ end
24
+ alias nonview_tables base_tables
25
+
26
+ def views(name = nil) #:nodoc:
27
+ views = []
28
+ execute("SHOW FULL TABLES WHERE TABLE_TYPE='VIEW'").each{|row| views << row[0]}
29
+ views
30
+ end
31
+
32
+ def tables_with_views_included(name = nil)
33
+ nonview_tables(name) + views(name)
34
+ end
35
+
36
+ def structure_dump
37
+ structure = ""
38
+ base_tables.each do |table|
39
+ structure += select_one("SHOW CREATE TABLE #{quote_table_name(table)}")["Create Table"] + ";\n\n"
40
+ end
41
+
42
+ views.each do |view|
43
+ structure += select_one("SHOW CREATE VIEW #{quote_table_name(view)}")["Create View"] + ";\n\n"
44
+ end
45
+
46
+ return structure
47
+ end
48
+
49
+ # Get the view select statement for the specified table.
50
+ def view_select_statement(view, name=nil)
51
+ begin
52
+ row = execute("SHOW CREATE VIEW #{view}", name).each do |row|
53
+ return convert_statement(row[1]) if row[0] == view
54
+ end
55
+ rescue ActiveRecord::StatementInvalid => e
56
+ raise "No view called #{view} found"
57
+ end
58
+ end
59
+
60
+ private
61
+ def convert_statement(s)
62
+ s.gsub!(/.* AS (select .*)/, '\1')
63
+ end
64
+ end
65
+ end
66
+ 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,39 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module OracleEnhancedAdapter
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
+ cursor = execute("SELECT TABLE_NAME FROM ALL_TABLES WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'", name)
12
+ while row = cursor.fetch
13
+ tables << row[0]
14
+ end
15
+ tables
16
+ end
17
+ alias nonview_tables base_tables
18
+
19
+ def views(name = nil) #:nodoc:
20
+ views = []
21
+ cursor = execute("SELECT VIEW_NAME FROM ALL_VIEWS WHERE owner = SYS_CONTEXT('userenv', 'current_schema')", name)
22
+ while row = cursor.fetch
23
+ views << row[0]
24
+ end
25
+ views
26
+ end
27
+
28
+ # Get the view select statement for the specified table.
29
+ def view_select_statement(view, name=nil)
30
+ cursor = execute("SELECT TEXT FROM ALL_VIEWS WHERE VIEW_NAME = '#{view}' AND owner = SYS_CONTEXT('userenv', 'current_schema')", name)
31
+ if row = cursor.fetch
32
+ return row[0]
33
+ else
34
+ raise "No view called #{view} found"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,72 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module OracleEnhancedAdapter
4
+ <<<<<<< HEAD
5
+ def self.included(base)
6
+ base.alias_method_chain :tables, :views_included
7
+ end
8
+ =======
9
+ >>>>>>> Add support for oracle_enhanced adapter. Don't alias tables in PostgreSQL adapter if it's already been aliased.
10
+ # Returns true as this adapter supports views.
11
+ def supports_views?
12
+ true
13
+ end
14
+
15
+ <<<<<<< HEAD
16
+ def tables_with_views_included(name = nil)
17
+ tables = []
18
+ sql = " SELECT TABLE_NAME FROM USER_TABLES
19
+ UNION
20
+ SELECT VIEW_NAME AS TABLE_NAME FROM USER_VIEWS"
21
+ cursor = execute(sql, name)
22
+ while row = cursor.fetch
23
+ tables << row[0].downcase
24
+ end
25
+ tables
26
+ end
27
+
28
+ =======
29
+ >>>>>>> Add support for oracle_enhanced adapter. Don't alias tables in PostgreSQL adapter if it's already been aliased.
30
+ def base_tables(name = nil) #:nodoc:
31
+ tables = []
32
+ cursor = execute("SELECT TABLE_NAME FROM USER_TABLES", name)
33
+ while row = cursor.fetch
34
+ <<<<<<< HEAD
35
+ tables << row[0].downcase
36
+ =======
37
+ tables << row[0]
38
+ >>>>>>> Add support for oracle_enhanced adapter. Don't alias tables in PostgreSQL adapter if it's already been aliased.
39
+ end
40
+ tables
41
+ end
42
+ alias nonview_tables base_tables
43
+
44
+ def views(name = nil) #:nodoc:
45
+ views = []
46
+ cursor = execute("SELECT VIEW_NAME FROM USER_VIEWS", name)
47
+ while row = cursor.fetch
48
+ <<<<<<< HEAD
49
+ views << row[0].downcase
50
+ =======
51
+ views << row[0]
52
+ >>>>>>> Add support for oracle_enhanced adapter. Don't alias tables in PostgreSQL adapter if it's already been aliased.
53
+ end
54
+ views
55
+ end
56
+
57
+ # Get the view select statement for the specified table.
58
+ def view_select_statement(view, name=nil)
59
+ <<<<<<< HEAD
60
+ view.upcase!
61
+ =======
62
+ >>>>>>> Add support for oracle_enhanced adapter. Don't alias tables in PostgreSQL adapter if it's already been aliased.
63
+ cursor = execute("SELECT TEXT FROM USER_VIEWS WHERE VIEW_NAME = '#{view}'", name)
64
+ if row = cursor.fetch
65
+ return row[0]
66
+ else
67
+ raise "No view called #{view} found"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ 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 unless method_defined?(:tables_with_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,69 @@
1
+ module RailsSqlViews
2
+ module ConnectionAdapters
3
+ module PostgreSQLAdapter
4
+ def self.included(base)
5
+ <<<<<<< HEAD
6
+ base.alias_method_chain :tables, :views_included unless method_defined?(:tables_with_views_included)
7
+ =======
8
+ base.alias_method_chain :tables, :views_included
9
+ >>>>>>> Make tests pass again for PostgreSQL and MySQL adapters.
10
+ end
11
+ # Returns true as this adapter supports views.
12
+ def supports_views?
13
+ true
14
+ end
15
+
16
+ def tables_with_views_included(name = nil)
17
+ q = <<-SQL
18
+ SELECT table_name, table_type
19
+ FROM information_schema.tables
20
+ WHERE table_schema IN (#{schemas})
21
+ AND table_type IN ('BASE TABLE', 'VIEW')
22
+ SQL
23
+
24
+ query(q, name).map { |row| row[0] }
25
+ end
26
+
27
+ def base_tables(name = nil)
28
+ q = <<-SQL
29
+ SELECT table_name, table_type
30
+ FROM information_schema.tables
31
+ WHERE table_schema IN (#{schemas})
32
+ AND table_type = 'BASE TABLE'
33
+ SQL
34
+
35
+ query(q, name).map { |row| row[0] }
36
+ end
37
+ alias nonview_tables base_tables
38
+
39
+ def views(name = nil) #:nodoc:
40
+ q = <<-SQL
41
+ SELECT table_name, table_type
42
+ FROM information_schema.tables
43
+ WHERE table_schema IN (#{schemas})
44
+ AND table_type = 'VIEW'
45
+ SQL
46
+
47
+ query(q, name).map { |row| row[0] }
48
+ end
49
+
50
+ def view_select_statement(view, name = nil)
51
+ q = <<-SQL
52
+ SELECT view_definition
53
+ FROM information_schema.views
54
+ WHERE table_catalog = (SELECT catalog_name FROM information_schema.information_schema_catalog_name)
55
+ AND table_schema IN (#{schemas})
56
+ AND table_name = '#{view}'
57
+ SQL
58
+
59
+ select_value(q, name) or raise "No view called #{view} found"
60
+ end
61
+
62
+ private
63
+
64
+ def schemas
65
+ schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
66
+ end
67
+ end
68
+ end
69
+ 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).gsub("CREATE VIEW #{view} AS ", "")) 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,20 @@
1
+
2
+ module RailsSqlViews
3
+ module Loader
4
+ SUPPORTED_ADAPTERS = %w( Mysql Mysql2 PostgreSQL SQLServer SQLite OracleEnhanced )
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
+ # prevent reloading extension when the environment is reloaded
14
+ $rails_sql_views_included = true
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,112 @@
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
+ # Optional: specify the order that in which views are created.
14
+ # This allows views to depend on and include fields from other views.
15
+ # It is not necessary to specify all the view names, just the ones that
16
+ # need to be created first
17
+ base.cattr_accessor :view_creation_order
18
+ base.view_creation_order = []
19
+ end
20
+
21
+ def trailer_with_views(stream)
22
+ # do nothing...we'll call this later
23
+ end
24
+
25
+ # Add views to the end of the dump stream
26
+ def dump_with_views(stream)
27
+ dump_without_views(stream)
28
+ begin
29
+ if @connection.supports_views?
30
+ views(stream)
31
+ end
32
+ rescue => e
33
+ if ActiveRecord::Base.logger
34
+ ActiveRecord::Base.logger.error "Unable to dump views: #{e}"
35
+ else
36
+ raise e
37
+ end
38
+ end
39
+ trailer_without_views(stream)
40
+ stream
41
+ end
42
+
43
+ # Add views to the stream
44
+ def views(stream)
45
+ if view_creation_order.empty?
46
+ sorted_views = @connection.views.sort
47
+ else
48
+ # set union, merge by joining arrays, removing dups
49
+ # this will float the view name sin view_creation_order to the top
50
+ # without requiring all the views to be specified
51
+ sorted_views = view_creation_order | @connection.views
52
+ end
53
+ sorted_views.each do |v|
54
+ next if [ActiveRecord::Migrator.schema_migrations_table_name, ignore_views].flatten.any? do |ignored|
55
+ case ignored
56
+ when String then v == ignored
57
+ when Symbol then v == ignored.to_s
58
+ when Regexp then v =~ ignored
59
+ else
60
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_views accepts an array of String and / or Regexp values.'
61
+ end
62
+ end
63
+ view(v, stream)
64
+ end
65
+ end
66
+
67
+ # Add the specified view to the stream
68
+ def view(view, stream)
69
+ columns = @connection.columns(view).collect { |c| c.name }
70
+ begin
71
+ v = StringIO.new
72
+
73
+ v.print " create_view #{view.inspect}"
74
+ v.print ", #{@connection.view_select_statement(view).dump}"
75
+ v.print ", :force => true"
76
+ v.puts " do |v|"
77
+
78
+ columns.each do |column|
79
+ v.print " v.column :#{column}"
80
+ v.puts
81
+ end
82
+
83
+ v.puts " end"
84
+ v.puts
85
+
86
+ v.rewind
87
+ stream.print v.read
88
+ rescue => e
89
+ stream.puts "# Could not dump view #{view.inspect} because of following #{e.class}"
90
+ stream.puts "# #{e.message}"
91
+ stream.puts
92
+ end
93
+
94
+ stream
95
+ end
96
+
97
+ def tables_with_views_excluded(stream)
98
+ @connection.base_tables.sort.each do |tbl|
99
+ next if [ActiveRecord::Migrator.schema_migrations_table_name, ignore_tables].flatten.any? do |ignored|
100
+ case ignored
101
+ when String then tbl == ignored
102
+ when Regexp then tbl =~ ignored
103
+ else
104
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
105
+ end
106
+ end
107
+ table(tbl, stream)
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,9 @@
1
+ module RailsSqlViews
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 7
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lunks-rails_sql_views
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Charles Treatman
9
+ - Anthony Eden
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-04-12 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ requirement: &70363715386660 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70363715386660
26
+ description: Adds support for using SQL views within ActiveRecord
27
+ email: charles.treatman@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files:
31
+ - LICENSE
32
+ - README
33
+ - TODO
34
+ files:
35
+ - CHANGELOG
36
+ - README
37
+ - Rakefile
38
+ - lib/active_record/view.rb
39
+ - lib/core_ext/module.rb
40
+ - lib/ctreatma-rails_sql_views.rb
41
+ - lib/rails_sql_views/connection_adapters/abstract/schema_definitions.rb
42
+ - lib/rails_sql_views/connection_adapters/abstract/schema_statements.rb
43
+ - lib/rails_sql_views/connection_adapters/abstract_adapter.rb
44
+ - lib/rails_sql_views/connection_adapters/mysql_adapter.rb
45
+ - lib/rails_sql_views/connection_adapters/oci_adapter.rb
46
+ - lib/rails_sql_views/connection_adapters/oracle_adapter.rb
47
+ - lib/rails_sql_views/connection_adapters/oracleenhanced_adapter.rb
48
+ - lib/rails_sql_views/connection_adapters/oracleenhanced_adapter.rb.orig
49
+ - lib/rails_sql_views/connection_adapters/postgresql_adapter.rb
50
+ - lib/rails_sql_views/connection_adapters/postgresql_adapter.rb.orig
51
+ - lib/rails_sql_views/connection_adapters/sqlite_adapter.rb
52
+ - lib/rails_sql_views/connection_adapters/sqlserver_adapter.rb
53
+ - lib/rails_sql_views/loader.rb
54
+ - lib/rails_sql_views/schema_dumper.rb
55
+ - lib/rails_sql_views/version.rb
56
+ - LICENSE
57
+ - TODO
58
+ homepage: http://github.com/lunks/rails_sql_views
59
+ licenses: []
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubyforge_project:
78
+ rubygems_version: 1.8.15
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Library which adds SQL Views to ActiveRecord.
82
+ test_files: []