spectacles 6.0.0 → 7.1.0
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.
- 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
|