verifiable_view 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d8a982a6eff95584ff03dabe667bd7ad548aa523
4
+ data.tar.gz: 65bea1f3a80b9eb5891681de82d5bfb8bf0f7823
5
+ SHA512:
6
+ metadata.gz: 7ba43c9d9fd53c831609618a66385ee3d600c1e22b298ee999f23f5209b2cce9e848a229737f347c1fdf4fca723d61c29ff6108508c0b4ce26539e2cfe62f86e
7
+ data.tar.gz: 2fa2d18cf62b8b587553134bf4ef9a70c34d17bc5acd1bced3362b2e92a001818ebe9d33695b7801619772cdab0927e29940f32338fe248eb69be0a499ea30b6
data/.gems ADDED
@@ -0,0 +1,3 @@
1
+ cutest -v 1.2.3
2
+ activerecord -v 5.2.1.1
3
+ pg -v 1.1.3
data/Readme.md ADDED
@@ -0,0 +1,45 @@
1
+ # Verifiable View
2
+
3
+ Keep your view definitions in sync with your named scopes and associations'
4
+ definitions.
5
+
6
+ ## Installation
7
+
8
+ `gem install verifiable_view`
9
+
10
+ ## Usage
11
+
12
+ ```rb
13
+ class RegularProduct
14
+ extend VerifiableView
15
+
16
+ definition do
17
+ Product.not_deprecated.where(..)
18
+ end
19
+ end
20
+ ```
21
+
22
+ Then in your tests you'll have a
23
+ ```rb
24
+ RegularProduct.verify
25
+ ```
26
+
27
+ That will check that you have a `regular_products` view defined in your database
28
+ and that the query matches with your definition (Products that are not
29
+ deprecated, etc)
30
+
31
+ ## Why
32
+
33
+ Named scopes and named associations are easy to write with Arel and easy to
34
+ maintain. Every time you write a view by crafting your SQL by hand you risk
35
+ forgetting a condition. If you already have a definition of your associations,
36
+ why to violate DRYness and duplicate them?
37
+
38
+ Also when updating the definition of a named scope, you will notice immediately
39
+ that your view got out of your sync.
40
+
41
+ ## Testing
42
+
43
+ `gem install dep`
44
+ `dep install`
45
+ `cutest test/*.rb`
@@ -0,0 +1,56 @@
1
+ require 'securerandom'
2
+
3
+ module VerifiableView
4
+ def verify
5
+ VerifiableView.verification_method.call(
6
+ code_view_definition,
7
+ db_view_definition
8
+ )
9
+ end
10
+
11
+ def definition(&block)
12
+ @definition ||= block or raise "Missing view definition"
13
+ end
14
+
15
+ def self.verification_method=(some_proc)
16
+ @verification_method = some_proc
17
+ end
18
+
19
+ private
20
+
21
+ def code_view_definition
22
+ view_name = "view_#{SecureRandom.hex(10)}"
23
+ result = ""
24
+ transaction do
25
+ create_view(view_name)
26
+ result = get_view_sql(view_name)
27
+ raise ActiveRecord::Rollback
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ def db_view_definition
34
+ get_view_sql(table_name)
35
+ end
36
+
37
+ def self.verification_method
38
+ @verification_method || (lambda do |code_def, db_def|
39
+ return true if db_def == code_def
40
+ raise "The view definition in your DB doesn't match with your code"
41
+ end)
42
+ end
43
+
44
+ def create_view(name)
45
+ definition_sql = definition.call.to_sql
46
+ connection.execute("create view #{name} as #{definition_sql}")
47
+ end
48
+
49
+ def get_view_sql(view_name)
50
+ connection.
51
+ execute("select view_definition from information_schema.views
52
+ where table_name = '#{view_name}'").
53
+ first&.
54
+ fetch("view_definition")
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ require "active_record"
2
+
3
+ ActiveRecord::Base.establish_connection(
4
+ adapter: "postgresql",
5
+ database: "verifiable_view",
6
+ encoding: "utf8",
7
+ host: "localhost",
8
+ min_messages: "warning"
9
+ )
10
+
11
+ class CreateSchema < ActiveRecord::Migration[5.2]
12
+ def self.up
13
+ execute <<~SQL
14
+ DROP VIEW IF EXISTS regular_products
15
+ SQL
16
+
17
+ create_table :products, force: true do |table|
18
+ table.string :product_category, null: false
19
+ table.boolean :deprecated, null: false
20
+ end
21
+
22
+ execute <<-SQL
23
+ CREATE VIEW regular_products AS
24
+ SELECT "products".* FROM "products" WHERE "products"."product_category" = 'regular' AND "products"."deprecated" = FALSE
25
+ SQL
26
+ end
27
+ end
28
+
29
+ CreateSchema.suppress_messages do
30
+ CreateSchema.migrate(:up)
31
+ end
@@ -0,0 +1,72 @@
1
+ $:.unshift File.expand_path("../../lib", __FILE__)
2
+ require_relative "support/setup_database_helper"
3
+ require 'verifiable_view'
4
+
5
+ class Product < ActiveRecord::Base
6
+ scope :regular, -> { where(product_category: :regular) }
7
+ scope :active, -> { where(deprecated: false) }
8
+ end
9
+
10
+ class RegularProduct < ActiveRecord::Base
11
+ extend VerifiableView
12
+
13
+ definition do
14
+ Product.regular.active
15
+ end
16
+ end
17
+
18
+ test ".verify" do
19
+ VerifiableView.verification_method = (lambda do |expected, actual|
20
+ assert_equal(expected, actual)
21
+ end)
22
+
23
+ RegularProduct.verify
24
+ end
25
+
26
+ test ".verify raises exception when missing code definition" do
27
+ class NoDefinitionView < ActiveRecord::Base
28
+ extend VerifiableView
29
+
30
+ self.table_name = :regular_products
31
+ end
32
+
33
+ error = assert_raise do
34
+ NoDefinitionView.verify
35
+ end
36
+ assert_equal("Missing view definition", error.message)
37
+ end
38
+
39
+ test ".verify with unmatched db definition" do
40
+ class DeprecatedRegularProduct < ActiveRecord::Base
41
+ extend VerifiableView
42
+ self.table_name = :regular_products
43
+
44
+ definition do
45
+ Product.regular.where(deprecated: true)
46
+ end
47
+ end
48
+
49
+ assert_raise do
50
+ DeprecatedRegularProduct.verify
51
+ end
52
+ end
53
+
54
+ test ".verify compares with nil when missing db view" do
55
+ class NoView < ActiveRecord::Base
56
+ extend VerifiableView
57
+
58
+ definition do
59
+ Product.regular
60
+ end
61
+ end
62
+
63
+ values = {}
64
+ VerifiableView.verification_method = (lambda do |expected, actual|
65
+ values[:actual] = actual
66
+ values[:expected] = expected
67
+ end)
68
+
69
+ NoView.verify
70
+ assert values[:expected].downcase.include?("select")
71
+ assert_equal(nil, values[:actual])
72
+ end
@@ -0,0 +1,14 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "verifiable_view"
3
+ s.version = "0.0.1"
4
+ s.summary = "Database Views that don't break"
5
+ s.description = "Easier way to mantain Database Views with ActiveRecord"
6
+ s.authors = ["CarlosIPe"]
7
+ s.email = ["carlos2@compendium.com.ar"]
8
+ s.homepage = "https://github.com/carlosipe/verifiable_view"
9
+ s.license = "MIT"
10
+
11
+ s.files = `git ls-files`.split("\n")
12
+
13
+ s.add_development_dependency "cutest", '~> 1'
14
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: verifiable_view
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - CarlosIPe
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cutest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1'
27
+ description: Easier way to mantain Database Views with ActiveRecord
28
+ email:
29
+ - carlos2@compendium.com.ar
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gems"
35
+ - Readme.md
36
+ - lib/verifiable_view.rb
37
+ - test/support/setup_database_helper.rb
38
+ - test/verifiable_view_test.rb
39
+ - verifiable_view.gemspec
40
+ homepage: https://github.com/carlosipe/verifiable_view
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.6.14.1
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Database Views that don't break
64
+ test_files: []