monocle-views 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 71b4dae54e7413c864bc7647293313599525af68
4
+ data.tar.gz: c8b2ca16c6ead7b30f01c34ef6c9faaae7f0b02f
5
+ SHA512:
6
+ metadata.gz: b4e071b3599073aa7390b8cc6e31f551b001cd6dba47568469e7a3896db7728efeb1405a256a668cc082ae24258f06367acddefc82f3272e4e5b4aafb287902a
7
+ data.tar.gz: 79c9f5e9c97f8382fde8d7448811425a1519254ea2c5f36a1d96648beb3e2dba7388ebebaf2ec0a83806cd3ac2574ba1ca2f8316b352ec52885b9bfa1bb43fa9
@@ -0,0 +1 @@
1
+ 2.3.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in monocle.gemspec
4
+ gemspec
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ar-monocle (0.1.6)
5
+ activerecord (>= 4, < 6)
6
+ activesupport (>= 4, < 6)
7
+ rake
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activemodel (5.1.0)
13
+ activesupport (= 5.1.0)
14
+ activerecord (5.1.0)
15
+ activemodel (= 5.1.0)
16
+ activesupport (= 5.1.0)
17
+ arel (~> 8.0)
18
+ activesupport (5.1.0)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (~> 0.7)
21
+ minitest (~> 5.1)
22
+ tzinfo (~> 1.1)
23
+ arel (8.0.0)
24
+ coderay (1.1.1)
25
+ concurrent-ruby (1.0.5)
26
+ database_cleaner (1.5.3)
27
+ diff-lcs (1.3)
28
+ dotenv (2.2.0)
29
+ i18n (0.8.1)
30
+ metaclass (0.0.4)
31
+ method_source (0.8.2)
32
+ minitest (5.10.2)
33
+ mocha (1.2.1)
34
+ metaclass (~> 0.0.1)
35
+ pg (0.19.0)
36
+ pry (0.10.4)
37
+ coderay (~> 1.1.0)
38
+ method_source (~> 0.8.1)
39
+ slop (~> 3.4)
40
+ pry-nav (0.2.4)
41
+ pry (>= 0.9.10, < 0.11.0)
42
+ rake (12.0.0)
43
+ rspec (3.5.0)
44
+ rspec-core (~> 3.5.0)
45
+ rspec-expectations (~> 3.5.0)
46
+ rspec-mocks (~> 3.5.0)
47
+ rspec-core (3.5.4)
48
+ rspec-support (~> 3.5.0)
49
+ rspec-expectations (3.5.0)
50
+ diff-lcs (>= 1.2.0, < 2.0)
51
+ rspec-support (~> 3.5.0)
52
+ rspec-mocks (3.5.0)
53
+ diff-lcs (>= 1.2.0, < 2.0)
54
+ rspec-support (~> 3.5.0)
55
+ rspec-support (3.5.0)
56
+ slop (3.6.0)
57
+ thread_safe (0.3.6)
58
+ tzinfo (1.2.3)
59
+ thread_safe (~> 0.1)
60
+
61
+ PLATFORMS
62
+ ruby
63
+
64
+ DEPENDENCIES
65
+ ar-monocle!
66
+ bundler (~> 1.14)
67
+ database_cleaner
68
+ dotenv
69
+ mocha
70
+ pg
71
+ pry-nav
72
+ rspec (~> 3.0)
73
+
74
+ BUNDLED WITH
75
+ 1.14.6
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Leonardo Bighetti
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,109 @@
1
+
2
+ # Monocle
3
+
4
+ Monocle helps you tame your database views by keeping the SQLs versioned neatly in your project and knowing when and how to migrate them if necessary. It knows how to deal with PostgreSQL materialized views and dependencies (view A points to view B) as well as regular views.
5
+
6
+ Monocle works with or without Rails, all it assumes is you're using ActiveRecord. See _Usage_ for more details.
7
+
8
+ ## Reasoning
9
+
10
+ At [InvitedHome](http://invitedhome.com/) we needed an easy to use system to manage a bunch of complex views (often materialized) that we use for things like caching.
11
+
12
+ The only gem that did something similar at the time was Thoughtbot's [Scenic](https://github.com/thoughtbot/scenic), but we didn't like some of its features such as how it would generate multiple versions of the same view's SQL.
13
+
14
+ We wanted something way simpler, one SQL file per view, versioning maintained by a timestamp at the top of the file. Thus, Monocle was born.
15
+
16
+ ## Installation
17
+
18
+ Add this line to your application's Gemfile:
19
+
20
+ ```ruby
21
+ gem 'ar-monocle', require: 'monocle'
22
+ ```
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install ar-monocle
31
+
32
+ ## Setup
33
+
34
+ If you're using Rails, there are generators for bootstrapping the gem:
35
+
36
+ $ rails g monocle:install
37
+
38
+ It will generate a migration for creating the Monocle::Migration table. If you're not using Rails, you'll need to create the table yourself. Check https://github.com/darkside/monocle/blob/master/spec/support/database_utils.rb for an example on how to do it.
39
+
40
+ ## Usage
41
+
42
+ The basic gist is you have a `db/views` in your project which contains all the view / materialized view SQL definitions. On top of those files there's a timestamp that you can control. Every time you change that timestamp, Monocle will try to migrate that view when calling `rake monocle:migrate`. You can automate this easily by hooking `monocle:migrate` to your deployment process.
43
+
44
+ Monocle knows about view dependencies and will drop and recreate dependants as necessary. So if you have a view A that references a view B and you need to upgrade view B, it will drop view A first, then drop and create view B, then create view A.
45
+
46
+ ## Included Generators (for Rails)
47
+
48
+ ### Generating a view
49
+
50
+ With Rails, you can use the generator:
51
+
52
+ $ rails g monocle:view view_name
53
+
54
+ This will generate a Monocle SQL template and a model. You can skip creating the model with `--skip-model`.
55
+
56
+ ### Generating a materialized view
57
+
58
+ With Rails, you can use the generator:
59
+
60
+ $ rails g monocle:matview view_name
61
+
62
+ This will generate a Monocle materialized SQL template and a model. You can skip creating the model with `--skip-model`.
63
+
64
+ ## Included Rake Tasks
65
+
66
+ ### List all views
67
+
68
+ You can use `rake monocle:list` to see all the view names that are being managed by Monocle.
69
+
70
+ ### List all migrated view slugs
71
+
72
+ You can use `rake monocle:versions` to see all the view slugs that have been migrated by Monocle.
73
+
74
+ ### Migrate views
75
+
76
+ You can use `rake monocle:migrate` to migrate any views that have a new timestamp. I recommend you hook this to your deployment process i.e after you call `rake db:migrate`
77
+
78
+ ### Bumping a view timestamp
79
+
80
+ With monocle, you decide when it's time to upgrade a view. So even if you have an updated view definition that you're working on, it won't actually change it unless the timestamp has changed. To bump a view timestamp, you can either do it yourself by changing the first line of the template or use the supplied rake task:
81
+
82
+ $ rake monocle:bump[my_view_name]
83
+
84
+ ### Refresh a view
85
+
86
+ For materialized views, this makes it easy for you to trigger a refresh, say, in a cron job or something.
87
+
88
+ $ rake monocle:refresh[my_view_name]
89
+
90
+ ### Refresh all views
91
+
92
+ This is also available as a top level method for Monocle. It will refresh all your materialized views.
93
+
94
+ $ rake monocle:refresh_all
95
+
96
+ ## Development
97
+
98
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
99
+
100
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
101
+
102
+ ## Contributing
103
+
104
+ Bug reports and pull requests are welcome on GitHub at https://github.com/darkside/monocle.
105
+
106
+ ## License
107
+
108
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
109
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ Dir.glob('lib/tasks/*.rake').each {|r| import r}
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "monocle"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,101 @@
1
+ # Bare essentials from Rails to make this work neatly
2
+ require "active_support/core_ext/module/delegation"
3
+ require 'active_record'
4
+
5
+ require 'monocle/configuration'
6
+ require 'monocle/railtie' if defined?(Rails)
7
+
8
+ require "monocle/version"
9
+ require "monocle/version_generator"
10
+ require "monocle/view"
11
+ require "monocle/migration"
12
+
13
+ require "monocle/bump_command"
14
+ require "monocle/list_command"
15
+
16
+ module Monocle
17
+
18
+ class << self
19
+ delegate :path_to_views, :logger, to: :configuration
20
+
21
+ def list
22
+ @list ||= ListCommand.new.call
23
+ end
24
+
25
+ def drop(view_name)
26
+ fetch(view_name).drop
27
+ end
28
+
29
+ def create(view_name)
30
+ fetch(view_name).create
31
+ end
32
+
33
+ def versions
34
+ Migration.versions
35
+ end
36
+
37
+ def migrate
38
+ logger.info "Starting materialized views migrations..."
39
+ list.each do |key, view|
40
+ logger.debug "Checking if #{key} is up to date..."
41
+ view.migrate
42
+ end
43
+ logger.info "All done!"
44
+ end
45
+
46
+ def bump(view_name)
47
+ BumpCommand.new(fetch(view_name)).call
48
+ end
49
+
50
+ def refresh(view_name, concurrently: false)
51
+ fetch(view_name).refresh concurrently: concurrently
52
+ end
53
+
54
+ def refresh_all
55
+ list.each do |key, view|
56
+ logger.info "Refreshing view #{key}..."
57
+ view.refresh # this will be a noop for non matviews
58
+ end
59
+ end
60
+
61
+ # Enables you to configure things in a block, i.e
62
+ # Monocle.configure do |config|
63
+ # config.logger = MyLogger.new
64
+ # config.path_to_views = "my/different/path/to/my/sql/files"
65
+ # end
66
+ def configure
67
+ yield configuration if block_given?
68
+ end
69
+
70
+ def views_path
71
+ File.join(root, path_to_views)
72
+ end
73
+
74
+ def root
75
+ # Get the absolute path of the project who is using us
76
+ File.expand_path(Dir.pwd)
77
+ end
78
+
79
+ def gem_root
80
+ # Get the absolute path of our gem root
81
+ File.expand_path(File.dirname(__dir__))
82
+ end
83
+
84
+ def fetch(view_name)
85
+ view_name = symbolize_name(view_name)
86
+ list.fetch(view_name)
87
+ end
88
+
89
+ protected
90
+
91
+ def configuration
92
+ @configuration ||= Configuration.new
93
+ end
94
+
95
+
96
+ def symbolize_name(name)
97
+ name.is_a?(String) ? name.to_sym : name
98
+ end
99
+ end
100
+
101
+ end
@@ -0,0 +1,23 @@
1
+ class Monocle::BumpCommand
2
+
3
+ attr_reader :view
4
+
5
+ def initialize(view)
6
+ @view = view
7
+ end
8
+
9
+ def call
10
+ # Get the SQL from the file, skipping the timestamp row
11
+ # Drop the newlines too
12
+ sql = File.readlines(view.path_for_sql)[1..-1]
13
+ # Generate the new timestamp line
14
+ timestamp = ["-- Timestamp: #{Time.now}\n"]
15
+ # Put it back together
16
+ new_sql = (timestamp + sql).join
17
+ # Open the file for writing (no w+, we want to clear it up)
18
+ File.open(view.path_for_sql, "w") do |f|
19
+ f << new_sql
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,16 @@
1
+ module Monocle
2
+ class Configuration
3
+ attr_accessor :path_to_views, :logger
4
+
5
+ # Define a custom logger
6
+ def logger
7
+ @logger ||= (defined?(Rails) ? Rails.logger : Logger.new('monocle.log'))
8
+ end
9
+
10
+ # The relative path to where views are stored, relative to the root of the
11
+ # project
12
+ def path_to_views
13
+ @path_to_views ||= "db/views"
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails/generators'
2
+
3
+ module Monocle::Generators
4
+ class InstallGenerator < Rails::Generators::Base
5
+ desc "Creates everything you need to start rolling with monocle"
6
+ def create_migration
7
+ invoke "migration", ["CreateMonocleMigrations", "version:string:uniq", "--primary-key-type=false"], options
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ require "rails/generators"
2
+
3
+ module Monocle::Generators
4
+ class MatviewGenerator < Rails::Generators::NamedBase
5
+ desc "Creates a materialized view SQL template and optionally a model to go with it"
6
+ class_option :skip_model, type: :boolean, default: false, desc: "Skips model generation"
7
+
8
+ def generate_sql_file
9
+ create_file "db/views/#{file_name}.sql" do
10
+ <<-EOF
11
+ -- Timestamp: #{Time.now}
12
+ CREATE MATERIALIZED VIEW #{file_name} AS
13
+ -- Add your stuff here
14
+ ;
15
+ EOF
16
+ end
17
+ end
18
+
19
+ def generate_model_file
20
+ # Don't do anything if we're skipping this
21
+ return if options[:skip_model]
22
+ # Invoke rails' nifty model generator for us
23
+ invoke "model", [file_path.singularize], options.merge(migration: false, test_framework: false)
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require "rails/generators"
2
+
3
+ module Monocle::Generators
4
+ class ViewGenerator < Rails::Generators::NamedBase
5
+ desc "Creates a view SQL template and optionally a model to go with it"
6
+ class_option :skip_model, type: :boolean, default: false, desc: "Skips model generation"
7
+
8
+ def generate_sql_file
9
+ create_file "db/views/#{file_name}.sql" do
10
+ <<-EOF
11
+ -- Timestamp: #{Time.now}
12
+ CREATE OR REPLACE VIEW #{file_name} AS
13
+ -- Add your stuff here
14
+ ;
15
+ EOF
16
+ end
17
+ end
18
+
19
+ def generate_model_file
20
+ # Don't do anything if we're skipping this
21
+ return if options[:skip_model]
22
+ # Invoke rails' nifty model generator for us
23
+ invoke "model", [file_path.singularize], options.merge(migration: false, test_framework: false)
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,19 @@
1
+ module Monocle
2
+ class ListCommand
3
+ delegate :views_path, to: Monocle
4
+
5
+ attr_reader :view_names
6
+
7
+ def initialize
8
+ @view_names = Dir[File.join(views_path, "*.sql")].map { |f| File.basename(f, ".sql") }
9
+ end
10
+
11
+ def call
12
+ {}.tap do |hash|
13
+ view_names.each do |view_name|
14
+ hash[view_name.to_sym] = View.new(view_name)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,8 @@
1
+ module Monocle
2
+ class Migration < ActiveRecord::Base
3
+ self.table_name = 'monocle_migrations'
4
+ def self.versions
5
+ all.pluck(:version)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ module Monocle
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load File.join(Monocle.gem_root, "lib/tasks/monocle.rake")
5
+ end
6
+
7
+ generators do
8
+ Dir[File.join(Monocle.gem_root, "lib/monocle/generators/*.rb")].each { |f| require f }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Monocle
2
+ VERSION = "0.1.7"
3
+ end
@@ -0,0 +1,18 @@
1
+ module Monocle
2
+ class VersionGenerator
3
+ attr_reader :path, :view
4
+
5
+ def initialize(path)
6
+ @path = path
7
+ @view = File.basename(path, ".sql")
8
+ end
9
+
10
+ def generate
11
+ timestamp = File.open path, &:readline
12
+ fail "can't read timestamp of #{path}! Aborting..." unless timestamp.starts_with? "-- Timestamp: "
13
+ # Get only the digits out of the timestamp line
14
+ timestamp.gsub!(/[^\d]/, '')
15
+ "#{view}_#{timestamp}"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,147 @@
1
+ # An in-memory representation of the view
2
+ module Monocle
3
+ class View
4
+ attr_reader :name
5
+ attr_accessor :dependants
6
+
7
+ delegate :views_path, :list, :versions, :logger, to: Monocle
8
+ delegate :info, :warn, :error, :debug, to: :logger
9
+
10
+ def initialize(name)
11
+ @name = name
12
+ @dependants = []
13
+ end
14
+
15
+ def materialized?
16
+ !!(@materialized ||= create_command =~ /MATERIALIZED VIEW/i)
17
+ end
18
+
19
+ def drop
20
+ debug "Dropping #{name}..."
21
+ self.dependants = get_dependants_from_pg
22
+ dependants.each &:drop
23
+ execute drop_command
24
+ true
25
+ end
26
+
27
+ def create
28
+ debug "Creating #{name}..."
29
+ execute create_command
30
+ Migration.find_or_create_by version: slug
31
+ dependants.each &:create
32
+ true
33
+ rescue ActiveRecord::StatementInvalid => e
34
+ # We may have another new view coming that this view depend on
35
+ # if the relation name is included on our list of views, we create
36
+ # that first and then retry
37
+ if e.message =~ /PG::UndefinedTable/ &&
38
+ e.message.scan(/relation \"(\w+)\" does not exist/) &&
39
+ list.keys.include?($1.to_sym)
40
+ warn "Can't create #{name} because it depends on #{$1}, creating that first..."
41
+ list.fetch($1.to_sym).create
42
+ retry
43
+ else
44
+ fail e
45
+ end
46
+ end
47
+
48
+ def migrate
49
+ if versions.include?(slug)
50
+ debug "Skipping #{name} as it's already up to date."
51
+ true
52
+ else
53
+ status = drop && create
54
+ info "#{name} migrated to #{slug}!"
55
+ status
56
+ end
57
+ end
58
+
59
+ def refresh(concurrently: false)
60
+ # We don't refresh normal views
61
+ return false unless materialized?
62
+ _concurrently = " CONCURRENTLY" if concurrently
63
+ execute "REFRESH MATERIALIZED VIEW#{_concurrently} #{name}"
64
+ true
65
+ rescue ActiveRecord::StatementInvalid => e
66
+ # This view is trying to select from a different view that hasn't been
67
+ # populated.
68
+ if e.message =~ /PG::ObjectNotInPrerequisiteState/ &&
69
+ e.message.scan(/materialized view \"(\w+)\" has not been populated/) &&
70
+ list.keys.include?($1.to_sym)
71
+ warn "Can't refresh #{name} because it depends on #{$1} which hasn't been populated, refreshing that first..."
72
+ list.fetch($1.to_sym).refresh
73
+ retry
74
+ else
75
+ fail e
76
+ end
77
+ end
78
+
79
+ def slug
80
+ @slug ||= VersionGenerator.new(path_for_sql).generate
81
+ end
82
+
83
+ def drop_command
84
+ _materialized = 'MATERIALIZED' if materialized?
85
+ "DROP #{_materialized} VIEW IF EXISTS #{name};"
86
+ end
87
+
88
+ def create_command
89
+ @create_command ||= File.read(path_for_sql)
90
+ end
91
+
92
+ def path_for_sql
93
+ @path_for_sql ||= File.join views_path, "#{name}.sql"
94
+ end
95
+
96
+ def exists?
97
+ execute(check_if_view_exists_sql).entries.map(&:values).flatten.first
98
+ end
99
+
100
+ protected
101
+
102
+ def check_if_view_exists_sql
103
+ <<-SQL
104
+ SELECT count(*) > 0
105
+ FROM pg_catalog.pg_class c
106
+ JOIN pg_namespace n ON n.oid = c.relnamespace
107
+ WHERE c.relkind in ('m','v')
108
+ AND n.nspname = 'public'
109
+ AND c.relname = '#{name}';
110
+ SQL
111
+ end
112
+
113
+ def get_dependants_from_pg
114
+ map_dependants(execute(find_dependants_sql).entries.map(&:values).flatten - [name])
115
+ end
116
+
117
+ def map_dependants(deps)
118
+ deps.map { |d| list[d.to_sym] }.compact
119
+ end
120
+
121
+ def find_dependants_sql
122
+ <<-SQL
123
+ WITH RECURSIVE vlist AS (
124
+ SELECT c.oid::REGCLASS AS view_name
125
+ FROM pg_class c
126
+ WHERE c.relname = '#{name}'
127
+ UNION ALL
128
+ SELECT DISTINCT r.ev_class::REGCLASS AS view_name
129
+ FROM pg_depend d
130
+ JOIN pg_rewrite r ON (r.oid = d.objid)
131
+ JOIN vlist ON (vlist.view_name = d.refobjid)
132
+ WHERE d.refobjsubid != 0
133
+ )
134
+ SELECT * FROM vlist;
135
+ SQL
136
+ end
137
+
138
+ def get_dependants_from_error(e)
139
+ map_dependants e.message.scan(/(\w+) depends on.+view #{name}/).flatten
140
+ end
141
+
142
+ def execute(sql)
143
+ ActiveRecord::Base.connection.execute(sql)
144
+ end
145
+ end
146
+
147
+ end
@@ -0,0 +1,34 @@
1
+ namespace :monocle do
2
+ desc "List all Monocle managed views"
3
+ task :list => :environment do
4
+ Monocle.list
5
+ end
6
+
7
+ desc "List all Monocle view slugs"
8
+ task :versions => :environment do
9
+ Monocle.versions
10
+ end
11
+
12
+ desc "Migrate any monocle views that need migratin'"
13
+ task :migrate => :environment do
14
+ Monocle.migrate
15
+ Rake::Task['db:structure:dump'].invoke
16
+ end
17
+
18
+ desc "Refreshes a given monocle view"
19
+ task :refresh, [:view_name] => :environment do |t, args|
20
+ Monocle.refresh(args.view_name)
21
+ end
22
+
23
+ desc "Refreshes a given monocle view"
24
+ task :refresh_all => :environment do |t, args|
25
+ Monocle.refresh_all
26
+ end
27
+
28
+ desc "Bump a monocle view's timestamp by name"
29
+ task :bump, [:view_name] do |t, args|
30
+ Rake::Task['environment'].invoke
31
+ view_name = args.view_name
32
+ Monocle.bump(view_name)
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'monocle/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "monocle-views"
8
+ spec.version = Monocle::VERSION
9
+ spec.authors = ["Leonardo Bighetti", "Eric Draut"]
10
+ spec.email = ["eric@invitedhome.com"]
11
+
12
+ spec.summary = %q{Monocle helps you manage your DB views.}
13
+ spec.description = %q{Monocle helps you manage your DB views.}
14
+ spec.homepage = "https://github.com/tommyfriendhusen08/monocle"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.14"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.add_development_dependency "mocha"
27
+ spec.add_development_dependency "pry-nav"
28
+ spec.add_development_dependency "dotenv"
29
+ spec.add_development_dependency "pg"
30
+ spec.add_development_dependency "database_cleaner"
31
+
32
+ spec.add_dependency "rake"
33
+ spec.add_dependency "activesupport", ">= 4", "< 6"
34
+ spec.add_dependency "activerecord", ">= 4", "< 6"
35
+ end
metadata ADDED
@@ -0,0 +1,219 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monocle-views
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.7
5
+ platform: ruby
6
+ authors:
7
+ - Leonardo Bighetti
8
+ - Eric Draut
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2018-06-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.14'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.14'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rspec
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '3.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: mocha
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry-nav
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: dotenv
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: pg
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: database_cleaner
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rake
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :runtime
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: activesupport
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '4'
133
+ - - "<"
134
+ - !ruby/object:Gem::Version
135
+ version: '6'
136
+ type: :runtime
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '4'
143
+ - - "<"
144
+ - !ruby/object:Gem::Version
145
+ version: '6'
146
+ - !ruby/object:Gem::Dependency
147
+ name: activerecord
148
+ requirement: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '4'
153
+ - - "<"
154
+ - !ruby/object:Gem::Version
155
+ version: '6'
156
+ type: :runtime
157
+ prerelease: false
158
+ version_requirements: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '4'
163
+ - - "<"
164
+ - !ruby/object:Gem::Version
165
+ version: '6'
166
+ description: Monocle helps you manage your DB views.
167
+ email:
168
+ - eric@invitedhome.com
169
+ executables: []
170
+ extensions: []
171
+ extra_rdoc_files: []
172
+ files:
173
+ - ".ruby-version"
174
+ - Gemfile
175
+ - Gemfile.lock
176
+ - LICENSE.txt
177
+ - README.md
178
+ - Rakefile
179
+ - bin/console
180
+ - bin/setup
181
+ - lib/monocle.rb
182
+ - lib/monocle/bump_command.rb
183
+ - lib/monocle/configuration.rb
184
+ - lib/monocle/generators/install_generator.rb
185
+ - lib/monocle/generators/matview_generator.rb
186
+ - lib/monocle/generators/view_generator.rb
187
+ - lib/monocle/list_command.rb
188
+ - lib/monocle/migration.rb
189
+ - lib/monocle/railtie.rb
190
+ - lib/monocle/version.rb
191
+ - lib/monocle/version_generator.rb
192
+ - lib/monocle/view.rb
193
+ - lib/tasks/monocle.rake
194
+ - monocle.gemspec
195
+ homepage: https://github.com/tommyfriendhusen08/monocle
196
+ licenses:
197
+ - MIT
198
+ metadata: {}
199
+ post_install_message:
200
+ rdoc_options: []
201
+ require_paths:
202
+ - lib
203
+ required_ruby_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: '0'
208
+ required_rubygems_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ requirements: []
214
+ rubyforge_project:
215
+ rubygems_version: 2.6.11
216
+ signing_key:
217
+ specification_version: 4
218
+ summary: Monocle helps you manage your DB views.
219
+ test_files: []