spectacles 1.0.1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 93b01de9ec07bd98a69b60b3c1b15ae85a8e3b12
4
- data.tar.gz: d25a236871d3d35208e157419b69f25f935a1437
2
+ SHA256:
3
+ metadata.gz: e2459706103157e49b71cf2cc254abd49a98a547c0e997c2a1fe7329d368c8e6
4
+ data.tar.gz: c65c091166383559ff45c37886b48c8a6252082765bb18c2ea40a63f351318d6
5
5
  SHA512:
6
- metadata.gz: 315b5606b2b1fb645b81846c4e3a49e8a018b9cfef29593094272636e7a31667e6a45121bc970952acbbd3c1f28197a05ab49dfebc166c3492cff2502b5a8401
7
- data.tar.gz: aef7db4bcfdd525337c5744f1d45962eff3edd691cd281157ddc87e432f03adffcc420a61a585732b776aa79f5053a13fb2b007a8b681bf56f872882803ec1e4
6
+ metadata.gz: 55a1ff8f400aa2d5d91bab39cde96f81e6bfe1d78e39a298896f830bd34469ec687c34744b463f769cbecb66031d12551b98f5aa08241c9cf492b4aab58b546f
7
+ data.tar.gz: 65440da01035016a79411268ff5239a5329b9c6227112a9b69760a662e6f41def749f1f0d844a6cd60f8a028685f23a1024a98f832256cff385a639f6b04db95
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ jdk:
3
+ - openjdk8
4
+ rvm:
5
+ - 2.2
6
+ - 2.3
7
+ - 2.4
8
+ - 2.5
9
+ - 2.6
10
+ - jruby
11
+ cache: bundler
12
+ services:
13
+ - mysql
14
+ - postgresql
data/Gemfile CHANGED
@@ -10,7 +10,6 @@ platforms :jruby do
10
10
  end
11
11
 
12
12
  platforms :ruby do
13
- gem "mysql"
14
13
  gem "mysql2"
15
14
  gem "pg"
16
15
  gem "sqlite3"
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2012 Adam Hutchison, Brandon Dewitt
3
+ Copyright (c) 2012-2019 Adam Hutchison, Brandon Dewitt
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy of
6
6
  this software and associated documentation files (the "Software"), to deal in
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require "bundler/gem_tasks"
2
2
  require 'rake/testtask'
3
3
 
4
4
  namespace :test do
5
- adapters = [ :mysql, :mysql2, :postgresql, :sqlite3 ]
5
+ adapters = [ :mysql2, :postgresql, :sqlite3 ]
6
6
  task :all => [ :spectacles ] + adapters
7
7
 
8
8
  adapters.each do |adapter|
