dry-web-roda 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -1
  3. data/.rspec +1 -2
  4. data/.travis.yml +14 -8
  5. data/CHANGELOG.md +22 -0
  6. data/Gemfile +26 -15
  7. data/README.md +22 -4
  8. data/dry-web-roda.gemspec +3 -1
  9. data/lib/dry/web/roda/application.rb +3 -3
  10. data/lib/dry/web/roda/cli.rb +9 -2
  11. data/lib/dry/web/roda/cli/generate.rb +4 -4
  12. data/lib/dry/web/roda/generators/flat_project.rb +31 -0
  13. data/lib/dry/web/roda/generators/{app.rb → sub_app.rb} +2 -2
  14. data/lib/dry/web/roda/generators/{umbrella.rb → umbrella_project.rb} +6 -6
  15. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/Gemfile +3 -3
  16. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/README.md.tt +0 -0
  17. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/Rakefile.tt +0 -0
  18. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/bin/console.tt +0 -0
  19. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/bin/setup +0 -0
  20. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/config.ru.tt +0 -0
  21. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/config/settings.yml.tt +0 -0
  22. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/db/sample_data.rb +0 -0
  23. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/db/seed.rb +0 -0
  24. data/lib/dry/web/roda/skeletons/{app → flat_project}/lib/__underscored_app_name__/.keep +0 -0
  25. data/lib/dry/web/roda/skeletons/{app → flat_project}/lib/__underscored_app_name__/views/welcome.rb.tt +0 -0
  26. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/lib/persistence/commands/.keep +0 -0
  27. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/lib/persistence/relations/.keep +0 -0
  28. data/lib/dry/web/roda/skeletons/flat_project/lib/rom/instrumentation.rb.tt +22 -0
  29. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/lib/types.rb +0 -0
  30. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/log/.keep +0 -0
  31. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/spec/app_helper.rb +0 -0
  32. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/spec/db_helper.rb.tt +0 -0
  33. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/spec/spec_helper.rb.tt +0 -0
  34. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/spec/support/db/test_factories.rb +0 -0
  35. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/spec/support/test_helpers.rb.tt +0 -0
  36. data/lib/dry/web/roda/skeletons/flat_project/system/__underscored_app_name__/application.rb.tt +29 -0
  37. data/lib/dry/web/roda/skeletons/flat_project/system/__underscored_app_name__/container.rb.tt +23 -0
  38. data/lib/dry/web/roda/skeletons/{app → flat_project}/system/__underscored_app_name__/import.rb.tt +0 -0
  39. data/lib/dry/web/roda/skeletons/{umbrella/lib → flat_project/system}/__underscored_app_name__/repository.rb.tt +0 -0
  40. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/system/__underscored_app_name__/settings.rb.tt +0 -0
  41. data/lib/dry/web/roda/skeletons/flat_project/system/__underscored_app_name__/transactions.rb.tt +34 -0
  42. data/lib/dry/web/roda/skeletons/{umbrella/lib → flat_project/system}/__underscored_app_name__/view_context.rb.tt +0 -0
  43. data/lib/dry/web/roda/skeletons/{app → flat_project}/system/__underscored_app_name__/view_controller.rb.tt +0 -0
  44. data/lib/dry/web/roda/skeletons/flat_project/system/boot.rb.tt +11 -0
  45. data/lib/dry/web/roda/skeletons/flat_project/system/boot/monitor.rb.tt +10 -0
  46. data/lib/dry/web/roda/skeletons/{umbrella → flat_project}/system/boot/rom.rb.tt +0 -0
  47. data/lib/dry/web/roda/skeletons/{app → flat_project}/system/boot/view.rb.tt +0 -0
  48. data/lib/dry/web/roda/skeletons/flat_project/transactions/example.rb.tt +10 -0
  49. data/lib/dry/web/roda/skeletons/{app → flat_project}/web/routes/example.rb.tt +0 -0
  50. data/lib/dry/web/roda/skeletons/{app → flat_project}/web/templates/layouts/application.html.slim +0 -0
  51. data/lib/dry/web/roda/skeletons/{app → flat_project}/web/templates/welcome.html.slim +0 -0
  52. data/lib/dry/web/roda/skeletons/sub_app/lib/__underscored_app_name__/.keep +0 -0
  53. data/lib/dry/web/roda/skeletons/sub_app/lib/__underscored_app_name__/views/welcome.rb.tt +11 -0
  54. data/lib/dry/web/roda/skeletons/{app → sub_app}/system/__underscored_app_name__/application.rb.tt +1 -1
  55. data/lib/dry/web/roda/skeletons/{app → sub_app}/system/__underscored_app_name__/container.rb.tt +2 -0
  56. data/lib/dry/web/roda/skeletons/{umbrella → sub_app}/system/__underscored_app_name__/import.rb.tt +0 -0
  57. data/lib/dry/web/roda/skeletons/{app → sub_app}/system/__underscored_app_name__/transactions.rb.tt +0 -0
  58. data/lib/dry/web/roda/skeletons/{app → sub_app}/system/__underscored_app_name__/view_context.rb.tt +0 -0
  59. data/lib/dry/web/roda/skeletons/sub_app/system/__underscored_app_name__/view_controller.rb.tt +13 -0
  60. data/lib/dry/web/roda/skeletons/{app → sub_app}/system/boot.rb.tt +1 -2
  61. data/lib/dry/web/roda/skeletons/sub_app/system/boot/view.rb.tt +3 -0
  62. data/lib/dry/web/roda/skeletons/{app → sub_app}/transactions/example.rb.tt +0 -0
  63. data/lib/dry/web/roda/skeletons/sub_app/web/routes/example.rb.tt +7 -0
  64. data/lib/dry/web/roda/skeletons/sub_app/web/templates/layouts/application.html.slim +3 -0
  65. data/lib/dry/web/roda/skeletons/sub_app/web/templates/welcome.html.slim +1 -0
  66. data/lib/dry/web/roda/skeletons/{umbrella → umbrella_project}/.gitignore +0 -0
  67. data/lib/dry/web/roda/skeletons/umbrella_project/Gemfile +39 -0
  68. data/lib/dry/web/roda/skeletons/umbrella_project/README.md.tt +12 -0
  69. data/lib/dry/web/roda/skeletons/umbrella_project/Rakefile.tt +81 -0
  70. data/lib/dry/web/roda/skeletons/umbrella_project/bin/console.tt +7 -0
  71. data/lib/dry/web/roda/skeletons/umbrella_project/bin/setup +7 -0
  72. data/lib/dry/web/roda/skeletons/umbrella_project/config.ru.tt +2 -0
  73. data/lib/dry/web/roda/skeletons/umbrella_project/config/settings.yml.tt +8 -0
  74. data/lib/dry/web/roda/skeletons/umbrella_project/db/sample_data.rb +1 -0
  75. data/lib/dry/web/roda/skeletons/umbrella_project/db/seed.rb +1 -0
  76. data/lib/dry/web/roda/skeletons/umbrella_project/lib/__underscored_app_name__/repository.rb.tt +11 -0
  77. data/lib/dry/web/roda/skeletons/{umbrella → umbrella_project}/lib/__underscored_app_name__/transactions.rb.tt +0 -0
  78. data/lib/dry/web/roda/skeletons/umbrella_project/lib/__underscored_app_name__/view_context.rb.tt +39 -0
  79. data/lib/dry/web/roda/skeletons/umbrella_project/lib/persistence/commands/.keep +0 -0
  80. data/lib/dry/web/roda/skeletons/umbrella_project/lib/persistence/relations/.keep +0 -0
  81. data/lib/dry/web/roda/skeletons/umbrella_project/lib/rom/instrumentation.rb.tt +22 -0
  82. data/lib/dry/web/roda/skeletons/umbrella_project/lib/types.rb +6 -0
  83. data/lib/dry/web/roda/skeletons/umbrella_project/log/.keep +0 -0
  84. data/lib/dry/web/roda/skeletons/umbrella_project/spec/app_helper.rb +35 -0
  85. data/lib/dry/web/roda/skeletons/umbrella_project/spec/db_helper.rb.tt +24 -0
  86. data/lib/dry/web/roda/skeletons/umbrella_project/spec/spec_helper.rb.tt +61 -0
  87. data/lib/dry/web/roda/skeletons/umbrella_project/spec/support/db/test_factories.rb +3 -0
  88. data/lib/dry/web/roda/skeletons/umbrella_project/spec/support/test_helpers.rb.tt +15 -0
  89. data/lib/dry/web/roda/skeletons/umbrella_project/system/__underscored_app_name__/application.rb.tt +14 -0
  90. data/lib/dry/web/roda/skeletons/{umbrella → umbrella_project}/system/__underscored_app_name__/container.rb.tt +1 -0
  91. data/lib/dry/web/roda/skeletons/umbrella_project/system/__underscored_app_name__/import.rb.tt +5 -0
  92. data/lib/dry/web/roda/skeletons/umbrella_project/system/__underscored_app_name__/settings.rb.tt +17 -0
  93. data/lib/dry/web/roda/skeletons/{umbrella → umbrella_project}/system/boot.rb.tt +0 -0
  94. data/lib/dry/web/roda/skeletons/umbrella_project/system/boot/monitor.rb.tt +10 -0
  95. data/lib/dry/web/roda/skeletons/umbrella_project/system/boot/rom.rb.tt +26 -0
  96. data/lib/dry/web/roda/version.rb +1 -1
  97. data/script/ci +67 -0
  98. data/script/setup +47 -0
  99. data/script/teardown +42 -0
  100. data/spec/integration/new_app_spec.rb +21 -0
  101. data/spec/spec_helper.rb +14 -16
  102. data/spec/support/app.rb +64 -0
  103. data/spec/support/bundler.rb +113 -0
  104. data/spec/support/cli.rb +47 -0
  105. data/spec/support/directories.rb +37 -0
  106. data/spec/support/env.rb +84 -0
  107. data/spec/support/files.rb +59 -0
  108. data/spec/support/project.rb +60 -0
  109. data/spec/support/requests.rb +5 -0
  110. data/spec/support/silently.rb +28 -0
  111. metadata +139 -49
  112. data/lib/dry/web/roda/skeletons/umbrella/system/__underscored_app_name__/application.rb.tt +0 -7
  113. data/lib/dry/web/roda/skeletons/umbrella/system/boot/logger.rb.tt +0 -5
