scenic 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +6 -4
- data/.yardopts +5 -0
- data/CONTRIBUTING.md +25 -0
- data/LICENSE.txt +1 -1
- data/NEWS.md +61 -9
- data/README.md +98 -62
- data/Rakefile +5 -0
- data/bin/setup +7 -0
- data/lib/generators/scenic/generators.rb +11 -0
- data/lib/generators/scenic/materializable.rb +22 -0
- data/lib/generators/scenic/model/USAGE +2 -0
- data/lib/generators/scenic/model/model_generator.rb +31 -4
- data/lib/generators/scenic/model/templates/model.erb +3 -2
- data/lib/generators/scenic/view/USAGE +2 -0
- data/lib/generators/scenic/view/templates/db/migrate/create_view.erb +1 -1
- data/lib/generators/scenic/view/templates/db/migrate/update_view.erb +7 -0
- data/lib/generators/scenic/view/view_generator.rb +5 -2
- data/lib/scenic.rb +13 -2
- data/lib/scenic/adapters/postgres.rb +74 -7
- data/lib/scenic/command_recorder.rb +1 -0
- data/lib/scenic/command_recorder/statement_arguments.rb +1 -0
- data/lib/scenic/configuration.rb +37 -0
- data/lib/scenic/definition.rb +2 -1
- data/lib/scenic/railtie.rb +4 -0
- data/lib/scenic/schema_dumper.rb +3 -6
- data/lib/scenic/statements.rb +61 -41
- data/lib/scenic/version.rb +1 -1
- data/lib/scenic/view.rb +41 -9
- data/scenic.gemspec +2 -2
- data/spec/dummy/db/views/.keep +0 -0
- data/spec/generators/scenic/model/model_generator_spec.rb +11 -0
- data/spec/generators/scenic/view/view_generator_spec.rb +13 -0
- data/spec/scenic/adapters/postgres_spec.rb +57 -14
- data/spec/scenic/configuration_spec.rb +27 -0
- data/spec/scenic/statements_spec.rb +33 -2
- data/spec/smoke +92 -67
- data/spec/support/generator_spec_setup.rb +1 -0
- metadata +19 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aad0218a7caab0f7bb48d964a0b2fb7b45d3da68
|
4
|
+
data.tar.gz: 567bf836c969e9a5287a19c10a5dcd40b9a0d03d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57c9ba2f5803667e7f256730881e75f95ffa113b6a75d3cb94b1ef884d9e258a26c696697840a4118a88ba010fb759d339e2c89f30a903ad967e700aeb227690
|
7
|
+
data.tar.gz: bb6f28f0b4a24ac0e737c98fe4081e4f2a67bf9205bf89701a4deb2d1fc7ccdb1bc5d4fe5d52a002b5cc4f99154ff64d7906ea53ccd49f2e420a87c96f5bce31
|
data/.travis.yml
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
+
addons:
|
2
|
+
postgresql: "9.3"
|
1
3
|
before_install:
|
2
4
|
- "echo '--colour' > ~/.rspec"
|
3
5
|
- "echo 'gem: --no-document' > ~/.gemrc"
|
4
6
|
- git config --global user.name 'Travis CI'
|
5
7
|
- git config --global user.email 'travis-ci@example.com'
|
6
|
-
before_script:
|
7
|
-
- pushd spec/dummy && bundle exec rake db:create && mkdir db/views && popd
|
8
8
|
branches:
|
9
9
|
only:
|
10
10
|
- master
|
11
|
+
install:
|
12
|
+
- travis_retry bin/setup
|
11
13
|
language:
|
12
14
|
- ruby
|
13
15
|
notifications:
|
14
16
|
email:
|
15
17
|
- false
|
16
18
|
rvm:
|
17
|
-
- 2.1.
|
18
|
-
- 2.2.
|
19
|
+
- 2.1.7
|
20
|
+
- 2.2.3
|
19
21
|
sudo: false
|
data/.yardopts
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
We love pull requests from everyone. By participating in this project, you
|
4
|
+
agree to abide by the thoughtbot [code of conduct].
|
5
|
+
|
6
|
+
[code of conduct]: https://thoughtbot.com/open-source-code-of-conduct
|
7
|
+
|
8
|
+
We expect everyone to follow the code of conduct anywhere in thoughtbot's
|
9
|
+
project codebases, issue trackers, chatrooms, and mailing lists.
|
10
|
+
|
11
|
+
## Setting Up for Development
|
12
|
+
|
13
|
+
1. For the repository.
|
14
|
+
2. Run `bin/setup`, which will install dependencies and create the dummy
|
15
|
+
application database.
|
16
|
+
3. Run `rake` to verify that the tests pass.
|
17
|
+
4. Make your change with new passing tests, following the [style guide].
|
18
|
+
5. Write a [good commit message], push your fork, and submit a pull request.
|
19
|
+
|
20
|
+
[style guide]: https://github.com/thoughtbot/guides/tree/master/style
|
21
|
+
[good commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
22
|
+
|
23
|
+
Others will give constructive feedback. This is a time for discussion and
|
24
|
+
improvements, and making the necessary changes will be required before we can
|
25
|
+
merge the contribution.
|
data/LICENSE.txt
CHANGED
data/NEWS.md
CHANGED
@@ -1,12 +1,64 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# News
|
2
|
+
|
3
|
+
The noteworthy changes for each Scenic version are included here. For a complete
|
4
|
+
changelog, see the [CHANGELOG] for each version via the version links.
|
5
|
+
|
6
|
+
[CHANGELOG]: https://github.com/thoughtbot/scenic/commits/master
|
7
|
+
|
8
|
+
## [1.0.0] - November 23, 2015
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Added support for [materialized views].
|
12
|
+
- Allow changing the database adapter via `Scenic::Configuration`.
|
13
|
+
|
14
|
+
### Fixed
|
15
|
+
- Improved formatting of the view when dumped to `schema.rb`.
|
16
|
+
- Fixed generation of namespaced models by using ActiveRecord's own model
|
17
|
+
generator.
|
18
|
+
- Eliminated `alias_method_chain` deprecation when running with Rails master
|
19
|
+
(5.0).
|
20
|
+
|
21
|
+
[materialized views]:https://github.com/thoughtbot/scenic/blob/v1.0.0/README.md
|
22
|
+
[1.0.0]: https://github.com/thoughtbot/scenic/compare/v0.3.0...v1.0.0
|
23
|
+
|
24
|
+
## [0.3.0] - January 23, 2015
|
25
|
+
|
26
|
+
### Added
|
27
|
+
- Previous view definition is copied into new view definition file when updating
|
3
28
|
an existing view.
|
4
|
-
* We avoid dumping views that belong to Postgres extensions
|
5
|
-
* `db/schema.rb` is prettier thanks to a blank line after each view definition.
|
6
29
|
|
7
|
-
|
8
|
-
|
30
|
+
### Fixed
|
31
|
+
- We avoid dumping views that belong to Postgres extensions.
|
32
|
+
- `db/schema.rb` is prettier thanks to a blank line after each view definition.
|
33
|
+
|
34
|
+
[0.3.0]: https://github.com/thoughtbot/scenic/compare/v0.2.1...v0.3.0
|
35
|
+
|
36
|
+
## [0.2.1] - January 5, 2015
|
37
|
+
|
38
|
+
### Fixed
|
39
|
+
- View generator will now create `db/views` directory if necessary.
|
40
|
+
|
41
|
+
[0.2.1]: https://github.com/thoughtbot/scenic/compare/v0.2.0...v0.2.1
|
42
|
+
|
43
|
+
## [0.2.0] - August 11, 2014
|
44
|
+
|
45
|
+
### Added
|
46
|
+
- Teach view generator to update existing views.
|
47
|
+
|
48
|
+
### Fixed
|
49
|
+
- Raise an error if view definition is empty.
|
50
|
+
|
51
|
+
[0.2.0]: https://github.com/thoughtbot/scenic/compare/v0.1.0...v0.2.0
|
52
|
+
|
53
|
+
## [0.1.0] - August 4, 2014
|
54
|
+
|
55
|
+
Scenic makes it easier to work with Postgres views in Rails.
|
56
|
+
|
57
|
+
It introduces view methods to ActiveRecord::Migration and allows views to be
|
58
|
+
dumped to db/schema.rb. It provides generators for models, view definitions,
|
59
|
+
and migrations. It is built around a basic versioning system for view
|
60
|
+
definition files.
|
61
|
+
|
62
|
+
In short, go add a view to your app.
|
9
63
|
|
10
|
-
|
11
|
-
* Teach view generator to update existing views [683361d](https://github.com/thoughtbot/scenic/commit/683361d59410f46aba508a3ceb850161dd0be027)
|
12
|
-
* Raise an error if view definition is empty. [PR #38](https://github.com/thoughtbot/scenic/issues/38)
|
64
|
+
[0.1.0]: https://github.com/thoughtbot/scenic/compare/8599daa132880cd6c07efb0395c0fb023b171f47...v0.1.0
|
data/README.md
CHANGED
@@ -1,27 +1,34 @@
|
|
1
1
|
# Scenic
|
2
2
|
|
3
|
-
|
3
|
+
Scenic adds methods to `ActiveRecord::Migration` to create and manage database
|
4
|
+
views in Rails.
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
Using Scenic, you can bring the power of SQL views to your Rails application
|
7
|
+
without having to switch your schema format to SQL. Scenic provides a convention
|
8
|
+
for versioning views that keeps your migration history consistent and reversible
|
9
|
+
and avoids having to duplicate SQL strings across migrations. As an added bonus,
|
10
|
+
you define the structure of your view in a SQL file, meaning you get full SQL
|
11
|
+
syntax highlighting in the editor of your choice and can easily test your SQL in
|
12
|
+
the database console during development.
|
8
13
|
|
9
|
-
|
14
|
+
Scenic ships with support for PostgreSQL. The adapter is configurable (see
|
15
|
+
`Scenic::Configuration`) and has a minimal interface (see
|
16
|
+
`Scenic::Adapters::Postgres`) that other gems can provide.
|
10
17
|
|
11
|
-
|
12
|
-
views in Rails.
|
18
|
+
## Great, how do I create a view?
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
an added bonus, you define the structure of your view in a SQL file, meaning you
|
18
|
-
get full SQL syntax highlighting support in the editor of your choice.
|
20
|
+
You've got this great idea for a view you'd like to call `searches`. You can
|
21
|
+
create the migration and the corresponding view definition file with the
|
22
|
+
following command:
|
19
23
|
|
20
|
-
|
24
|
+
```sh
|
25
|
+
$ rails generate scenic:view searches
|
26
|
+
create db/views/searches_v01.sql
|
27
|
+
create db/migrate/[TIMESTAMP]_create_searches.rb
|
28
|
+
```
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
like to build your view with. Perhaps that looks something like this:
|
30
|
+
Edit the `db/views/searches_v01.sql` file with the SQL statement that defines
|
31
|
+
your view. In our example, this might look something like this:
|
25
32
|
|
26
33
|
```sql
|
27
34
|
SELECT
|
@@ -40,34 +47,37 @@ SELECT
|
|
40
47
|
FROM statuses
|
41
48
|
```
|
42
49
|
|
43
|
-
|
50
|
+
The generated migration will contain a `create_view` statement. Run the
|
51
|
+
migration, and [baby, you got a view going][carl]. The migration is reversible
|
52
|
+
and the schema will be dumped into your `schema.rb` file.
|
44
53
|
|
45
|
-
|
46
|
-
def change
|
47
|
-
create_view :searches
|
48
|
-
end
|
49
|
-
```
|
54
|
+
[carl]: https://www.youtube.com/watch?v=Sr2PlqXw03Y
|
50
55
|
|
51
|
-
|
52
|
-
|
56
|
+
```sh
|
57
|
+
$ rake db:migrate
|
58
|
+
```
|
53
59
|
|
54
60
|
## Cool, but what if I need to change that view?
|
55
61
|
|
56
|
-
|
57
|
-
the following `change` method:
|
62
|
+
Here's where Scenic really shines. Run that same view generator once more:
|
58
63
|
|
59
|
-
```
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
```sh
|
65
|
+
$ rails generate scenic:view searches
|
66
|
+
create db/views/searches_v02.sql
|
67
|
+
create db/migrate/[TIMESTAMP]_update_searches_to_version_2.rb
|
63
68
|
```
|
64
69
|
|
65
|
-
|
66
|
-
|
70
|
+
Scenic detected that we already had an existing `searches` view at version 1,
|
71
|
+
created a copy of that definition as version 2, and created a migration to
|
72
|
+
update to the version 2 schema. All that's left for you to do is tweak the
|
73
|
+
schema in the new definition and run the `update_view` migration.
|
67
74
|
|
68
75
|
## Can I use this view to back a model?
|
69
76
|
|
70
|
-
You bet!
|
77
|
+
You bet! Using view-backed models can help promote concepts hidden in your
|
78
|
+
relational data to first-class domain objects and can clean up complex
|
79
|
+
ActiveRecord or ARel queries. As far as ActiveRecord is concerned, you a view is
|
80
|
+
no different than a table.
|
71
81
|
|
72
82
|
```ruby
|
73
83
|
class Search < ActiveRecord::Base
|
@@ -81,50 +91,76 @@ class Search < ActiveRecord::Base
|
|
81
91
|
end
|
82
92
|
```
|
83
93
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
94
|
+
Scenic even provides a `scenic:model` generator that is a superset of
|
95
|
+
`scenic:view`. It will act identically to the Rails `model` generator except
|
96
|
+
that it will create a Scenic view migration rather than a table migration.
|
97
|
+
|
98
|
+
There is no special base class or mixin needed. If desired, any code the model
|
99
|
+
generator adds can be removed without worry.
|
100
|
+
|
101
|
+
```sh
|
102
|
+
$ rails generate scenic:model recent_status
|
103
|
+
invoke active_record
|
104
|
+
create app/models/recent_status.rb
|
105
|
+
invoke test_unit
|
106
|
+
create test/models/recent_status_test.rb
|
107
|
+
create test/fixtures/recent_statuses.yml
|
108
|
+
create db/views/recent_statuses_v01.sql
|
109
|
+
create db/migrate/20151112015036_create_recent_statuses.rb
|
110
|
+
```
|
89
111
|
|
90
|
-
|
91
|
-
scratch. `db/views/[model]_v01.sql` wil be an empty file that you fill in only
|
92
|
-
the [query] portion of the view with.
|
112
|
+
### When I query that model with `find` I get an error. What gives?
|
93
113
|
|
94
|
-
|
114
|
+
Your view cannot have a primary key, but ActiveRecord's `find` method expects to
|
115
|
+
query based on one. You can use `find_by!` or you can explicitly set the primary
|
116
|
+
key column on your model like so:
|
95
117
|
|
118
|
+
```ruby
|
119
|
+
class People < ActiveRecord::Base
|
120
|
+
self.primary_key = :id
|
121
|
+
end
|
96
122
|
```
|
97
|
-
$ rails generate scenic:model search
|
98
|
-
create app/models/search.rb
|
99
|
-
create db/views/searches_v01.sql
|
100
|
-
create db/migrate/[TIMESTAMP]_create_searches.rb
|
101
|
-
```
|
102
|
-
|
103
|
-
### View generator
|
104
123
|
|
105
|
-
|
106
|
-
that it doesn't create the model. Convenient.
|
124
|
+
## What about materialized views?
|
107
125
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
create db/migrate/[TIMESTAMP]_create_searches.rb
|
112
|
-
```
|
126
|
+
Materialized views are essentially SQL queries whose results can be cached to a
|
127
|
+
table, indexed, and periodically refreshed when desired. Does Scenic support
|
128
|
+
those? Of course!
|
113
129
|
|
114
|
-
|
130
|
+
The `scenic:view` and `scenic:model` generators accept a `--materialized`
|
131
|
+
option for this purpose. When used with the model generator, your model will
|
132
|
+
have the following method defined as a convenience to aid in scheduling
|
133
|
+
refreshes:
|
115
134
|
|
116
|
-
```
|
117
|
-
|
118
|
-
|
119
|
-
|
135
|
+
```ruby
|
136
|
+
def self.refresh
|
137
|
+
Scenic.database.refresh_materialized_view(table_name)
|
138
|
+
end
|
120
139
|
```
|
121
140
|
|
122
141
|
## I don't need this view anymore. Make it go away.
|
123
142
|
|
124
|
-
|
143
|
+
Scenic gives you `drop_view` too:
|
125
144
|
|
126
145
|
```ruby
|
127
146
|
def change
|
128
147
|
drop_view :searches, revert_to_version: 2
|
129
148
|
end
|
130
149
|
```
|
150
|
+
|
151
|
+
## About
|
152
|
+
|
153
|
+
Scenic is maintained by [Derek Prior] and [Caleb Thompson], funded by
|
154
|
+
thoughtbot, inc. The names and logos for thoughtbot are trademarks of
|
155
|
+
thoughtbot, inc.
|
156
|
+
|
157
|
+
[Derek Prior]: http://prioritized.net
|
158
|
+
[Caleb Thompson]: http://calebthompson.io
|
159
|
+
|
160
|
+
![thoughtbot](https://thoughtbot.com/logo.png)
|
161
|
+
|
162
|
+
We love open source software! See [our other projects][community] or [hire
|
163
|
+
us][hire] to help build your product.
|
164
|
+
|
165
|
+
[community]: https://thoughtbot.com/community?utm_source=github
|
166
|
+
[hire]: https://thoughtbot.com/hire-us?utm_source=github
|
data/Rakefile
CHANGED
data/bin/setup
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Scenic
|
2
|
+
# Scenic provides generators for creating and updating views and ActiveRecord
|
3
|
+
# models that are backed by views.
|
4
|
+
#
|
5
|
+
# See:
|
6
|
+
# * {file:lib/generators/scenic/model/USAGE Model Generator}
|
7
|
+
# * {file:lib/generators/scenic/view/USAGE View Generator}
|
8
|
+
# * {file:README.md README}
|
9
|
+
module Generators
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Scenic
|
2
|
+
module Generators
|
3
|
+
# @api private
|
4
|
+
module Materializable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_option :materialized,
|
9
|
+
type: :boolean,
|
10
|
+
required: false,
|
11
|
+
desc: "Makes the view materialized",
|
12
|
+
default: false
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def materialized?
|
18
|
+
options[:materialized]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,19 +1,46 @@
|
|
1
1
|
require "rails/generators"
|
2
|
+
require "rails/generators/rails/model/model_generator"
|
2
3
|
require "generators/scenic/view/view_generator"
|
4
|
+
require "generators/scenic/materializable"
|
3
5
|
|
4
6
|
module Scenic
|
5
7
|
module Generators
|
8
|
+
# @api private
|
6
9
|
class ModelGenerator < Rails::Generators::NamedBase
|
10
|
+
include Scenic::Generators::Materializable
|
7
11
|
source_root File.expand_path("../templates", __FILE__)
|
8
12
|
|
9
|
-
|
13
|
+
def invoke_rails_model_generator
|
14
|
+
invoke "model", [name], options.merge(migration: false)
|
15
|
+
end
|
10
16
|
|
11
|
-
def
|
12
|
-
|
17
|
+
def inject_model_methods
|
18
|
+
if materialized? && generating?
|
19
|
+
inject_into_class "app/models/#{file_path}.rb", class_name do
|
20
|
+
evaluate_template("model.erb")
|
21
|
+
end
|
22
|
+
end
|
13
23
|
end
|
14
24
|
|
15
25
|
def invoke_view_generator
|
16
|
-
invoke "scenic:view", [
|
26
|
+
invoke "scenic:view", [table_name], options
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def evaluate_template(source)
|
32
|
+
source = File.expand_path(find_in_source_paths(source.to_s))
|
33
|
+
context = instance_eval("binding")
|
34
|
+
ERB.new(
|
35
|
+
::File.binread(source),
|
36
|
+
nil,
|
37
|
+
"-",
|
38
|
+
"@output_buffer",
|
39
|
+
).result(context)
|
40
|
+
end
|
41
|
+
|
42
|
+
def generating?
|
43
|
+
behavior != :revoke
|
17
44
|
end
|
18
45
|
end
|
19
46
|
end
|