data/Readme.rdoc CHANGED
@@ -1,6 +1,8 @@
1
+ {<img src="https://travis-ci.org/liveh2o/spectacles.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/liveh2o/spectacles] {<img src="https://badge.fury.io/rb/spectacles.svg" alt="Gem Version" />}[https://badge.fury.io/rb/spectacles]
2
+
1
3
  = Spectacles
2
4
 
3
- Spectacles adds database view functionality to ActiveRecord. It is heavily inspired by Rails SQL Views (http://github.com/aeden/rails_sql_views/) and built from the ground-up to work with Rails 3.2+.
5
+ Spectacles adds database view functionality to ActiveRecord. It is heavily inspired by Rails SQL Views (created by https://github.com/aeden but no longer maintained) and built from the ground up to work with Rails 3.2+.
4
6
 
5
7
  Spectacles provides the ability to create views in migrations using a similar format to creating tables. It also provides an abstract view class that inherits from ActiveRecord::Base that can be used to create view-backed models.
6
8
 
@@ -57,7 +59,7 @@ they are kind of a cross between tables (which persist data) and views
57
59
  # just like Spectacles::View
58
60
  end
59
61
 
60
- Because these materialized views cache a snapshot of the data as it
62
+ Because materialized views cache a snapshot of the data as it
61
63
  exists at a point in time (typically when the view was created), you
62
64
  need to manually _refresh_ the view when new data is added to the
63
65
  original tables. You can do this with the +#refresh!+ method on
@@ -115,4 +117,4 @@ to affect how the new view is created:
115
117
 
116
118
  = License
117
119
 
118
- Spectacles is licensed under MIT license (Read lib/spectactles.rb for full license)
120
+ Spectacles is licensed under MIT license (Read the LICENSE file for full license)
@@ -0,0 +1,10 @@
1
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
2
+ class << self
3
+ alias_method(:_spectacles_orig_inherited, :inherited) if method_defined?(:inherited)
4
+
5
+ def inherited(_subclass)
6
+ ::Spectacles::load_adapters
7
+ _spectacles_orig_inherited(_subclass) if methods.include?(:_spectacles_orig_inherited)
8
+ end
9
+ end
10
+ end
@@ -1,9 +1,10 @@
1
1
  module Spectacles
2
2
  class Configuration
3
- attr_accessor :enable_schema_dump
3
+ attr_accessor :enable_schema_dump, :skip_views
4
4
 
5
5
  def initialize
6
6
  @enable_schema_dump = true
7
+ @skip_views = []
7
8
  end
8
9
  end
9
10
  end
@@ -3,15 +3,23 @@ module Spectacles
3
3
  self.abstract_class = true
4
4
 
5
5
  def self.new(*)
6
- raise NotImplementedError
6
+ raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
7
7
  end
8
8
 
9
9
  def self.materialized_view_exists?
10
10
  self.connection.materialized_view_exists?(self.view_name)
11
11
  end
12
12
 
13
- def self.refresh!
14
- self.connection.refresh_materialized_view(self.view_name)
13
+ def self.refresh!(concurrently: false)
14
+ if concurrently
15
+ self.connection.refresh_materialized_view_concurrently(self.view_name)
16
+ else
17
+ self.connection.refresh_materialized_view(self.view_name)
18
+ end
19
+ end
20
+
21
+ def self.refresh_concurrently!
22
+ refresh!(concurrently: true)
15
23
  end
16
24
 
17
25
  class << self
@@ -5,11 +5,14 @@ module Spectacles
5
5
  class Railtie < ::Rails::Railtie
6
6
  config.spectacles = ::ActiveSupport::OrderedOptions.new
7
7
 
8
- initializer "spectacles.configure" do |app|
8
+ initializer 'spectacles.configure' do |app|
9
9
  Spectacles.configure do |config|
10
- if app.config.spectacles.has_key?(:enable_schema_dump)
10
+ if app.config.spectacles.key?(:enable_schema_dump)
11
11
  config.enable_schema_dump = app.config.spectacles[:enable_schema_dump]
12
12
  end
13
+ if app.config.spectacles.key?(:skip_views)
14
+ config.skip_views = app.config.spectacles[:skip_views]
15
+ end
13
16
  end
14
17
  end
15
18
  end
@@ -1,18 +1,21 @@
1
1
  module Spectacles
2
2
  module SchemaDumper
3
3
  def self.dump_views(stream, connection)
4
- unless (Spectacles.config.enable_schema_dump == false)
4
+ unless Spectacles.config.enable_schema_dump == false
5
5
  connection.views.sort.each do |view|
6
+ next if skip_view?(view)
6
7
  dump_view(stream, connection, view)
7
8
  end
8
9
  end
9
10
  end
10
11
 
11
- def self.dump_materialized_views(stream, connection)
12
- unless (Spectacles.config.enable_schema_dump == false)
12
+ def self.dump_materialized_views(dumper, stream, connection)
13
+ unless Spectacles.config.enable_schema_dump == false
13
14
  if connection.supports_materialized_views?
14
15
  connection.materialized_views.sort.each do |view|
16
+ next if skip_view?(view)
15
17
  dump_materialized_view(stream, connection, view)
18
+ dumper.send(:indexes, view, stream)
16
19
  end
17
20
  end
18
21
  end
@@ -45,7 +48,7 @@ module Spectacles
45
48
  def self.format_option_hash(hash)
46
49
  hash.map do |key, value|
47
50
  "#{key}: #{format_option_value(value)}"
48
- end.join(", ")
51
+ end.join(', ')
49
52
  end
50
53
 
51
54
  def self.format_option_value(value)
@@ -57,5 +60,9 @@ module Spectacles
57
60
  else raise "can't format #{value.inspect}"
58
61
  end
59
62
  end
63
+
64
+ def self.skip_view?(view)
65
+ Spectacles.config.skip_views.any? { |item| item === view }
66
+ end
60
67
  end
61
68
  end
@@ -19,8 +19,8 @@ module Spectacles
19
19
  end
20
20
 
21
21
  def create_view_statement(view_name, create_query)
22
- query = "CREATE VIEW ? AS #{create_query}"
23
- query_array = [query, view_name.to_s]
22
+ #query = "CREATE VIEW ? AS #{create_query}"
23
+ #query_array = [query, view_name.to_s]
24
24
 
25
25
  #return ActiveRecord::Base.__send__(:sanitize_sql_array, query_array)
26
26
  "CREATE VIEW #{view_name} AS #{create_query}"
@@ -32,8 +32,8 @@ module Spectacles
32
32
  end
33
33
 
34
34
  def drop_view_statement(view_name)
35
- query = "DROP VIEW IF EXISTS ? "
36
- query_array = [query, view_name.to_s]
35
+ #query = "DROP VIEW IF EXISTS ? "
36
+ #query_array = [query, view_name.to_s]
37
37
 
38
38
  #return ActiveRecord::Base.__send__(:sanitize_sql_array, query_array)
39
39
  "DROP VIEW IF EXISTS #{view_name} "
@@ -74,6 +74,10 @@ module Spectacles
74
74
  def refresh_materialized_view(view_name)
75
75
  raise NotImplementedError, "Override refresh_materialized_view for your db adapter in #{self.class}"
76
76
  end
77
+
78
+ def refresh_materialized_view_concurrently(view_name)
79
+ raise NotImplementedError, "Override refresh_materialized_view_concurrently for your db adapter in #{self.class}"
80
+ end
77
81
  end
78
82
  end
79
83
  end
@@ -1,9 +1,47 @@
1
- require 'spectacles/schema_statements/mysql_adapter'
2
-
3
1
  module Spectacles
4
2
  module SchemaStatements
5
3
  module Mysql2Adapter
6
- include Spectacles::SchemaStatements::MysqlAdapter
4
+ include Spectacles::SchemaStatements::AbstractAdapter
5
+
6
+ # overrides the #tables method from ActiveRecord's MysqlAdapter
7
+ # to return only tables, and not views.
8
+ def tables(name = nil, database = nil, like = nil)
9
+ database = database ? quote_table_name(database) : "DATABASE()"
10
+ by_name = like ? "AND table_name LIKE #{quote(like)}" : ""
11
+
12
+ sql = <<-SQL.squish
13
+ SELECT table_name, table_type
14
+ FROM information_schema.tables
15
+ WHERE table_schema = #{database}
16
+ AND table_type = 'BASE TABLE'
17
+ #{by_name}
18
+ SQL
19
+
20
+ execute_and_free(sql, 'SCHEMA') do |result|
21
+ rows_from(result).map(&:first)
22
+ end
23
+ end
24
+
25
+ def views(name = nil) #:nodoc:
26
+ result = execute("SHOW FULL TABLES WHERE TABLE_TYPE='VIEW'")
27
+
28
+ rows_from(result).map(&:first)
29
+ end
30
+
31
+ def view_build_query(view, name = nil)
32
+ result = execute("SHOW CREATE VIEW #{view}", name)
33
+ algorithm_string = rows_from(result).first[1]
34
+
35
+ algorithm_string.gsub(/CREATE .*? (AS)+/i, "")
36
+ rescue ActiveRecord::StatementInvalid => e
37
+ raise "No view called #{view} found, #{e}"
38
+ end
39
+
40
+ private
41
+
42
+ def rows_from(result)
43
+ result.respond_to?(:rows) ? result.rows : result
44
+ end
7
45
  end
8
46
  end
9
47
  end
@@ -7,10 +7,12 @@ module Spectacles
7
7
 
8
8
  def views(name = nil) #:nodoc:
9
9
  q = <<-SQL
10
- SELECT table_name, table_type
11
- FROM information_schema.tables
12
- WHERE table_schema = ANY(current_schemas(false))
13
- AND table_type = 'VIEW'
10
+ SELECT t.table_name, t.table_type
11
+ FROM information_schema.tables AS t
12
+ INNER JOIN pg_class AS c ON c.relname = t.table_name
13
+ WHERE t.table_schema = ANY(current_schemas(false))
14
+ AND t.table_type = 'VIEW'
15
+ AND pg_catalog.pg_get_userbyid(c.relowner) = #{quote(database_username)}
14
16
  SQL
15
17
 
16
18
  execute(q, name).map { |row| row['table_name'] }
@@ -66,7 +68,7 @@ module Spectacles
66
68
  definition = row["definition"].strip.sub(/;$/, "")
67
69
 
68
70
  options = {}
69
- options[:data] = false if ispopulated == 'f'
71
+ options[:data] = false if ispopulated == 'f' || ispopulated == false
70
72
  options[:storage] = parse_storage_definition(storage) if storage.present?
71
73
  options[:tablespace] = tablespace if tablespace.present?
72
74
 
@@ -133,6 +135,10 @@ module Spectacles
133
135
  execute "REFRESH MATERIALIZED VIEW #{quote_table_name(view_name)}"
134
136
  end
135
137
 
138
+ def refresh_materialized_view_concurrently(view_name)
139
+ execute "REFRESH MATERIALIZED VIEW CONCURRENTLY #{quote_table_name(view_name)}"
140
+ end
141
+
136
142
  def parse_storage_definition(storage)
137
143
  # JRuby 9000 returns storage as an Array, whereas
138
144
  # MRI returns a string.
@@ -145,6 +151,10 @@ module Spectacles
145
151
  hash
146
152
  end
147
153
  end
154
+
155
+ def database_username
156
+ @config[:username]
157
+ end
148
158
  end
149
159
  end
150
160
  end
@@ -21,7 +21,7 @@ module Spectacles
21
21
  end
22
22
 
23
23
  def generate_view_query(*columns)
24
- sql = <<-SQL
24
+ <<-SQL
25
25
  SELECT #{columns.join(',')}
26
26
  FROM sqlite_master
27
27
  WHERE type = 'view'
@@ -1,3 +1,3 @@
1
1
  module Spectacles
2
- VERSION = "1.0.1"
2
+ VERSION = "6.0.0"
3
3
  end
@@ -3,8 +3,7 @@ module Spectacles
3
3
  self.abstract_class = true
4
4
 
5
5
  def self.new(*)
6
- warn "DEPRECATION WARNING: #{self} is an abstract class and should not be instantiated. In v1.0, calling `#{self}.new` will raise a NotImplementedError."
7
- super # raise NotImplementedError, "#{self} is an abstract class and can not be instantiated."
6
+ raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
8
7
  end
9
8
 
10
9
  def self.view_exists?
data/lib/spectacles.rb CHANGED
@@ -6,6 +6,7 @@ require 'spectacles/view'
6
6
  require 'spectacles/materialized_view'
7
7
  require 'spectacles/version'
8
8
  require 'spectacles/configuration'
9
+ require 'spectacles/abstract_adapter_override'
9
10
 
10
11
  require 'spectacles/railtie' if defined?(Rails)
11
12
 
@@ -23,21 +24,12 @@ module Spectacles
23
24
  end
24
25
  end
25
26
 
26
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
27
- alias_method(:_spectacles_original_inherited, :inherited) if method_defined?(:inherited)
28
-
29
- def self.inherited(klass)
30
- ::Spectacles::load_adapters
31
- _spectacles_orig_inherited if method_defined?(:_spectacles_original_inherited)
32
- end
33
- end
34
-
35
27
  ActiveRecord::SchemaDumper.class_eval do
36
28
  alias_method(:_spectacles_orig_trailer, :trailer)
37
29
 
38
30
  def trailer(stream)
39
31
  ::Spectacles::SchemaDumper.dump_views(stream, @connection)
40
- ::Spectacles::SchemaDumper.dump_materialized_views(stream, @connection)
32
+ ::Spectacles::SchemaDumper.dump_materialized_views(self, stream, @connection)
41
33
  _spectacles_orig_trailer(stream)
42
34
  end
43
35
  end
@@ -6,7 +6,7 @@ describe "Spectacles::SchemaStatements::Mysql2Adapter" do
6
6
  :host => "localhost",
7
7
  :username => "root"
8
8
  }
9
-
9
+
10
10
  configure_database(config)
11
11
  recreate_database("spectacles_test")
12
12
  load_schema
@@ -25,44 +25,44 @@ describe "Spectacles::SchemaStatements::PostgreSQLAdapter" do
25
25
 
26
26
  describe "#view_build_query" do
27
27
  it "should escape double-quotes returned by Postgres" do
28
- test_base.view_build_query(:new_product_users).must_match(/\\"/)
28
+ _(test_base.view_build_query(:new_product_users)).must_match(/\\"/)
29
29
  end
30
30
  end
31
31
 
32
32
  describe "#materialized_views" do
33
33
  it "should support materialized views" do
34
- test_base.supports_materialized_views?.must_equal true
34
+ _(test_base.supports_materialized_views?).must_equal true
35
35
  end
36
36
  end
37
37
 
38
38
  describe "#create_materialized_view_statement" do
39
39
  it "should work with no options" do
40
40
  query = test_base.create_materialized_view_statement(:view_name, "select_query_here")
41
- query.must_match(/create materialized view view_name as select_query_here with data/i)
41
+ _(query).must_match(/create materialized view view_name as select_query_here with data/i)
42
42
  end
43
43
 
44
44
  it "should allow column names to be specified" do
45
45
  query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
46
46
  columns: %i(first second third))
47
- query.must_match(/create materialized view view_name \(first,second,third\) as select_query_here with data/i)
47
+ _(query).must_match(/create materialized view view_name \(first,second,third\) as select_query_here with data/i)
48
48
  end
49
49
 
50
50
  it "should allow storage parameters to be specified" do
51
51
  query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
52
52
  storage: { bats_in_belfry: true, max_wingspan: 15 })
53
- query.must_match(/create materialized view view_name with \(bats_in_belfry=true, max_wingspan=15\) as select_query_here with data/i)
53
+ _(query).must_match(/create materialized view view_name with \(bats_in_belfry=true, max_wingspan=15\) as select_query_here with data/i)
54
54
  end
55
55
 
56
56
  it "should allow tablespace to be specified" do
57
57
  query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
58
58
  tablespace: :the_final_frontier)
59
- query.must_match(/create materialized view view_name tablespace the_final_frontier as select_query_here with data/i)
59
+ _(query).must_match(/create materialized view view_name tablespace the_final_frontier as select_query_here with data/i)
60
60
  end
61
61
 
62
62
  it "should allow empty view to be created" do
63
63
  query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
64
64
  data: false)