@@ -0,0 +1,23 @@
1
+ require "dry/web/umbrella"
2
+ require_relative "settings"
3
+
4
+ module <%= config[:camel_cased_app_name] %>
5
+ class Container < Dry::Web::Umbrella
6
+ configure do
7
+ config.name = :<%= config[:underscored_app_name] %>
8
+ config.default_namespace = "<%= config[:underscored_app_name] %>"
9
+ config.settings_loader = <%= config[:camel_cased_app_name] %>::Settings
10
+ config.listeners = true
11
+
12
+ config.auto_register = %w[
13
+ lib/<%= config[:underscored_app_name] %>
14
+ ]
15
+ end
16
+
17
+ load_paths! "lib", "system"
18
+
19
+ def self.settings
20
+ config.settings
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ require "dry-configurable"
2
+ require "dry-transaction"
3
+ require "<%= config[:underscored_app_name] %>/container"
4
+
5
+ module <%= config[:camel_cased_app_name] %>
6
+ class Transactions
7
+ extend Dry::Configurable
8
+
9
+ attr_reader :options
10
+
11
+ setting :container, <%= config[:camel_cased_app_name] %>::Container
12
+ setting :options, {}
13
+
14
+ def self.define(&block)
15
+ yield(new(options))
16
+ end
17
+
18
+ def self.options
19
+ {container: config.container}.merge(config.options)
20
+ end
21
+
22
+ def initialize(options)
23
+ @options = options
24
+ end
25
+
26
+ def container
27
+ options[:container]
28
+ end
29
+
30
+ def define(name, &block)
31
+ container.register(name, Dry.Transaction(options, &block))
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,11 @@
1
+ require "byebug" if ENV["RACK_ENV"] == "development"
2
+ require "pry" if ENV["RACK_ENV"] == "development"
3
+
4
+ require_relative "<%= config[:underscored_app_name] %>/container"
5
+
6
+ <%= config[:camel_cased_app_name] %>::Container.finalize!
7
+
8
+ require "<%= config[:underscored_app_name] %>/application"
9
+
10
+ require "<%= config[:underscored_app_name] %>/transactions"
11
+ <%= config[:camel_cased_app_name] %>::Container.require "transactions/**/*.rb"
@@ -0,0 +1,10 @@
1
+ <%= config[:camel_cased_app_name] %>::Container.finalize :monitor do |_container|
2
+ init do
3
+ require "dry/monitor"
4
+ require "rom/instrumentation"
5
+ end
6
+
7
+ start do
8
+ Dry::Monitor::SQL::Logger.new(logger).subscribe(notifications)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require "<%= config[:underscored_app_name] %>/transactions"
2
+
3
+ <%= config[:camel_cased_app_name] %>::Transactions.define do |t|
4
+ # Define your dry-transaction objects here:
5
+ #
6
+ # t.define "transactions.users.sign_up" do
7
+ # step :persist, with: "users.operations.sign_up"
8
+ # # other steps here
9
+ # end
10
+ end
@@ -0,0 +1,11 @@
1
+ require "<%= config[:underscored_app_name] %>/view_controller"
2
+
3
+ module <%= config[:camel_cased_app_name] %>
4
+ module Views
5
+ class Welcome < <%= config[:camel_cased_app_name] %>::ViewController
6
+ configure do |config|
7
+ config.template = "welcome"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,8 +4,8 @@ require_relative "container"
4
4
  module <%= config[:camel_cased_app_name] %>
