rubaidh-rails_sql_views 0.7.1

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.
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
+