65
- query.must_match(/create materialized view view_name as select_query_here with no data/i)
65
+ _(query).must_match(/create materialized view view_name as select_query_here with no data/i)
66
66
  end
67
67
  end
68
68
  end
data/specs/spec_helper.rb CHANGED
@@ -50,4 +50,4 @@ def recreate_database(database)
50
50
  ActiveRecord::Base.connection.drop_database(database) rescue nil
51
51
  ActiveRecord::Base.connection.create_database(database)
52
52
  ActiveRecord::Base.establish_connection(@database_config.merge(:database => database))
53
- end
53
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe "loading an adapter" do
4
+ it "calls the original AR::CA::AbstractAdapter.inherited method" do
5
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
6
+ def self.inherited(subclass)
7
+ @_spectacles_inherited_called = true
8
+ end
9
+ end
10
+ load File.join(__dir__, '../../lib/spectacles/abstract_adapter_override.rb')
11
+ Class.new(ActiveRecord::ConnectionAdapters::AbstractAdapter)
12
+ _(ActiveRecord::ConnectionAdapters::AbstractAdapter.instance_variable_get("@_spectacles_inherited_called")).must_equal true
13
+ end
14
+ end
@@ -1,10 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Spectacles::SchemaStatements::AbstractAdapter do
3
+ describe Spectacles::SchemaStatements::AbstractAdapter do
4
4
  class TestBase