5
5
  class Application < Dry::Web::Roda::Application
6
6
  configure do |config|
7
- config.routes = "web/routes".freeze
8
7
  config.container = Container
8
+ config.routes = "web/routes".freeze
9
9
  end
10
10
 
11
11
  opts[:root] = Pathname(__FILE__).join("../..").realpath.dirname
@@ -9,6 +9,8 @@ module <%= config[:camel_cased_app_name] %>
9
9
  configure do |config|
10
10
  config.root = Pathname(__FILE__).join("../..").realpath.dirname.freeze
11
11
 
12
+ config.logger = <%= config[:camel_cased_umbrella_name] %>::Container[:logger]
13
+
12
14
  config.default_namespace = "<%= config[:underscored_app_name] %>"
13
15
 
14
16
  config.auto_register = %w[
@@ -0,0 +1,13 @@
1
+ require "slim"
2
+ require "dry-view"
3
+ require "<%= config[:underscored_app_name] %>/container"
4
+
5
+ module <%= config[:camel_cased_app_name] %>
6
+ class ViewController < Dry::View::Controller
7
+ configure do |config|
8
+ config.paths = [Container.root.join("web/templates")]
9
+ config.context = Container["view_context"]
10
+ config.layout = "application"
11
+ end
12
+ end
13
+ end
@@ -1,7 +1,6 @@
1
1
  require_relative "<%= config[:underscored_app_name] %>/container"
2
2
 
3
- <%= config[:camel_cased_app_name] %>::Container.finalize! do |container|
4
- end
3
+ <%= config[:camel_cased_app_name] %>::Container.finalize!
5
4
 
6
5
  require "<%= config[:underscored_app_name] %>/application"
7
6
  require "<%= config[:underscored_app_name] %>/transactions"
@@ -0,0 +1,3 @@
1
+ require "<%= config[:underscored_app_name] %>/view_context"
2
+
3
+ <%= config[:camel_cased_app_name] %>::Container.register "view_context", <%= config[:camel_cased_app_name] %>::ViewContext.new
@@ -0,0 +1,7 @@
1
+ # Define your routes like this:
2
+ #
3
+ # class <%= config[:camel_cased_app_name] %>::Application
4
+ # route "example" do |r|
5
+ # # Routes go here
6
+ # end
7
+ # end
@@ -0,0 +1 @@
1
+ h1 Welcome to dry-web-roda!
@@ -0,0 +1,39 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rake"
4
+
5
+ # Web framework
6
+ gem "dry-web", ">= 0.6.0"
7
+ gem "dry-web-roda", ">= 0.6.0"
8
+ gem "puma"
9
+ gem "rack_csrf"
10
+
11
+ gem "rack", ">= 2.0"
12
+ gem "shotgun", ">= 0.9.2"
13
+
14
+ # Database persistence
15
+ gem "pg"
16
+ gem "rom-repository"
17
+ gem "rom-sql"
18
+
19
+ # Application dependencies
20
+ gem "dry-matcher"
21
+ gem "dry-monads"
22
+ gem "dry-struct"
23
+ gem "dry-transaction"
24
+ gem "dry-types"
25
+ gem "dry-validation"
26
+ gem "dry-view", ">= 0.2.2"
27
+ gem "slim"
28
+
29
+ group :development, :test do
30
+ gem "pry-byebug", platform: :mri
31
+ end
32
+
33
+ group :test do
34
+ gem "capybara"
35
+ gem "capybara-screenshot"
36
+ gem "database_cleaner"
37
+ gem "poltergeist"
38
+ gem "rspec"
39
+ end
@@ -0,0 +1,12 @@
1
+ # <%= config[:camel_cased_app_name] %>
2
+
3
+ Welcome! You’ve generated an app using dry-web-roda.
4
+
5
+ ## First steps
6
+
7
+ 1. Run `bundle`
8
+ 1. Review `config/settings.yml` (and make a copy at `config/settings.example.yml` if you want to keep example settings checked in)
9
+ 1. Create a `<%= config[:underscored_app_name] %>_development` database
10
+ 1. Add your own steps to `bin/setup`
11
+ 1. Run the app with `bundle exec shotgun -p 3000 -o 0.0.0.0 config.ru`
12
+ 1. Initialize git with `git init` and make your initial commit
@@ -0,0 +1,81 @@
1
+ require "bundler/setup"
2
+
3
+ require "byebug" unless ENV["RACK_ENV"] == "production"
4
+
5
+ begin
6
+ require "rspec/core/rake_task"
7
+ RSpec::Core::RakeTask.new :spec
8
+ task default: [:spec]
9
+ rescue LoadError
10
+ end
11
+
12
+ require_relative "system/<%= config[:underscored_app_name] %>/container"
13
+
14
+ require "rom/sql/rake_task"
15
+ require "sequel"
16
+ namespace :db do
17
+ task :setup do
18
+ <%= config[:camel_cased_app_name] %>::Container.boot! :rom
19
+ end
20
+
21
+ # The following migration tasks are adapted from https://gist.github.com/kalmbach/4471560
22
+ Sequel.extension :migration
23
+ DB = Sequel.connect(<%= config[:camel_cased_app_name] %>::Container.settings.database_url)
24
+
25
+ desc "Prints current schema version"
26
+ task :version do
27
+ version = if DB.tables.include?(:schema_migrations)
28
+ DB[:schema_migrations].order(:filename).last[:filename]
29
+ end || "not available"
30
+
31
+ puts "Current schema version: #{version}"
32
+ end
33
+
34
+ namespace :structure do
35
+ desc "Dump database structure to db/structure.sql"
36
+ task :dump do
37
+ require "uri"
38
+ uri = URI(DB.url)
39
+
40
+ dump = `pg_dump -h #{uri.hostname} -s -x -O #{uri.path.tr("/", "")}`
41
+ File.open "db/structure.sql", "w" do |file|
42
+ file.write dump
43
+ end
44
+ end
45
+ end
46
+
47
+ task :check_migrations_exist do
48
+ unless Dir["db/migrate/*.rb"].any?
49
+ puts "No migrations found"
50
+ exit 1
51
+ end
52
+ end
53
+
54
+ # Enhance the migration task provided by ROM
55
+ desc "Perform migration up to latest migration available"
56
+ task :migrate => [:check_migrations_exist] do
57
+ # Once db:migrate finishes, dump the db structure:
58
+ Rake::Task["db:structure:dump"].execute
59
+
60
+ # And print the current migration version:
61
+ Rake::Task["db:version"].execute
62
+ end
63
+
64
+ desc "Perform rollback to specified target"
65
+ task :rollback, :target do |t, args|
66
+ Sequel::Migrator.run(DB, "db/migrate", :target => args[:target].to_i)
67
+ Rake::Task["db:version"].execute
68
+ end
69
+
70
+ desc "Load seed data into the database"
71
+ task :seed do
72
+ seed_data = File.join("db", "seed.rb")
73
+ load(seed_data) if File.exist?(seed_data)
74
+ end
75
+
76
+ desc "Load a small, representative set of data so that the application can start in a useful state (for development)."
77
+ task :sample_data do
78
+ sample_data = File.join("db", "sample_data.rb")
79
+ load(sample_data) if File.exist?(sample_data)
80
+ end
81
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dry/web/console"
5
+ require_relative "../system/boot"
6
+
7
+ Dry::Web::Console.start
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ APP_ROOT = File.expand_path("../../", __FILE__)
4
+
5
+ Dir.chdir(APP_ROOT) do
6
+ # Set up your app for development here
7
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "system/boot"
2
+ run <%= config[:camel_cased_app_name] %>::Application.freeze.app
@@ -0,0 +1,8 @@
1
+ development: &base
2
+ database_url: 'postgres://localhost/<%= config[:underscored_app_name] %>_development'
3
+ session_secret: '<%= SecureRandom.hex %>'
4
+ test:
5
+ <<: *base
6
+ database_url: 'postgres://localhost/<%= config[:underscored_app_name] %>_test'
7
+ production:
8
+ <<: *base
@@ -0,0 +1 @@
1
+ # Build your sample data here
@@ -0,0 +1 @@
1
+ # Build your seed data here
@@ -0,0 +1,11 @@
1
+ require "rom-repository"
2
+ require "<%= config[:underscored_app_name] %>/container"
3
+ require "<%= config[:underscored_app_name] %>/import"
4
+
5
+ <%= config[:camel_cased_app_name] %>::Container.boot! :rom
6
+
7
+ module <%= config[:camel_cased_app_name] %>
8
+ class Repository < ROM::Repository::Root
9
+ include <%= config[:camel_cased_app_name] %>::Import.args["persistence.rom"]
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ module <%= config[:camel_cased_app_name] %>
2
+ class ViewContext
3
+ attr_reader :attrs
4
+
5
+ def initialize(attrs = {})
6
+ @attrs = attrs
7
+ end
8
+
9
+ def csrf_token
10
+ self[:csrf_token]
11
+ end
12
+
13
+ def csrf_metatag
14
+ self[:csrf_metatag]
15
+ end
16
+
17
+ def csrf_tag
18
+ self[:csrf_tag]
19
+ end
20
+
21
+ def flash
22
+ self[:flash]
23
+ end
24
+
25
+ def flash?
26
+ %i[notice alert].any? { |type| flash[type] }
27
+ end
28
+
29
+ def with(new_attrs)
30
+ self.class.new(attrs.merge(new_attrs))
31
+ end
32
+
33
+ private
34
+
35
+ def [](name)
36
+ attrs.fetch(name)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ require "rom-sql"
2
+
3
+ module ROM
4
+ class Relation
5
+ # Monkey patch ROM::SQL to support query logs until an official API is available
6
+ module Instrumentation
7
+ def self.included(klass)
8
+ klass.option :notifications, reader: true, optional: false, default: proc {
9
+ <%= config[:camel_cased_app_name] %>::Container[:notifications]
10
+ }
11
+ end
12
+
13
+ def to_a
14
+ notifications.instrument(:sql, name: name.relation, query: dataset.sql) do
15
+ super
16
+ end
17
+ end
18
+ end
19
+
20
+ SQL::Relation.include(Instrumentation)
21
+ end
22
+ end