spectacles 6.0.0 → 7.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/.standard.yml +4 -0
- data/CHANGELOG.md +58 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/LICENSE.txt +21 -0
- data/README.md +136 -0
- data/Rakefile +15 -15
- data/lib/spectacles/abstract_adapter_override.rb +3 -3
- data/lib/spectacles/materialized_view.rb +7 -7
- data/lib/spectacles/railtie.rb +3 -3
- data/lib/spectacles/schema_dumper.rb +1 -1
- data/lib/spectacles/schema_statements/abstract_adapter.rb +15 -9
- data/lib/spectacles/schema_statements/mysql2_adapter.rb +6 -26
- data/lib/spectacles/schema_statements/postgresql_adapter.rb +31 -33
- data/lib/spectacles/schema_statements/sqlite3_adapter.rb +5 -20
- data/lib/spectacles/schema_statements/sqlserver_adapter.rb +5 -5
- data/lib/spectacles/schema_statements/vertica_adapter.rb +5 -5
- data/lib/spectacles/schema_statements.rb +2 -2
- data/lib/spectacles/version.rb +3 -1
- data/lib/spectacles/view.rb +5 -5
- data/lib/spectacles.rb +14 -12
- metadata +37 -44
- data/.gitignore +0 -8
- data/.travis.yml +0 -14
- data/Gemfile +0 -20
- data/LICENSE +0 -20
- data/Readme.rdoc +0 -120
- data/specs/adapters/mysql2_adapter_spec.rb +0 -16
- data/specs/adapters/postgresql_adapter_spec.rb +0 -68
- data/specs/adapters/sqlite3_adapter_spec.rb +0 -14
- data/specs/spec_helper.rb +0 -53
- data/specs/spectacles/abstract_adapter_override_spec.rb +0 -14
- data/specs/spectacles/schema_statements/abstract_adapter_spec.rb +0 -82
- data/specs/spectacles/view_spec.rb +0 -7
- data/specs/support/minitest_matchers.rb +0 -5
- data/specs/support/minitest_shared.rb +0 -20
- data/specs/support/schema_statement_examples.rb +0 -241
- data/specs/support/view_examples.rb +0 -62
- data/spectacles.gemspec +0 -33
@@ -1,68 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Spectacles::SchemaStatements::PostgreSQLAdapter" do
|
4
|
-
config = {
|
5
|
-
:adapter => "postgresql",
|
6
|
-
:host => "localhost",
|
7
|
-
:username => "postgres",
|
8
|
-
:database => "postgres",
|
9
|
-
:min_messages => "error"
|
10
|
-
}
|
11
|
-
configure_database(config)
|
12
|
-
recreate_database("spectacles_test")
|
13
|
-
load_schema
|
14
|
-
|
15
|
-
it_behaves_like "an adapter", "PostgreSQLAdapter"
|
16
|
-
it_behaves_like "a view model"
|
17
|
-
|
18
|
-
test_base = Class.new do
|
19
|
-
extend Spectacles::SchemaStatements::PostgreSQLAdapter
|
20
|
-
def self.schema_search_path; ""; end
|
21
|
-
def self.select_value(_, _); "\"products\""; end;
|
22
|
-
def self.quote_table_name(name); name; end
|
23
|
-
def self.quote_column_name(name); name; end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "#view_build_query" do
|
27
|
-
it "should escape double-quotes returned by Postgres" do
|
28
|
-
_(test_base.view_build_query(:new_product_users)).must_match(/\\"/)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe "#materialized_views" do
|
33
|
-
it "should support materialized views" do
|
34
|
-
_(test_base.supports_materialized_views?).must_equal true
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "#create_materialized_view_statement" do
|
39
|
-
it "should work with no options" do
|
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)
|
42
|
-
end
|
43
|
-
|
44
|
-
it "should allow column names to be specified" do
|
45
|
-
query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
|
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)
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should allow storage parameters to be specified" do
|
51
|
-
query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
|
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)
|
54
|
-
end
|
55
|
-
|
56
|
-
it "should allow tablespace to be specified" do
|
57
|
-
query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
|
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)
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should allow empty view to be created" do
|
63
|
-
query = test_base.create_materialized_view_statement(:view_name, "select_query_here",
|
64
|
-
data: false)
|
65
|
-
_(query).must_match(/create materialized view view_name as select_query_here with no data/i)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Spectacles::SchemaStatements::SQLite3Adapter" do
|
4
|
-
File.delete(File.expand_path(File.dirname(__FILE__) + "/../test.db")) rescue nil
|
5
|
-
|
6
|
-
ActiveRecord::Base.establish_connection(
|
7
|
-
:adapter => "sqlite3",
|
8
|
-
:database => "specs/test.db"
|
9
|
-
)
|
10
|
-
load_schema
|
11
|
-
|
12
|
-
it_behaves_like "an adapter", "SQLite3Adapter"
|
13
|
-
it_behaves_like "a view model"
|
14
|
-
end
|
data/specs/spec_helper.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'simplecov'
|
2
|
-
SimpleCov.start do
|
3
|
-
add_filter "/specs"
|
4
|
-
end
|
5
|
-
|
6
|
-
require 'rubygems'
|
7
|
-
require 'bundler'
|
8
|
-
Bundler.require(:default, :development, :test)
|
9
|
-
|
10
|
-
require 'minitest/spec'
|
11
|
-
require 'minitest/autorun'
|
12
|
-
require 'minitest/pride'
|
13
|
-
require 'support/minitest_shared'
|
14
|
-
require 'support/minitest_matchers'
|
15
|
-
require 'support/schema_statement_examples'
|
16
|
-
require 'support/view_examples'
|
17
|
-
|
18
|
-
class User < ActiveRecord::Base
|
19
|
-
has_many :products
|
20
|
-
end
|
21
|
-
|
22
|
-
class Product < ActiveRecord::Base
|
23
|
-
belongs_to :user
|
24
|
-
end
|
25
|
-
|
26
|
-
ActiveRecord::Schema.verbose = false
|
27
|
-
|
28
|
-
def configure_database(config)
|
29
|
-
@database_config = config
|
30
|
-
end
|
31
|
-
|
32
|
-
def load_schema
|
33
|
-
ActiveRecord::Schema.define(:version => 1) do
|
34
|
-
create_table :users do |t|
|
35
|
-
t.string :first_name
|
36
|
-
t.string :last_name
|
37
|
-
end
|
38
|
-
|
39
|
-
create_table :products do |t|
|
40
|
-
t.string :name
|
41
|
-
t.integer :value
|
42
|
-
t.boolean :available, :default => true
|
43
|
-
t.belongs_to :user
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def recreate_database(database)
|
49
|
-
ActiveRecord::Base.establish_connection(@database_config)
|
50
|
-
ActiveRecord::Base.connection.drop_database(database) rescue nil
|
51
|
-
ActiveRecord::Base.connection.create_database(database)
|
52
|
-
ActiveRecord::Base.establish_connection(@database_config.merge(:database => database))
|
53
|
-
end
|
@@ -1,14 +0,0 @@
|
|
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,82 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Spectacles::SchemaStatements::AbstractAdapter do
|
4
|
-
class TestBase
|
5
|
-
extend Spectacles::SchemaStatements::AbstractAdapter
|
6
|
-
|
7
|
-
def self.materialized_views
|
8
|
-
@materialized_views ||= nil
|
9
|
-
@materialized_views || super
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.with_materialized_views(list)
|
13
|
-
@materialized_views = list
|
14
|
-
yield
|
15
|
-
ensure
|
16
|
-
@materialized_views = nil
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
describe "#create_view" do
|
21
|
-
it "throws error when block not given and no build_query" do
|
22
|
-
_(lambda { TestBase.create_view(:view_name) }).must_raise(RuntimeError)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "#views" do
|
27
|
-
it "throws error when accessed on AbstractAdapter" do
|
28
|
-
_(lambda { TestBase.views }).must_raise(RuntimeError)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe "#supports_materialized_views?" do
|
33
|
-
it "returns false when accessed on AbstractAdapter" do
|
34
|
-
_(TestBase.supports_materialized_views?).must_equal false
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
describe "#materialized_views" do
|
39
|
-
it "throws error when accessed on AbstractAdapter" do
|
40
|
-
_(lambda { TestBase.materialized_views }).must_raise(NotImplementedError)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "#materialized_view_exists?" do
|
45
|
-
it "is true when materialized_views includes the view" do
|
46
|
-
TestBase.with_materialized_views(%w(alpha beta gamma)) do
|
47
|
-
_(TestBase.materialized_view_exists?(:beta)).must_equal true
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "is false when materialized_views does not include the view" do
|
52
|
-
TestBase.with_materialized_views(%w(alpha beta gamma)) do
|
53
|
-
_(TestBase.materialized_view_exists?(:delta)).must_equal false
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe "#materialized_view_build_query" do
|
59
|
-
it "throws error when accessed on AbstractAdapter" do
|
60
|
-
_(lambda { TestBase.materialized_view_build_query(:books) }).must_raise(NotImplementedError)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe "#create_materialized_view" do
|
65
|
-
it "throws error when accessed on AbstractAdapter" do
|
66
|
-
_(lambda { TestBase.create_materialized_view(:books) }).must_raise(NotImplementedError)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe "#drop_materialized_view" do
|
71
|
-
it "throws error when accessed on AbstractAdapter" do
|
72
|
-
_(lambda { TestBase.drop_materialized_view(:books) }).must_raise(NotImplementedError)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
describe "#refresh_materialized_view" do
|
77
|
-
it "throws error when accessed on AbstractAdapter" do
|
78
|
-
_(lambda { TestBase.refresh_materialized_view(:books) }).must_raise(NotImplementedError)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'minitest/spec'
|
2
|
-
|
3
|
-
MiniTest::Spec.class_eval do
|
4
|
-
def self.shared_examples
|
5
|
-
@shared_examples ||= {}
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
module MiniTest::Spec::SharedExamples
|
10
|
-
def shared_examples_for(desc, &block)
|
11
|
-
MiniTest::Spec.shared_examples[desc] = block
|
12
|
-
end
|
13
|
-
|
14
|
-
def it_behaves_like(desc, *args)
|
15
|
-
examples = MiniTest::Spec.shared_examples[desc]
|
16
|
-
instance_exec(*args, &examples)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
Object.class_eval { include(MiniTest::Spec::SharedExamples) }
|
@@ -1,241 +0,0 @@
|
|
1
|
-
shared_examples_for "an adapter" do |adapter|
|
2
|
-
shared_base = Class.new do
|
3
|
-
extend Spectacles::SchemaStatements.const_get(adapter)
|
4
|
-
def self.quote_table_name(name); name; end
|
5
|
-
def self.quote_column_name(name); name; end
|
6
|
-
def self.execute(query); query; end
|
7
|
-
end
|
8
|
-
|
9
|
-
describe "ActiveRecord::SchemaDumper#dump" do
|
10
|
-
before(:each) do
|
11
|
-
ActiveRecord::Base.connection.drop_view(:new_product_users)
|
12
|
-
|
13
|
-
ActiveRecord::Base.connection.create_view(:new_product_users) do
|
14
|
-
"SELECT name AS product_name, first_name AS username FROM
|
15
|
-
products JOIN users ON users.id = products.user_id"
|
16
|
-
end
|
17
|
-
|
18
|
-
if ActiveRecord::Base.connection.supports_materialized_views?
|
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
|
23
|
-
"SELECT name AS product_name, first_name AS username FROM
|
24
|
-
products JOIN users ON users.id = products.user_id"
|
25
|
-
end
|
26
|
-
|
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
|
30
|
-
"SELECT name AS product_name, first_name AS username FROM
|
31
|
-
products JOIN users ON users.id = products.user_id"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
it "should return create_view in dump stream" do
|
37
|
-
stream = StringIO.new
|
38
|
-
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
39
|
-
_(stream.string).must_match(/create_view/)
|
40
|
-
end
|
41
|
-
|
42
|
-
if ActiveRecord::Base.connection.supports_materialized_views?
|
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
|
50
|
-
stream = StringIO.new
|
51
|
-
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
52
|
-
_(stream.string).must_match(/add_index/)
|
53
|
-
end
|
54
|
-
|
55
|
-
it "should include options for create_materialized_view" do
|
56
|
-
stream = StringIO.new
|
57
|
-
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
58
|
-
_(stream.string).must_match(/create_materialized_view.*fillfactor: 50/)
|
59
|
-
_(stream.string).must_match(/create_materialized_view.*data: false/)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
it "should rebuild views in dump stream" do
|
64
|
-
stream = StringIO.new
|
65
|
-
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
66
|
-
|
67
|
-
if ActiveRecord::Base.connection.supports_materialized_views?
|
68
|
-
ActiveRecord::Base.connection.materialized_views.each do |view|
|
69
|
-
ActiveRecord::Base.connection.drop_materialized_view(view)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
ActiveRecord::Base.connection.views.each do |view|
|
74
|
-
ActiveRecord::Base.connection.drop_view(view)
|
75
|
-
end
|
76
|
-
|
77
|
-
ActiveRecord::Base.connection.tables.each do |table|
|
78
|
-
ActiveRecord::Base.connection.drop_table(table)
|
79
|
-
end
|
80
|
-
|
81
|
-
eval(stream.string)
|
82
|
-
|
83
|
-
_(ActiveRecord::Base.connection.views).must_include('new_product_users')
|
84
|
-
|
85
|
-
if ActiveRecord::Base.connection.supports_materialized_views?
|
86
|
-
_(ActiveRecord::Base.connection.materialized_views).must_include('materialized_product_users')
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe "#create_view" do
|
92
|
-
let(:view_name) { :view_name }
|
93
|
-
|
94
|
-
it "throws error when block not given and no build_query" do
|
95
|
-
_(lambda { shared_base.create_view(view_name) }).must_raise(RuntimeError)
|
96
|
-
end
|
97
|
-
|
98
|
-
describe "view_name" do
|
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}/)
|
101
|
-
end
|
102
|
-
|
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}/)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "build_query" do
|
109
|
-
it "uses a string if passed" do
|
110
|
-
select_statement = "SELECT * FROM products"
|
111
|
-
_(shared_base.create_view(view_name, select_statement)).must_match(/#{Regexp.escape(select_statement)}/)
|
112
|
-
end
|
113
|
-
|
114
|
-
it "uses an Arel::Relation if passed" do
|
115
|
-
select_statement = Product.all.to_sql
|
116
|
-
_(shared_base.create_view(view_name, Product.all)).must_match(/#{Regexp.escape(select_statement)}/)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
describe "block" do
|
121
|
-
it "can use an Arel::Relation from the yield" do
|
122
|
-
select_statement = Product.all.to_sql
|
123
|
-
_(shared_base.create_view(view_name) { Product.all }).must_match(/#{Regexp.escape(select_statement)}/)
|
124
|
-
end
|
125
|
-
|
126
|
-
it "can use a String from the yield" do
|
127
|
-
select_statement = "SELECT * FROM products"
|
128
|
-
_(shared_base.create_view(view_name) { "SELECT * FROM products" }).must_match(/#{Regexp.escape(select_statement)}/)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
describe "#drop_view" do
|
134
|
-
let(:view_name) { :view_name }
|
135
|
-
|
136
|
-
describe "view_name" do
|
137
|
-
it "takes a symbol as the view_name" do
|
138
|
-
_(shared_base.drop_view(view_name.to_sym)).must_match(/#{view_name}/)
|
139
|
-
end
|
140
|
-
|
141
|
-
it "takes a string as the view_name" do
|
142
|
-
_(shared_base.drop_view(view_name.to_s)).must_match(/#{view_name}/)
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
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
|
-
|
164
|
-
if shared_base.supports_materialized_views?
|
165
|
-
describe "#create_materialized_view" do
|
166
|
-
let(:view_name) { :view_name }
|
167
|
-
|
168
|
-
it "throws error when block not given and no build_query" do
|
169
|
-
_(lambda { shared_base.create_materialized_view(view_name) }).must_raise(RuntimeError)
|
170
|
-
end
|
171
|
-
|
172
|
-
describe "view_name" do
|
173
|
-
it "takes a symbol as the view_name" do
|
174
|
-
_(shared_base.create_materialized_view(view_name.to_sym, Product.all)).must_match(/#{view_name}/)
|
175
|
-
end
|
176
|
-
|
177
|
-
it "takes a string as the view_name" do
|
178
|
-
_(shared_base.create_materialized_view(view_name.to_s, Product.all)).must_match(/#{view_name}/)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
describe "build_query" do
|
183
|
-
it "uses a string if passed" do
|
184
|
-
select_statement = "SELECT * FROM products"
|
185
|
-
_(shared_base.create_materialized_view(view_name, select_statement)).must_match(/#{Regexp.escape(select_statement)}/)
|
186
|
-
end
|
187
|
-
|
188
|
-
it "uses an Arel::Relation if passed" do
|
189
|
-
select_statement = Product.all.to_sql
|
190
|
-
_(shared_base.create_materialized_view(view_name, Product.all)).must_match(/#{Regexp.escape(select_statement)}/)
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
describe "block" do
|
195
|
-
it "can use an Arel::Relation from the yield" do
|
196
|
-
select_statement = Product.all.to_sql
|
197
|
-
_(shared_base.create_materialized_view(view_name) { Product.all }).must_match(/#{Regexp.escape(select_statement)}/)
|
198
|
-
end
|
199
|
-
|
200
|
-
it "can use a String from the yield" do
|
201
|
-
select_statement = "SELECT * FROM products"
|
202
|
-
_(shared_base.create_materialized_view(view_name) { "SELECT * FROM products" }).must_match(/#{Regexp.escape(select_statement)}/)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
describe "#drop_materialized_view" do
|
208
|
-
let(:view_name) { :view_name }
|
209
|
-
|
210
|
-
describe "view_name" do
|
211
|
-
it "takes a symbol as the view_name" do
|
212
|
-
_(shared_base.drop_materialized_view(view_name.to_sym)).must_match(/#{view_name}/)
|
213
|
-
end
|
214
|
-
|
215
|
-
it "takes a string as the view_name" do
|
216
|
-
_(shared_base.drop_materialized_view(view_name.to_s)).must_match(/#{view_name}/)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
describe "#refresh_materialized_view" do
|
222
|
-
let(:view_name) { :view_name }
|
223
|
-
|
224
|
-
describe "view_name" do
|
225
|
-
it "takes a symbol as the view_name" do
|
226
|
-
_(shared_base.refresh_materialized_view(view_name.to_sym)).must_match(/#{view_name}/)
|
227
|
-
end
|
228
|
-
|
229
|
-
it "takes a string as the view_name" do
|
230
|
-
_(shared_base.refresh_materialized_view(view_name.to_s)).must_match(/#{view_name}/)
|
231
|
-
end
|
232
|
-
end
|
233
|
-
end
|
234
|
-
else
|
235
|
-
describe "#materialized_views" do
|
236
|
-
it "should not be supported by #{adapter}" do
|
237
|
-
_(lambda { shared_base.materialized_views }).must_raise(NotImplementedError)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
shared_examples_for "a view model" do
|
2
|
-
ActiveRecord::Base.connection.create_view(:new_product_users) do
|
3
|
-
"SELECT name AS product_name, first_name AS username FROM
|
4
|
-
products JOIN users ON users.id = products.user_id"
|
5
|
-
end
|
6
|
-
|
7
|
-
class NewProductUser < Spectacles::View
|
8
|
-
scope :duck_lovers, lambda { where(:product_name => 'Rubber Duck') }
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "Spectacles::View" do
|
12
|
-
describe "inherited class" do
|
13
|
-
before(:each) do
|
14
|
-
User.destroy_all
|
15
|
-
Product.destroy_all
|
16
|
-
@john = User.create(:first_name => 'John', :last_name => 'Doe')
|
17
|
-
@john.products.create(:name => 'Rubber Duck', :value => 10)
|
18
|
-
end
|
19
|
-
|
20
|
-
let(:new_product_user) { NewProductUser.duck_lovers.load.first }
|
21
|
-
|
22
|
-
it "can have scopes" do
|
23
|
-
_(new_product_user.username).must_be @john.first_name
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "an instance" do
|
27
|
-
it "is readonly" do
|
28
|
-
_(new_product_user.readonly?).must_be true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
if ActiveRecord::Base.connection.supports_materialized_views?
|
35
|
-
ActiveRecord::Base.connection.create_materialized_view(:materialized_product_users) do
|
36
|
-
"SELECT name AS product_name, first_name AS username FROM
|
37
|
-
products JOIN users ON users.id = products.user_id"
|
38
|
-
end
|
39
|
-
|
40
|
-
class MaterializedProductUser < Spectacles::MaterializedView
|
41
|
-
scope :duck_lovers, lambda { where(:product_name => 'Rubber Duck') }
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "Spectacles::MaterializedView" do
|
45
|
-
before(:each) do
|
46
|
-
User.delete_all
|
47
|
-
Product.delete_all
|
48
|
-
@john = User.create(:first_name => 'John', :last_name => 'Doe')
|
49
|
-
@duck = @john.products.create(:name => 'Rubber Duck', :value => 10)
|
50
|
-
MaterializedProductUser.refresh!
|
51
|
-
end
|
52
|
-
|
53
|
-
it "can have scopes" do
|
54
|
-
_(MaterializedProductUser.duck_lovers.load.first.username).must_be @john.first_name
|
55
|
-
end
|
56
|
-
|
57
|
-
it "is readonly" do
|
58
|
-
_(MaterializedProductUser.first.readonly?).must_be true
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
data/spectacles.gemspec
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "spectacles/version"
|
5
|
-
|
6
|
-
Gem::Specification.new do |gem|
|
7
|
-
gem.version = Spectacles::VERSION
|
8
|
-
gem.name = "spectacles"
|
9
|
-
gem.authors = ["Adam Hutchison, Brandon Dewitt"]
|
10
|
-
gem.email = ["liveh2o@gmail.com, brandonsdewitt@gmail.com"]
|
11
|
-
gem.homepage = "http://github.com/liveh2o/spectacles"
|
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, Vertica, and MySQL.}
|
14
|
-
gem.license = 'MIT'
|
15
|
-
|
16
|
-
gem.files = `git ls-files`.split($\)
|
17
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
-
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
-
gem.require_paths = ["lib"]
|
20
|
-
|
21
|
-
##
|
22
|
-
# Dependencies
|
23
|
-
#
|
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
|
-
|
28
|
-
##
|
29
|
-
# Development dependencies
|
30
|
-
#
|
31
|
-
gem.add_development_dependency "rake"
|
32
|
-
gem.add_development_dependency "minitest"
|
33
|
-
end
|