5
5
  extend Spectacles::SchemaStatements::AbstractAdapter
6
6
 
7
7
  def self.materialized_views
8
+ @materialized_views ||= nil
8
9
  @materialized_views || super
9
10
  end
10
11
 
@@ -17,64 +18,64 @@ describe Spectacles::SchemaStatements::AbstractAdapter do
17
18
  end
18
19
 
19
20
  describe "#create_view" do
20
- it "throws error when block not given and no build_query" do
21
- lambda { TestBase.create_view(:view_name) }.must_raise(RuntimeError)
21
+ it "throws error when block not given and no build_query" do
22
+ _(lambda { TestBase.create_view(:view_name) }).must_raise(RuntimeError)
22
23
  end
23
24
  end
24
25
 
25
- describe "#views" do
26
- it "throws error when accessed on AbstractAdapter" do
27
- lambda { TestBase.views }.must_raise(RuntimeError)
26
+ describe "#views" do
27
+ it "throws error when accessed on AbstractAdapter" do
28
+ _(lambda { TestBase.views }).must_raise(RuntimeError)
28
29
  end
29
30
  end
30
31
 
31
32
  describe "#supports_materialized_views?" do
32
33
  it "returns false when accessed on AbstractAdapter" do
33
- TestBase.supports_materialized_views?.must_equal false
34
+ _(TestBase.supports_materialized_views?).must_equal false
34
35
  end
35
36
  end
36
37
 
37
38
  describe "#materialized_views" do
38
39
  it "throws error when accessed on AbstractAdapter" do
39
- lambda { TestBase.materialized_views }.must_raise(NotImplementedError)
40
+ _(lambda { TestBase.materialized_views }).must_raise(NotImplementedError)
40
41
  end
41
42
  end
42
43
 
43
44
  describe "#materialized_view_exists?" do
44
45
  it "is true when materialized_views includes the view" do
45
46
  TestBase.with_materialized_views(%w(alpha beta gamma)) do
46
- TestBase.materialized_view_exists?(:beta).must_equal true
47
+ _(TestBase.materialized_view_exists?(:beta)).must_equal true
47
48
  end
48
49
  end
49
50
 
50
51
  it "is false when materialized_views does not include the view" do
51
52
  TestBase.with_materialized_views(%w(alpha beta gamma)) do
52
- TestBase.materialized_view_exists?(:delta).must_equal false
53
+ _(TestBase.materialized_view_exists?(:delta)).must_equal false
53
54
  end
54
55
  end
55
56
  end
56
57
 
57
58
  describe "#materialized_view_build_query" do
58
59
  it "throws error when accessed on AbstractAdapter" do
59
- lambda { TestBase.materialized_view_build_query(:books) }.must_raise(NotImplementedError)
60
+ _(lambda { TestBase.materialized_view_build_query(:books) }).must_raise(NotImplementedError)
60
61
  end
61
62
  end
62
63
 
63
64
  describe "#create_materialized_view" do
64
65
  it "throws error when accessed on AbstractAdapter" do
65
- lambda { TestBase.create_materialized_view(:books) }.must_raise(NotImplementedError)
66
+ _(lambda { TestBase.create_materialized_view(:books) }).must_raise(NotImplementedError)
66
67
  end
67
68
  end
68
69
 
69
70
  describe "#drop_materialized_view" do
70
71
  it "throws error when accessed on AbstractAdapter" do
71
- lambda { TestBase.drop_materialized_view(:books) }.must_raise(NotImplementedError)
72
+ _(lambda { TestBase.drop_materialized_view(:books) }).must_raise(NotImplementedError)
72
73
  end
73
74
  end
74
75
 
75
76
  describe "#refresh_materialized_view" do
76
77
  it "throws error when accessed on AbstractAdapter" do
77
- lambda { TestBase.refresh_materialized_view(:books) }.must_raise(NotImplementedError)
78
+ _(lambda { TestBase.refresh_materialized_view(:books) }).must_raise(NotImplementedError)
78
79
  end
79
80
  end
80
81
 
@@ -2,6 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  describe Spectacles::View do
4
4
  it "is an abstract class" do
5
- Spectacles::View.abstract_class?.must_be true
5
+ _(Spectacles::View.abstract_class?).must_be true
6
6
  end
7
- end
7
+ end
@@ -1,57 +1,66 @@
1
- require 'spec_helper'
2
-
3
1
  shared_examples_for "an adapter" do |adapter|
4
2
  shared_base = Class.new do
5
3
  extend Spectacles::SchemaStatements.const_get(adapter)
6
4
  def self.quote_table_name(name); name; end
7
5
  def self.quote_column_name(name); name; end
8
- def self.execute(query); query; end
6
+ def self.execute(query); query; end
9
7
  end
10
8
 
11
- describe "ActiveRecord::SchemaDumper#dump" do
9
+ describe "ActiveRecord::SchemaDumper#dump" do
12
10
  before(:each) do
13
11
  ActiveRecord::Base.connection.drop_view(:new_product_users)
14
12
 
15
- ActiveRecord::Base.connection.create_view(:new_product_users) do
13
+ ActiveRecord::Base.connection.create_view(:new_product_users) do
16
14
  "SELECT name AS product_name, first_name AS username FROM
17
15
  products JOIN users ON users.id = products.user_id"
18
16
  end
19
17
 
20
18
  if ActiveRecord::Base.connection.supports_materialized_views?
21
- ActiveRecord::Base.connection.create_materialized_view(:materialized_product_users, force: true) do
19
+ ActiveRecord::Base.connection.drop_materialized_view(:materialized_product_users)
20
+ ActiveRecord::Base.connection.drop_materialized_view(:empty_materialized_product_users)
21
+
22
+ ActiveRecord::Base.connection.create_materialized_view(:materialized_product_users, force: true) do
22
23
  "SELECT name AS product_name, first_name AS username FROM
23
24
  products JOIN users ON users.id = products.user_id"
24
25
  end
25
26
 
26
- ActiveRecord::Base.connection.create_materialized_view(:empty_materialized_product_users, storage: { fillfactor: 50 }, data: false, force: true) do
27
+ ActiveRecord::Base.connection.add_index :materialized_product_users, :product_name
28
+
29
+ ActiveRecord::Base.connection.create_materialized_view(:empty_materialized_product_users, storage: { fillfactor: 50 }, data: false, force: true) do
27
30
  "SELECT name AS product_name, first_name AS username FROM
28
31
  products JOIN users ON users.id = products.user_id"
29
32
  end
30
33
  end
31
34
  end
32
35
 
33
- it "should return create_view in dump stream" do
36
+ it "should return create_view in dump stream" do
34
37
  stream = StringIO.new
35
38
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
36
- stream.string.must_match(/create_view/)
39
+ _(stream.string).must_match(/create_view/)
37
40
  end
38
41
 
39
42
  if ActiveRecord::Base.connection.supports_materialized_views?
40
- it "should return create_materialized_view in dump stream" do
43
+ it "should return create_materialized_view in dump stream" do
44
+ stream = StringIO.new
45
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
46
+ _(stream.string).must_match(/create_materialized_view/)
47
+ end
48
+
49
+ it "should return add_index in dump stream" do
41
50
  stream = StringIO.new
42
51
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
43
- stream.string.must_match(/create_materialized_view/)
52
+ _(stream.string).must_match(/add_index/)
44
53
  end
45
54
 
46
55
  it "should include options for create_materialized_view" do
47
56
  stream = StringIO.new
48
57
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
49
- stream.string.must_match(/create_materialized_view.*fillfactor: 50/)
50
- stream.string.must_match(/create_materialized_view.*data: false/)
58
+ _(stream.string).must_match(/create_materialized_view.*fillfactor: 50/)
59
+ _(stream.string).must_match(/create_materialized_view.*data: false/)
51
60
  end
52
61
  end
53
62
 
54
- it "should rebuild views in dump stream" do
63
+ it "should rebuild views in dump stream" do
55
64
  stream = StringIO.new
56
65
  ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
57
66
 
@@ -71,10 +80,10 @@ shared_examples_for "an adapter" do |adapter|
71
80
 
72
81
  eval(stream.string)
73
82
 
74
- ActiveRecord::Base.connection.views.must_include('new_product_users')
83
+ _(ActiveRecord::Base.connection.views).must_include('new_product_users')
75
84
 
76
85
  if ActiveRecord::Base.connection.supports_materialized_views?
77
- ActiveRecord::Base.connection.materialized_views.must_include('materialized_product_users')
86
+ _(ActiveRecord::Base.connection.materialized_views).must_include('materialized_product_users')
78
87
  end
79
88
  end
80
89
  end
@@ -82,41 +91,41 @@ shared_examples_for "an adapter" do |adapter|
82
91
  describe "#create_view" do
83
92
  let(:view_name) { :view_name }
84
93
 
85
- it "throws error when block not given and no build_query" do
86
- lambda { shared_base.create_view(view_name) }.must_raise(RuntimeError)
94
+ it "throws error when block not given and no build_query" do
95
+ _(lambda { shared_base.create_view(view_name) }).must_raise(RuntimeError)
87
96
  end
88
97
 
89
98
  describe "view_name" do
90
- it "takes a symbol as the view_name" do
91
- shared_base.create_view(view_name.to_sym, Product.all).must_match(/#{view_name}/)
99
+ it "takes a symbol as the view_name" do
100
+ _(shared_base.create_view(view_name.to_sym, Product.all)).must_match(/#{view_name}/)
92
101
  end
93
102
 
94
- it "takes a string as the view_name" do
95
- shared_base.create_view(view_name.to_s, Product.all).must_match(/#{view_name}/)
103
+ it "takes a string as the view_name" do
104
+ _(shared_base.create_view(view_name.to_s, Product.all)).must_match(/#{view_name}/)
96
105
  end
97
106
  end
98
107
 
99
- describe "build_query" do
100
- it "uses a string if passed" do
108
+ describe "build_query" do
109
+ it "uses a string if passed" do
101
110
  select_statement = "SELECT * FROM products"
102
- shared_base.create_view(view_name, select_statement).must_match(/#{Regexp.escape(select_statement)}/)
111
+ _(shared_base.create_view(view_name, select_statement)).must_match(/#{Regexp.escape(select_statement)}/)
103
112
  end
104
113
 
105
- it "uses an Arel::Relation if passed" do
114
+ it "uses an Arel::Relation if passed" do
106
115
  select_statement = Product.all.to_sql
107
- shared_base.create_view(view_name, Product.all).must_match(/#{Regexp.escape(select_statement)}/)
116
+ _(shared_base.create_view(view_name, Product.all)).must_match(/#{Regexp.escape(select_statement)}/)
108
117
  end
109
118
  end
110
119
 
111
- describe "block" do
112
- it "can use an Arel::Relation from the yield" do
120
+ describe "block" do
121
+ it "can use an Arel::Relation from the yield" do
113
122
  select_statement = Product.all.to_sql
114
- shared_base.create_view(view_name) { Product.all }.must_match(/#{Regexp.escape(select_statement)}/)
123
+ _(shared_base.create_view(view_name) { Product.all }).must_match(/#{Regexp.escape(select_statement)}/)
115
124
  end
116
125
 
117
- it "can use a String from the yield" do
126
+ it "can use a String from the yield" do
118
127
  select_statement = "SELECT * FROM products"
119
- shared_base.create_view(view_name) { "SELECT * FROM products" }.must_match(/#{Regexp.escape(select_statement)}/)
128
+ _(shared_base.create_view(view_name) { "SELECT * FROM products" }).must_match(/#{Regexp.escape(select_statement)}/)
120
129
  end
121
130
  end
122
131
  end
@@ -125,55 +134,72 @@ shared_examples_for "an adapter" do |adapter|
125
134
  let(:view_name) { :view_name }
126
135
 
127
136
  describe "view_name" do
128
- it "takes a symbol as the view_name" do
129
- shared_base.drop_view(view_name.to_sym).must_match(/#{view_name}/)
137
+ it "takes a symbol as the view_name" do
138
+ _(shared_base.drop_view(view_name.to_sym)).must_match(/#{view_name}/)
130
139
  end
131
140
 
132
- it "takes a string as the view_name" do
133
- shared_base.drop_view(view_name.to_s).must_match(/#{view_name}/)
141
+ it "takes a string as the view_name" do
142
+ _(shared_base.drop_view(view_name.to_s)).must_match(/#{view_name}/)
134
143
  end
135
144
  end
136
145
  end
137
146
 
147
+ describe "#tables" do
148
+ it "returns an array of all table names" do
149
+ _(ActiveRecord::Base.connection.tables).must_include("products")
150
+ _(ActiveRecord::Base.connection.tables).must_include("users")
151
+ end
152
+
153
+ it "does not include the names of the views" do
154
+ _(ActiveRecord::Base.connection.tables).wont_include("new_product_users")
155
+ end
156
+ end
157
+
158
+ describe "#views" do
159
+ it "returns an array of all views" do
160
+ _(ActiveRecord::Base.connection.views).must_include("new_product_users")
161
+ end
162
+ end
163
+
138
164
  if shared_base.supports_materialized_views?
139
165
  describe "#create_materialized_view" do
140
166
  let(:view_name) { :view_name }
141
167
 
142
168
  it "throws error when block not given and no build_query" do
143
- lambda { shared_base.create_materialized_view(view_name) }.must_raise(RuntimeError)
169
+ _(lambda { shared_base.create_materialized_view(view_name) }).must_raise(RuntimeError)
144
170
  end
145
171
 
146
172
  describe "view_name" do
147
173
  it "takes a symbol as the view_name" do
148
- shared_base.create_materialized_view(view_name.to_sym, Product.all).must_match(/#{view_name}/)
174
+ _(shared_base.create_materialized_view(view_name.to_sym, Product.all)).must_match(/#{view_name}/)
149
175
  end
150
176
 
151
177
  it "takes a string as the view_name" do
152
- shared_base.create_materialized_view(view_name.to_s, Product.all).must_match(/#{view_name}/)
178
+ _(shared_base.create_materialized_view(view_name.to_s, Product.all)).must_match(/#{view_name}/)
153
179
  end
154
180
  end
155
181
 
156
182
  describe "build_query" do
157
183
  it "uses a string if passed" do
158
184
  select_statement = "SELECT * FROM products"
159
- shared_base.create_materialized_view(view_name, select_statement).must_match(/#{Regexp.escape(select_statement)}/)
185
+ _(shared_base.create_materialized_view(view_name, select_statement)).must_match(/#{Regexp.escape(select_statement)}/)
160
186
  end
161
187
 
162
188
  it "uses an Arel::Relation if passed" do
163
189
  select_statement = Product.all.to_sql
164
- shared_base.create_materialized_view(view_name, Product.all).must_match(/#{Regexp.escape(select_statement)}/)
190
+ _(shared_base.create_materialized_view(view_name, Product.all)).must_match(/#{Regexp.escape(select_statement)}/)
165
191
  end
166
192
  end
167
193
 
168
194
  describe "block" do
169
195
  it "can use an Arel::Relation from the yield" do
170
196
  select_statement = Product.all.to_sql
171
- shared_base.create_materialized_view(view_name) { Product.all }.must_match(/#{Regexp.escape(select_statement)}/)
197
+ _(shared_base.create_materialized_view(view_name) { Product.all }).must_match(/#{Regexp.escape(select_statement)}/)
172
198
  end
173
199
 
174
200
  it "can use a String from the yield" do
175
201
  select_statement = "SELECT * FROM products"
176
- shared_base.create_materialized_view(view_name) { "SELECT * FROM products" }.must_match(/#{Regexp.escape(select_statement)}/)
202
+ _(shared_base.create_materialized_view(view_name) { "SELECT * FROM products" }).must_match(/#{Regexp.escape(select_statement)}/)
177
203
  end
178
204
  end
179
205
  end
@@ -183,11 +209,11 @@ shared_examples_for "an adapter" do |adapter|
183
209
 
184
210
  describe "view_name" do
185
211
  it "takes a symbol as the view_name" do
186
- shared_base.drop_materialized_view(view_name.to_sym).must_match(/#{view_name}/)
212
+ _(shared_base.drop_materialized_view(view_name.to_sym)).must_match(/#{view_name}/)
187
213
  end
188
214
 
189
215
  it "takes a string as the view_name" do
190
- shared_base.drop_materialized_view(view_name.to_s).must_match(/#{view_name}/)
216
+ _(shared_base.drop_materialized_view(view_name.to_s)).must_match(/#{view_name}/)
191
217
  end
192
218
  end
193
219
  end
@@ -197,18 +223,18 @@ shared_examples_for "an adapter" do |adapter|
197
223
 
198
224
  describe "view_name" do
199
225
  it "takes a symbol as the view_name" do
200
- shared_base.refresh_materialized_view(view_name.to_sym).must_match(/#{view_name}/)
226
+ _(shared_base.refresh_materialized_view(view_name.to_sym)).must_match(/#{view_name}/)
201
227
  end
202
228
 
203
229
  it "takes a string as the view_name" do
204
- shared_base.refresh_materialized_view(view_name.to_s).must_match(/#{view_name}/)
230
+ _(shared_base.refresh_materialized_view(view_name.to_s)).must_match(/#{view_name}/)
205
231
  end
206
232
  end
207
233
  end
208
234
  else
209
235
  describe "#materialized_views" do
210
236
  it "should not be supported by #{adapter}" do
211
- lambda { shared_base.materialized_views }.must_raise(NotImplementedError)
237
+ _(lambda { shared_base.materialized_views }).must_raise(NotImplementedError)
212
238
  end
213
239
  end
214
240
  end
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  shared_examples_for "a view model" do
4
2
  ActiveRecord::Base.connection.create_view(:new_product_users) do
5
3
  "SELECT name AS product_name, first_name AS username FROM
@@ -12,18 +10,22 @@ shared_examples_for "a view model" do
12
10
 
13
11
  describe "Spectacles::View" do
14
12
  describe "inherited class" do
15
- it "can has scopes" do
13
+ before(:each) do
16
14
  User.destroy_all
17
15
  Product.destroy_all
18
16
  @john = User.create(:first_name => 'John', :last_name => 'Doe')
19
17
  @john.products.create(:name => 'Rubber Duck', :value => 10)
18
+ end
19
+
20
+ let(:new_product_user) { NewProductUser.duck_lovers.load.first }
20
21
 
21
- NewProductUser.duck_lovers.load.first.username.must_be @john.first_name
22
+ it "can have scopes" do
23
+ _(new_product_user.username).must_be @john.first_name
22
24
  end
23
25
 
24
26
  describe "an instance" do
25
27
  it "is readonly" do
26
- NewProductUser.new.readonly?.must_be true
28
+ _(new_product_user.readonly?).must_be true
27
29
  end
28
30
  end
29
31
  end
@@ -48,12 +50,12 @@ shared_examples_for "a view model" do
48
50
  MaterializedProductUser.refresh!
49
51
  end
50
52
 
51
- it "can has scopes" do
52
- MaterializedProductUser.duck_lovers.load.first.username.must_be @john.first_name
53
+ it "can have scopes" do
54
+ _(MaterializedProductUser.duck_lovers.load.first.username).must_be @john.first_name
53
55
  end
54
56
 
55
57
  it "is readonly" do
56
- MaterializedProductUser.first.readonly?.must_be true
58
+ _(MaterializedProductUser.first.readonly?).must_be true
57
59
  end
58
60
  end
59
61
  end
data/spectacles.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.email = ["liveh2o@gmail.com, brandonsdewitt@gmail.com"]
11
11
  gem.homepage = "http://github.com/liveh2o/spectacles"
12
12
  gem.summary = %q{Spectacles (derived from RailsSQLViews) adds database view functionality to ActiveRecord.}
13
- gem.description = %q{Spectacles adds database view functionality to ActiveRecord. Current supported adapters include Postgres, SQLite and Vertica (MySQL is close).}
13
+ gem.description = %q{Spectacles adds database view functionality to ActiveRecord. Current supported adapters include Postgres, SQLite, Vertica, and MySQL.}
14
14
  gem.license = 'MIT'
15
15
 
16
16
  gem.files = `git ls-files`.split($\)
@@ -21,9 +21,9 @@ Gem::Specification.new do |gem|
21
21
  ##
22
22
  # Dependencies
23
23
  #
24
- gem.required_ruby_version = ">= 2.0.0"
25
- gem.add_dependency "activerecord", ">= 3.2.0"
26
- gem.add_dependency "activesupport", ">= 3.2.0"
24
+ gem.required_ruby_version = ">= 2.2.0"
25
+ gem.add_dependency "activerecord", ">= 3.2.0", "~> 6.1.0"
26
+ gem.add_dependency "activesupport", ">= 3.2.0", "~> 6.1.0"
27
27
 
28
28
  ##
29
29
  # Development dependencies
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spectacles
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Hutchison, Brandon Dewitt
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-29 00:00:00.000000000 Z
11
+ date: 2022-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -17,6 +17,9 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 3.2.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: 6.1.0
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,6 +27,9 @@ dependencies:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: 3.2.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 6.1.0
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: activesupport
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -31,6 +37,9 @@ dependencies:
31
37
  - - ">="
32
38
  - !ruby/object:Gem::Version
33
39
  version: 3.2.0
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: 6.1.0
34
43
  type: :runtime
35
44
  prerelease: false
36
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -38,6 +47,9 @@ dependencies:
38
47
  - - ">="
39
48
  - !ruby/object:Gem::Version
40
49
  version: 3.2.0
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: 6.1.0
41
53
  - !ruby/object:Gem::Dependency
42
54
  name: rake
43
55
  requirement: !ruby/object:Gem::Requirement
@@ -67,7 +79,7 @@ dependencies:
67
79
  - !ruby/object:Gem::Version
68
80
  version: '0'
69
81
  description: Spectacles adds database view functionality to ActiveRecord. Current
70
- supported adapters include Postgres, SQLite and Vertica (MySQL is close).
82
+ supported adapters include Postgres, SQLite, Vertica, and MySQL.
71
83
  email:
72
84
  - liveh2o@gmail.com, brandonsdewitt@gmail.com
73
85
  executables: []
@@ -75,11 +87,13 @@ extensions: []
75
87
  extra_rdoc_files: []
76
88
  files:
77
89
  - ".gitignore"
90
+ - ".travis.yml"
78
91
  - Gemfile
79
92
  - LICENSE
80
93
  - Rakefile
81
94
  - Readme.rdoc
82
95
  - lib/spectacles.rb
96
+ - lib/spectacles/abstract_adapter_override.rb
83
97
  - lib/spectacles/configuration.rb
84
98
  - lib/spectacles/materialized_view.rb
85
99
  - lib/spectacles/railtie.rb
@@ -87,7 +101,6 @@ files:
87
101
  - lib/spectacles/schema_statements.rb
88
102
  - lib/spectacles/schema_statements/abstract_adapter.rb
89
103
  - lib/spectacles/schema_statements/mysql2_adapter.rb
90
- - lib/spectacles/schema_statements/mysql_adapter.rb
91
104
  - lib/spectacles/schema_statements/postgresql_adapter.rb
92
105
  - lib/spectacles/schema_statements/sqlite3_adapter.rb
93
106
  - lib/spectacles/schema_statements/sqlserver_adapter.rb
@@ -95,10 +108,10 @@ files:
95
108
  - lib/spectacles/version.rb
96
109
  - lib/spectacles/view.rb
97
110
  - specs/adapters/mysql2_adapter_spec.rb
98
- - specs/adapters/mysql_adapter_spec.rb
99
111
  - specs/adapters/postgresql_adapter_spec.rb
100
112
  - specs/adapters/sqlite3_adapter_spec.rb
101
113
  - specs/spec_helper.rb
114
+ - specs/spectacles/abstract_adapter_override_spec.rb
102
115
  - specs/spectacles/schema_statements/abstract_adapter_spec.rb
103
116
  - specs/spectacles/view_spec.rb
104
117
  - specs/support/minitest_matchers.rb
@@ -110,7 +123,7 @@ homepage: http://github.com/liveh2o/spectacles
110
123
  licenses:
111
124
  - MIT
112
125
  metadata: {}
113
- post_install_message:
126
+ post_install_message:
114
127
  rdoc_options: []
115
128
  require_paths:
116
129
  - lib
@@ -118,16 +131,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
118
131
  requirements:
119
132
  - - ">="
120
133
  - !ruby/object:Gem::Version
121
- version: 2.0.0
134
+ version: 2.2.0
122
135
  required_rubygems_version: !ruby/object:Gem::Requirement
123
136
  requirements:
124
137
  - - ">="
125
138
  - !ruby/object:Gem::Version
126
139
  version: '0'
127
140
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.4.8
130
- signing_key:
141
+ rubygems_version: 3.3.6
142
+ signing_key:
131
143
  specification_version: 4
132
144
  summary: Spectacles (derived from RailsSQLViews) adds database view functionality
133
145
  to ActiveRecord.
@@ -1,39 +0,0 @@
1
- require 'spectacles/schema_statements/abstract_adapter'
2
-
3
- module Spectacles
4
- module SchemaStatements
5
- module MysqlAdapter
6
- include Spectacles::SchemaStatements::AbstractAdapter
7
-
8
- # overrides the #tables method from ActiveRecord's MysqlAdapter
9
- # to return only tables, and not views.
10
- def tables(name = nil, database = nil, like = nil)
11
- database = database ? quote_table_name(database) : "DATABASE()"
12
- by_name = like ? "AND table_name LIKE #{quote(like)}" : ""
13
-
14
- sql = <<-SQL.squish
15
- SELECT table_name, table_type
16
- FROM information_schema.tables
17
- WHERE table_schema = #{database}
18
- AND table_type = 'BASE TABLE'
19
- #{by_name}
20
- SQL
21
-
22
- execute_and_free(sql, 'SCHEMA') do |result|
23
- result.collect(&:first)
24
- end
25
- end
26
-
27
- def views(name = nil) #:nodoc:
28
- execute("SHOW FULL TABLES WHERE TABLE_TYPE='VIEW'").map { |row| row[0] }
29
- end
30
-
31
- def view_build_query(view, name = nil)
32
- row = execute("SHOW CREATE VIEW #{view}", name).first
33
- return row[1].gsub(/CREATE .*? (AS)+/i, "")
34
- rescue ActiveRecord::StatementInvalid => e
35
- raise "No view called #{view} found"
36
- end
37
- end
38
- end
39
- end
@@ -1,15 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Spectacles::SchemaStatements::MysqlAdapter" do
4
- config = {
5
- :adapter => "mysql",
6
- :host => "localhost",
7
- :username => "root"
8
- }
9
- configure_database(config)
10
- recreate_database("spectacles_test")
11
- load_schema
12
-
13
- it_behaves_like "an adapter", "MysqlAdapter"
14
- it_behaves_like "a view model"
15
- end