Fix Rubocop issues

This commit is contained in:
Tim Riley 2020-03-13 23:06:56 +11:00
parent 209a3706a5
commit cb479c9d5e
No known key found for this signature in database
GPG Key ID: 747ABA1282E88BC9
86 changed files with 986 additions and 1021 deletions

32
Gemfile
View File

@ -1,34 +1,34 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source "https://rubygems.org"
eval_gemfile 'Gemfile.devtools' eval_gemfile "Gemfile.devtools"
gemspec gemspec
group :tools do group :tools do
gem 'hotch' gem "hotch"
gem 'pry-byebug', platform: :mri gem "pry-byebug", platform: :mri
end end
group :test do group :test do
gem 'rack', '>= 2.0.6' gem "rack", ">= 2.0.6"
gem 'erbse' gem "erbse"
gem 'erubi' gem "erubi"
gem 'hamlit' gem "hamlit"
gem 'hamlit-block' gem "hamlit-block"
gem 'slim', '~> 4.0' gem "slim", "~> 4.0"
end end
group :benchmarks do group :benchmarks do
gem 'benchmark-ips' gem "actionpack"
gem 'actionview' gem "actionview"
gem 'actionpack' gem "benchmark-ips"
end end
group :docs do group :docs do
gem 'yard' gem "redcarpet", platforms: :mri
gem 'yard-junk' gem "yard"
gem 'redcarpet', platforms: :mri gem "yard-junk"
end end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'bundler/gem_tasks' require "bundler/gem_tasks"
require 'rspec/core/rake_task' require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec)
task default: [:spec] task default: [:spec]

View File

@ -1,23 +1,25 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname' require "pathname"
require 'ostruct' require "ostruct"
require 'benchmark/ips' require "benchmark/ips"
require 'dry/view' require "dry/view"
require 'action_view' require "action_view"
require 'action_controller' require "action_controller"
TEMPLATES_PATHS = Pathname(__FILE__).dirname.join('templates') TEMPLATES_PATHS = Pathname(__FILE__).dirname.join("templates")
TEMPLATE_LOCALS = { users: [ TEMPLATE_LOCALS = {
OpenStruct.new(name: 'Jane', email: 'Jane@example.com'), users: [
OpenStruct.new(name: 'Teresa', email: 'teresa@example.com') OpenStruct.new(name: "Jane", email: "Jane@example.com"),
] } OpenStruct.new(name: "Teresa", email: "teresa@example.com")
]
}.freeze
ActionController::Base.view_paths = TEMPLATES_PATHS ActionController::Base.view_paths = TEMPLATES_PATHS
class UsersController < ActionController::Base class UsersController < ActionController::Base
layout 'app' layout "app"
attr_reader :users attr_reader :users
@ -29,8 +31,8 @@ end
class DryView < Dry::View class DryView < Dry::View
config.paths = TEMPLATES_PATHS config.paths = TEMPLATES_PATHS
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users expose :users
@ -39,7 +41,10 @@ end
action_controller = UsersController.new action_controller = UsersController.new
dry_view = DryView.new dry_view = DryView.new
if (action_controller_output = action_controller.index) != (dry_view_output = dry_view.(TEMPLATE_LOCALS).to_s) action_controller_output = action_controller.index
dry_view_output = dry_view.(TEMPLATE_LOCALS).to_s
if action_controller_output != dry_view_output
puts "Output doesn't match:" puts "Output doesn't match:"
puts puts
puts "ActionView:\n\n#{action_controller_output}\n" puts "ActionView:\n\n#{action_controller_output}\n"
@ -47,11 +52,11 @@ if (action_controller_output = action_controller.index) != (dry_view_output = dr
end end
Benchmark.ips do |x| Benchmark.ips do |x|
x.report('action_controller') do x.report("action_controller") do
1000.times { action_controller.index } 1000.times { action_controller.index }
end end
x.report('dry-view') do x.report("dry-view") do
1000.times { dry_view.(TEMPLATE_LOCALS).to_s } 1000.times { dry_view.(TEMPLATE_LOCALS).to_s }
end end

View File

@ -1,21 +1,23 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'hotch' require "hotch"
require 'pathname' require "pathname"
require 'ostruct' require "ostruct"
require 'dry/view' require "dry/view"
TEMPLATES_PATHS = Pathname(__FILE__).dirname.join('templates') TEMPLATES_PATHS = Pathname(__FILE__).dirname.join("templates")
TEMPLATE_LOCALS = { users: [ TEMPLATE_LOCALS = {
OpenStruct.new(name: 'Jane', email: 'Jane@example.com'), users: [
OpenStruct.new(name: 'Teresa', email: 'teresa@example.com') OpenStruct.new(name: "Jane", email: "Jane@example.com"),
] } OpenStruct.new(name: "Teresa", email: "teresa@example.com")
]
}.freeze
class View < Dry::View class View < Dry::View
config.paths = TEMPLATES_PATHS config.paths = TEMPLATES_PATHS
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
expose :users expose :users
end end

View File

@ -1,8 +1,8 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
require 'bundler/setup' require "bundler/setup"
require 'dry-view' require "dry-view"
require 'pry' require "pry"
Pry.start Pry.start

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
require_relative 'setup_helpers' require_relative "setup_helpers"
Setup.execute 'bundle' Setup.execute "bundle"

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'open3' require "open3"
module Setup module Setup
module_function module_function
@ -18,7 +18,7 @@ module Setup
status = wait_thr.value status = wait_thr.value
end end
if !status.success? unless status.success?
puts "Failed to run #{cmd}" puts "Failed to run #{cmd}"
puts err puts err
exit 1 exit 1

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source "https://rubygems.org"
gem 'byebug' gem "byebug"
gem 'dry-view', path: '../..' gem "dry-view", path: "../.."
gem 'dry-web' gem "dry-web"
gem 'sinatra' gem "sinatra"
gem 'slim' gem "slim"

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative './system/boot' require_relative "./system/boot"
require 'example_app/web' require "example_app/web"
run ExampleApp::Web run ExampleApp::Web

View File

@ -1,18 +1,18 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'ostruct' require "ostruct"
module ExampleApp module ExampleApp
Article = Struct.new(:slug, :title) Article = Struct.new(:slug, :title)
class ArticleRepo class ArticleRepo
ARTICLES = [ ARTICLES = [
{slug: 'together-breakfast', title: 'Together Breakfast'}, {slug: "together-breakfast", title: "Together Breakfast"},
{slug: 'cat-fingers', title: 'Cat Fingers'}, {slug: "cat-fingers", title: "Cat Fingers"}
].freeze ].freeze
def by_slug!(slug) def by_slug!(slug)
if (article = ARTICLES.detect { |a| a[:slug] == slug}) if (article = ARTICLES.detect { |a| a[:slug] == slug })
Article.new(*article.values) Article.new(*article.values)
else else
raise "Article with slug +#{slug}+ not found" raise "Article with slug +#{slug}+ not found"

View File

@ -2,13 +2,13 @@
# auto_register: false # auto_register: false
require 'dry/view' require "dry/view"
require 'slim' require "slim"
require 'example_app/container' require "example_app/container"
module ExampleApp module ExampleApp
class View < Dry::View class View < Dry::View
config.paths = Container.root.join('web/templates') config.paths = Container.root.join("web/templates")
config.layout = 'application' config.layout = "application"
end end
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative '../view' require_relative "../view"
require 'dry/view/context' require "dry/view/context"
module ExampleApp module ExampleApp
class View class View

View File

@ -1,15 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'example_app/import' require "example_app/import"
require 'example_app/view' require "example_app/view"
module ExampleApp module ExampleApp
module Views module Views
module Articles module Articles
class Index < View class Index < View
include Import['article_repo'] include Import["article_repo"]
config.template = 'articles/index' config.template = "articles/index"
expose :articles do expose :articles do
article_repo.listing article_repo.listing
@ -18,4 +18,3 @@ module ExampleApp
end end
end end
end end

View File

@ -1,15 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'example_app/import' require "example_app/import"
require 'example_app/view' require "example_app/view"
module ExampleApp module ExampleApp
module Views module Views
module Articles module Articles
class Show < View class Show < View
include Import['article_repo'] include Import["article_repo"]
config.template = 'articles/show' config.template = "articles/show"
expose :article do |slug:| expose :article do |slug:|
article_repo.by_slug!(slug) article_repo.by_slug!(slug)
@ -18,4 +18,3 @@ module ExampleApp
end end
end end
end end

View File

@ -1,37 +1,37 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'sinatra/base' require "sinatra/base"
require 'byebug' require "byebug"
module ExampleApp module ExampleApp
class Web < Sinatra::Base class Web < Sinatra::Base
get '/' do get "/" do
redirect '/articles' redirect "/articles"
end end
get '/articles' do get "/articles" do
render_view 'articles.index' render_view "articles.index"
end end
get '/articles/:slug' do |slug| get "/articles/:slug" do |slug|
render_view 'articles.show', slug: slug render_view "articles.show", slug: slug
end end
helpers do helpers do
def render_view(identifier, with: {}, **input) def render_view(identifier, with: {}, **input)
container["views.#{identifier}"].( container["views.#{identifier}"].(
context: view_context(**with), context: view_context(**with),
**input, **input
).to_s ).to_s
end end
def view_context(**options) def view_context(**options)
container['view.context'].with(view_context_options(**options)) container["view.context"].with(view_context_options(**options))
end end
def view_context_options(**overrides) def view_context_options(**overrides)
{ {
request: request, request: request
}.merge(overrides) }.merge(overrides)
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative 'example_app/container' require_relative "example_app/container"
container = ExampleApp::Container container = ExampleApp::Container
container.finalize! container.finalize!

View File

@ -1,17 +1,17 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/web/container' require "dry/web/container"
require 'dry/system/components' require "dry/system/components"
module ExampleApp module ExampleApp
class Container < Dry::Web::Container class Container < Dry::Web::Container
configure do configure do
config.name = :example_app config.name = :example_app
config.log_levels = %i[test development production].map { |e| [e, Logger::DEBUG] }.to_h config.log_levels = %i[test development production].map { |e| [e, Logger::DEBUG] }.to_h
config.default_namespace = 'example_app' config.default_namespace = "example_app"
config.auto_register = %w[lib/example_app] config.auto_register = %w[lib/example_app]
end end
load_paths! 'lib' load_paths! "lib"
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative 'container' require_relative "container"
module ExampleApp module ExampleApp
Import = Container.injector Import = Container.injector

View File

@ -1,21 +1,21 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'https://rubygems.org' source "https://rubygems.org"
ruby '2.6.0' ruby "2.6.0"
gem 'rails', '~> 5.2.2' gem "puma", "~> 3.11"
gem 'sqlite3' gem "rails", "~> 5.2.2"
gem 'puma', '~> 3.11' gem "sqlite3"
gem 'dry-view', path: '../..' gem "dry-view", path: "../.."
gem 'slim' gem "slim"
group :development, :test do group :development, :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem "byebug", platforms: %i[mri mingw x64_mingw]
end end
group :development do group :development do
gem 'web-console', '>= 3.3.0' gem "listen", ">= 3.0.5", "< 3.2"
gem 'listen', '>= 3.0.5', '< 3.2' gem "web-console", ">= 3.3.0"
end end

View File

@ -3,6 +3,6 @@
# Add your own tasks in files placed in lib/tasks ending in .rake, # Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative 'config/application' require_relative "config/application"
Rails.application.load_tasks Rails.application.load_tasks

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
class ApplicationView < Dry::View class ApplicationView < Dry::View
config.paths = Rails.root.join('app/templates') config.paths = Rails.root.join("app/templates")
config.default_context = ApplicationViewContext.new config.default_context = ApplicationViewContext.new
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/context' require "dry/view/context"
class ApplicationViewContext < Dry::View::Context class ApplicationViewContext < Dry::View::Context
def method_missing(name, *args, &block) def method_missing(name, *args, &block)
@ -16,7 +16,9 @@ class ApplicationViewContext < Dry::View::Context
private private
def respond_to_missing?(name, include_private = false) def respond_to_missing?(name, include_private = false)
url_helpers.respond_to?(name, include_private) || helpers.respond_to?(name, include_private) || super url_helpers.respond_to?(name, include_private) ||
helpers.respond_to?(name, include_private) ||
super
end end
def url_helpers def url_helpers

View File

@ -3,7 +3,7 @@
module Views module Views
module Articles module Articles
class Index < ApplicationView class Index < ApplicationView
config.template = 'articles/index' config.template = "articles/index"
expose :articles expose :articles
end end

View File

@ -3,7 +3,7 @@
module Views module Views
module Articles module Articles
class Show < ApplicationView class Show < ApplicationView
config.template = 'articles/show' config.template = "articles/show"
expose :article expose :article
end end

View File

@ -1,5 +1,5 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
load Gem.bin_path('bundler', 'bundle') load Gem.bin_path("bundler", "bundle")

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
APP_PATH = File.expand_path('../config/application', __dir__) APP_PATH = File.expand_path("../config/application", __dir__)
require_relative '../config/boot' require_relative "../config/boot"
require 'rails/commands' require "rails/commands"

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
require_relative '../config/boot' require_relative "../config/boot"
require 'rake' require "rake"
Rake.application.run Rake.application.run

View File

@ -1,38 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a starting point to setup your application.
# Add necessary setup steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
# cp 'config/database.yml.sample', 'config/database.yml'
# end
puts "\n== Preparing database =="
system! 'bin/rails db:setup'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
end

View File

@ -1,33 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
end
chdir APP_ROOT do
# This script is a way to update your development environment automatically.
# Add necessary update steps to this file.
puts '== Installing dependencies =='
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
puts "\n== Updating database =="
system! 'bin/rails db:migrate'
puts "\n== Removing old logs and tempfiles =="
system! 'bin/rails log:clear tmp:clear'
puts "\n== Restarting application server =="
system! 'bin/rails restart'
end

View File

@ -1,13 +1,13 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
# frozen_string_literal: true # frozen_string_literal: true
APP_ROOT = File.expand_path('..', __dir__) APP_ROOT = File.expand_path("..", __dir__)
Dir.chdir(APP_ROOT) do Dir.chdir(APP_ROOT) do
begin begin
exec 'yarnpkg', *ARGV exec "yarnpkg", *ARGV
rescue Errno::ENOENT rescue Errno::ENOENT
$stderr.puts 'Yarn executable was not detected in the system.' warn "Yarn executable was not detected in the system."
$stderr.puts 'Download Yarn at https://yarnpkg.com/en/docs/install' warn "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1 exit 1
end end
end end

View File

@ -2,6 +2,6 @@
# This file is used by Rack-based servers to start the application. # This file is used by Rack-based servers to start the application.
require_relative 'config/environment' require_relative "config/environment"
run Rails.application run Rails.application

View File

@ -1,12 +1,12 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative 'boot' require_relative "boot"
require 'rails' require "rails"
require 'active_model/railtie' require "active_model/railtie"
require 'active_record/railtie' require "active_record/railtie"
require 'action_controller/railtie' require "action_controller/railtie"
require 'action_view/railtie' require "action_view/railtie"
# Require the gems listed in Gemfile, including any gems # Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production. # you've limited to :test, :development, or :production.
@ -17,7 +17,7 @@ module DryViewExample
config.load_defaults 5.2 config.load_defaults 5.2
# dry-view setup # dry-view setup
Rails.application.config.autoload_paths << Rails.root.join('app/views') Rails.application.config.autoload_paths << Rails.root.join("app/views")
# Remove heinous monkey patch # Remove heinous monkey patch
Dry::View::Part.undef_method :to_param Dry::View::Part.undef_method :to_param

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile. require "bundler/setup" # Set up gems listed in the Gemfile.

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
# Load the Rails application. # Load the Rails application.
require_relative 'application' require_relative "application"
# Initialize the Rails application. # Initialize the Rails application.
Rails.application.initialize! Rails.application.initialize!

View File

@ -16,12 +16,12 @@ Rails.application.configure do
# Enable/disable caching. By default caching is disabled. # Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching. # Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist? if Rails.root.join("tmp", "caching-dev.txt").exist?
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
config.cache_store = :memory_store config.cache_store = :memory_store
config.public_file_server.headers = { config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}" "Cache-Control" => "public, max-age=#{2.days.to_i}"
} }
else else
config.action_controller.perform_caching = false config.action_controller.perform_caching = false
@ -38,7 +38,6 @@ Rails.application.configure do
# Highlight code that triggered database queries in logs. # Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true config.active_record.verbose_query_logs = true
# Raises error for missing translations # Raises error for missing translations
# config.action_view.raise_on_missing_translations = true # config.action_view.raise_on_missing_translations = true

View File

@ -22,7 +22,7 @@ Rails.application.configure do
# Disable serving static files from the `/public` folder by default since # Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this. # Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
# Enable serving of images, stylesheets, and JavaScripts from an asset server. # Enable serving of images, stylesheets, and JavaScripts from an asset server.
# config.action_controller.asset_host = 'http://assets.example.com' # config.action_controller.asset_host = 'http://assets.example.com'
@ -39,7 +39,7 @@ Rails.application.configure do
config.log_level = :debug config.log_level = :debug
# Prepend all log lines with the following tags. # Prepend all log lines with the following tags.
config.log_tags = [ :request_id ] config.log_tags = [:request_id]
# Use a different cache store in production. # Use a different cache store in production.
# config.cache_store = :mem_cache_store # config.cache_store = :mem_cache_store
@ -62,7 +62,7 @@ Rails.application.configure do
# require 'syslog/logger' # require 'syslog/logger'
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
if ENV['RAILS_LOG_TO_STDOUT'].present? if ENV["RAILS_LOG_TO_STDOUT"].present?
logger = ActiveSupport::Logger.new(STDOUT) logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter logger.formatter = config.log_formatter
config.logger = ActiveSupport::TaggedLogging.new(logger) config.logger = ActiveSupport::TaggedLogging.new(logger)

View File

@ -17,7 +17,7 @@ Rails.application.configure do
# Configure public file server for tests with Cache-Control for performance. # Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true config.public_file_server.enabled = true
config.public_file_server.headers = { config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{1.hour.to_i}" "Cache-Control" => "public, max-age=#{1.hour.to_i}"
} }
# Show full error reports and disable caching. # Show full error reports and disable caching.

View File

@ -6,16 +6,16 @@
# the maximum value specified for Puma. Default is set to 5 threads for minimum # the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record. # and maximum; this matches the default thread size of Active Record.
# #
threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests; default is 3000. # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
# #
port ENV.fetch('PORT') { 3000 } port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in. # Specifies the `environment` that Puma will run in.
# #
environment ENV.fetch('RAILS_ENV') { 'development' } environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode. # Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together # Workers are forked webserver processes. If using threads and workers together

View File

@ -2,5 +2,5 @@
Rails.application.routes.draw do Rails.application.routes.draw do
resources :articles, only: %i[index show] resources :articles, only: %i[index show]
root to: 'articles#index' root to: "articles#index"
end end

View File

@ -12,12 +12,10 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_01_16_013956) do ActiveRecord::Schema.define(version: 20_190_116_013_956) do
create_table "articles", force: :cascade do |t|
create_table 'articles', force: :cascade do |t| t.string "title"
t.string 'title' t.datetime "created_at", null: false
t.datetime 'created_at', null: false t.datetime "updated_at", null: false
t.datetime 'updated_at', null: false
end end
end end

View File

@ -1,3 +1,3 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"

View File

@ -1,19 +1,19 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/configurable' require "dry/configurable"
require 'dry/core/cache' require "dry/core/cache"
require 'dry/equalizer' require "dry/equalizer"
require 'dry/inflector' require "dry/inflector"
require_relative 'view/context' require_relative "view/context"
require_relative 'view/exposures' require_relative "view/exposures"
require_relative 'view/errors' require_relative "view/errors"
require_relative 'view/part_builder' require_relative "view/part_builder"
require_relative 'view/path' require_relative "view/path"
require_relative 'view/render_environment' require_relative "view/render_environment"
require_relative 'view/rendered' require_relative "view/rendered"
require_relative 'view/renderer' require_relative "view/renderer"
require_relative 'view/scope_builder' require_relative "view/scope_builder"
# A collection of next-generation Ruby libraries, helping you to write clear, # A collection of next-generation Ruby libraries, helping you to write clear,
# flexible, and more maintainable Ruby code. Each dry-rb gem fulfils a common # flexible, and more maintainable Ruby code. Each dry-rb gem fulfils a common
@ -35,7 +35,7 @@ module Dry
# @api public # @api public
class View class View
# @api private # @api private
DEFAULT_RENDERER_OPTIONS = { default_encoding: 'utf-8' }.freeze DEFAULT_RENDERER_OPTIONS = {default_encoding: "utf-8"}.freeze
include Dry::Equalizer(:config, :exposures) include Dry::Equalizer(:config, :exposures)
@ -92,7 +92,7 @@ module Dry
# @param dir [String] directory name # @param dir [String] directory name
# @api public # @api public
# @!scope class # @!scope class
setting :layouts_dir, 'layouts' setting :layouts_dir, "layouts"
# @overload config.scope=(scope_class) # @overload config.scope=(scope_class)
# Set the scope class to use when rendering the view's template. # Set the scope class to use when rendering the view's template.
@ -230,8 +230,10 @@ module Dry
# @!macro [new] exposure_options # @!macro [new] exposure_options
# @param options [Hash] the exposure's options # @param options [Hash] the exposure's options
# @option options [Boolean] :layout expose this value to the layout (defaults to false) # @option options [Boolean] :layout expose this value to the layout (defaults to false)
# @option options [Boolean] :decorate decorate this value in a matching Part (defaults to true) # @option options [Boolean] :decorate decorate this value in a matching Part (defaults to
# @option options [Symbol, Class] :as an alternative name or class to use when finding a matching Part # true)
# @option options [Symbol, Class] :as an alternative name or class to use when finding a
# matching Part
# @overload expose(name, **options, &block) # @overload expose(name, **options, &block)
# Define a value to be passed to the template. The return value of the # Define a value to be passed to the template. The return value of the
@ -315,7 +317,8 @@ module Dry
# #
# @param name [Symbol] name for the exposure # @param name [Symbol] name for the exposure
# @macro exposure_options # @macro exposure_options
# @option options [Boolean] :default a default value to provide if there is no matching input data # @option options [Boolean] :default a default value to provide if there is no matching
# input data
# #
# @overload expose(*names, **options) # @overload expose(*names, **options)
# Define multiple values to pass through from the input data (when there # Define multiple values to pass through from the input data (when there
@ -326,7 +329,8 @@ module Dry
# #
# @param names [Symbol] names for the exposures # @param names [Symbol] names for the exposures
# @macro exposure_options # @macro exposure_options
# @option options [Boolean] :default a default value to provide if there is no matching input data # @option options [Boolean] :default a default value to provide if there is no matching
# input data
# #
# @see https://dry-rb.org/gems/dry-view/exposures/ # @see https://dry-rb.org/gems/dry-view/exposures/
# #
@ -411,7 +415,7 @@ module Dry
config.paths, config.paths,
format: format, format: format,
engine_mapping: config.renderer_engine_mapping, engine_mapping: config.renderer_engine_mapping,
**config.renderer_options, **config.renderer_options
) )
} }
end end
@ -467,7 +471,10 @@ module Dry
if layout? if layout?
layout_env = self.class.layout_env(format: format, context: context) layout_env = self.class.layout_env(format: format, context: context)
output = env.template(self.class.layout_path, layout_env.scope(config.scope, layout_locals(locals))) { output } output = env.template(
self.class.layout_path,
layout_env.scope(config.scope, layout_locals(locals))
) { output }
end end
Rendered.new(output: output, locals: locals) Rendered.new(output: output, locals: locals)
@ -477,8 +484,8 @@ module Dry
# @api private # @api private
def ensure_config def ensure_config
raise UndefinedConfigError.new(:paths) unless Array(config.paths).any? raise UndefinedConfigError, :paths unless Array(config.paths).any?
raise UndefinedConfigError.new(:template) unless config.template raise UndefinedConfigError, :template unless config.template
end end
# @api private # @api private
@ -501,7 +508,7 @@ module Dry
# @api private # @api private
def layout? def layout?
!!config.layout !!config.layout # rubocop:disable Style/DoubleNegation
end end
end end
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/equalizer' require "dry/equalizer"
require_relative 'decorated_attributes' require_relative "decorated_attributes"
module Dry module Dry
class View class View
@ -43,7 +43,7 @@ module Dry
# @api private # @api private
def for_render_env(render_env) def for_render_env(render_env)
return self if render_env == self._render_env return self if render_env == _render_env
self.class.new(**_options.merge(render_env: render_env)) self.class.new(**_options.merge(render_env: render_env))
end end
@ -72,7 +72,7 @@ module Dry
def with(**new_options) def with(**new_options)
self.class.new( self.class.new(
render_env: _render_env, render_env: _render_env,
**_options.merge(new_options), **_options.merge(new_options)
) )
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'set' require "set"
module Dry module Dry
class View class View
@ -27,7 +27,8 @@ module Dry
# #
# @param names [Array<Symbol>] the attribute names # @param names [Array<Symbol>] the attribute names
# @param options [Hash] the options to pass to the Part Builder # @param options [Hash] the options to pass to the Part Builder
# @option options [Symbol, Class] :as an alternative name or class to use when finding a matching Part # @option options [Symbol, Class] :as an alternative name or class to use when finding a
# matching Part
# #
# @api public # @api public
def decorate(*names, **options) def decorate(*names, **options)

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry-equalizer' require "dry-equalizer"
module Dry module Dry
class View class View
@ -10,8 +10,8 @@ module Dry
class Exposure class Exposure
include Dry::Equalizer(:name, :proc, :object, :options) include Dry::Equalizer(:name, :proc, :object, :options)
EXPOSURE_DEPENDENCY_PARAMETER_TYPES = [:req, :opt].freeze EXPOSURE_DEPENDENCY_PARAMETER_TYPES = %i[req opt].freeze
INPUT_PARAMETER_TYPES = [:key, :keyreq, :keyrest].freeze INPUT_PARAMETER_TYPES = %i[key keyreq keyrest].freeze
attr_reader :name attr_reader :name
attr_reader :proc attr_reader :proc

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'tsort' require "tsort"
require 'dry/equalizer' require "dry/equalizer"
require 'dry/view/exposure' require "dry/view/exposure"
module Dry module Dry
class View class View
@ -46,8 +46,9 @@ module Dry
end end
def call(input) def call(input)
# rubocop:disable Style/MultilineBlockChain
tsort.each_with_object({}) { |name, memo| tsort.each_with_object({}) { |name, memo|
next unless exposure = self[name] next unless (exposure = self[name])
value = exposure.(input, memo) value = exposure.(input, memo)
value = yield(value, exposure) if block_given? value = yield(value, exposure) if block_given?
@ -56,6 +57,7 @@ module Dry
}.each_with_object({}) { |(name, value), memo| }.each_with_object({}) { |(name, value), memo|
memo[name] = value unless self[name].private? memo[name] = value unless self[name].private?
} }
# rubocop:enable Style/MultilineBlockChain
end end
private private

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/equalizer' require "dry/equalizer"
require_relative 'decorated_attributes' require_relative "decorated_attributes"
require_relative 'render_environment_missing' require_relative "render_environment_missing"
module Dry module Dry
class View class View
@ -60,7 +60,7 @@ module Dry
# #
# @api private # @api private
def self.part_name(inflector) def self.part_name(inflector)
name ? inflector.underscore(inflector.demodulize(name)) : 'part' name ? inflector.underscore(inflector.demodulize(name)) : "part"
end end
# Returns a new Part instance # Returns a new Part instance
@ -70,7 +70,11 @@ module Dry
# @param render_env [RenderEnvironment] render environment # @param render_env [RenderEnvironment] render environment
# #
# @api public # @api public
def initialize(render_env: RenderEnvironmentMissing.new, name: self.class.part_name(render_env.inflector), value:) def initialize(
render_env: RenderEnvironmentMissing.new,
name: self.class.part_name(render_env.inflector),
value:
)
@_name = name @_name = name
@_value = value @_value = value
@_render_env = render_env @_render_env = render_env
@ -115,15 +119,18 @@ module Dry
# itself responds to `#render`. # itself responds to `#render`.
# #
# @param partial_name [Symbol, String] partial name # @param partial_name [Symbol, String] partial name
# @param as [Symbol] the name for the Part to assume in the partial's locals. Default's to the Part's `_name`. # @param as [Symbol] the name for the Part to assume in the partial's locals. Defaults to
# the Part's `_name`.
# @param locals [Hash<Symbol, Object>] other locals to provide the partial # @param locals [Hash<Symbol, Object>] other locals to provide the partial
# #
# @return [String] rendered partial # @return [String] rendered partial
# #
# @api public # @api public
# rubocop:disable Naming/UncommunicativeMethodParamName
def _render(partial_name, as: _name, **locals, &block) def _render(partial_name, as: _name, **locals, &block)
_render_env.partial(partial_name, _render_env.scope({ as => self }.merge(locals)), &block) _render_env.partial(partial_name, _render_env.scope({as => self}.merge(locals)), &block)
end end
# rubocop:enable Naming/UncommunicativeMethodParamName
# Builds a new scope with the part included in its locals. # Builds a new scope with the part included in its locals.
# #
@ -133,14 +140,15 @@ module Dry
# A convenience alias for `#_scope`. Is available unless the value # A convenience alias for `#_scope`. Is available unless the value
# itself responds to `#scope`. # itself responds to `#scope`.
# #
# @param scope_name [Symbol, nil] scope name, used by the scope builder to determine the scope class # @param scope_name [Symbol, nil] scope name, used by the scope builder to determine the
# scope class
# @param locals [Hash<Symbol, Object>] other locals to provide the partial # @param locals [Hash<Symbol, Object>] other locals to provide the partial
# #
# @return [Dry::View::Scope] scope # @return [Dry::View::Scope] scope
# #
# @api public # @api public
def _scope(scope_name = nil, **locals) def _scope(scope_name = nil, **locals)
_render_env.scope(scope_name, { _name => self }.merge(locals)) _render_env.scope(scope_name, {_name => self}.merge(locals))
end end
# Returns a string representation of the value # Returns a string representation of the value
@ -168,12 +176,12 @@ module Dry
# @param options[Hash<Symbol, Object>] other options to provide when initializing the new part # @param options[Hash<Symbol, Object>] other options to provide when initializing the new part
# #
# @api public # @api public
def new(klass = (self.class), name: (_name), value: (_value), **options) def new(klass = self.class, name: _name, value: _value, **options)
klass.new( klass.new(
name: name, name: name,
value: value, value: value,
render_env: _render_env, render_env: _render_env,
**options, **options
) )
end end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/core/cache' require "dry/core/cache"
require 'dry/equalizer' require "dry/equalizer"
require_relative 'part' require_relative "part"
module Dry module Dry
class View class View
@ -54,7 +54,7 @@ module Dry
klass.new( klass.new(
name: name, name: name,
value: value, value: value,
render_env: render_env, render_env: render_env
) )
end end
@ -69,11 +69,13 @@ module Dry
build_part(name, arr, **options.merge(as: collection_as)) build_part(name, arr, **options.merge(as: collection_as))
end end
# rubocop:disable Lint/UnusedMethodArgument
def collection_options(name:, **options) def collection_options(name:, **options)
collection_as = options[:as].is_a?(Array) ? options[:as].first : nil collection_as = options[:as].is_a?(Array) ? options[:as].first : nil
options.merge(as: collection_as) options.merge(as: collection_as)
end end
# rubocop:enable Lint/UnusedMethodArgument
def collection_item_options(name:, **options) def collection_item_options(name:, **options)
singular_name = inflector.singularize(name).to_sym singular_name = inflector.singularize(name).to_sym
@ -90,7 +92,7 @@ module Dry
options.merge( options.merge(
name: singular_name, name: singular_name,
as: singular_as, as: singular_as
) )
end end
@ -106,6 +108,7 @@ module Dry
end end
end end
# rubocop:disable Metrics/PerceivedComplexity
def resolve_part_class(name:, fallback_class:) def resolve_part_class(name:, fallback_class:)
return fallback_class unless namespace return fallback_class unless namespace
@ -114,7 +117,7 @@ module Dry
# Give autoloaders a chance to act # Give autoloaders a chance to act
begin begin
klass = namespace.const_get(name) klass = namespace.const_get(name)
rescue NameError rescue NameError # rubocop:disable Lint/HandleExceptions
end end
if !klass && namespace.const_defined?(name, false) if !klass && namespace.const_defined?(name, false)
@ -127,6 +130,7 @@ module Dry
fallback_class fallback_class
end end
end end
# rubocop:enable Metrics/PerceivedComplexity
def inflector def inflector
render_env.inflector render_env.inflector

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'pathname' require "pathname"
require 'dry/core/cache' require "dry/core/cache"
module Dry module Dry
class View class View
@ -61,7 +61,7 @@ module Dry
end end
def lookup_in_parent_dir(name, format, child_dirs:) def lookup_in_parent_dir(name, format, child_dirs:)
!root? && chdir('..').lookup(name, format, child_dirs: child_dirs, parent_dir: true) !root? && chdir("..").lookup(name, format, child_dirs: child_dirs, parent_dir: true)
end end
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/equalizer' require "dry/equalizer"
module Dry module Dry
class View class View
@ -12,7 +12,7 @@ module Dry
inflector: config.inflector, inflector: config.inflector,
context: context, context: context,
scope_builder: config.scope_builder.new(namespace: config.scope_namespace), scope_builder: config.scope_builder.new(namespace: config.scope_namespace),
part_builder: config.part_builder.new(namespace: config.part_namespace), part_builder: config.part_builder.new(namespace: config.part_namespace)
) )
end end
@ -36,7 +36,7 @@ module Dry
part_builder.(name, value, **options) part_builder.(name, value, **options)
end end
def scope(name = nil, locals) def scope(name = nil, locals) # rubocop:disable Style/OptionalArguments
scope_builder.(name, locals) scope_builder.(name, locals)
end end
@ -54,7 +54,7 @@ module Dry
inflector: inflector, inflector: inflector,
context: context, context: context,
scope_builder: scope_builder, scope_builder: scope_builder,
part_builder: part_builder, part_builder: part_builder
) )
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/inflector' require "dry/inflector"
module Dry module Dry
class View class View
@ -8,7 +8,7 @@ module Dry
class RenderEnvironmentMissing class RenderEnvironmentMissing
class MissingEnvironmentError < StandardError class MissingEnvironmentError < StandardError
def message def message
'a +render_env+ must be provided' "a +render_env+ must be provided"
end end
end end
@ -20,19 +20,19 @@ module Dry
raise MissingEnvironmentError raise MissingEnvironmentError
end end
def part(name, value, **options) def part(_name, _value, **_options)
raise MissingEnvironmentError raise MissingEnvironmentError
end end
def scope(name = nil, locals) def scope(_name = nil, _locals) # rubocop:disable Style/OptionalArguments
raise MissingEnvironmentError raise MissingEnvironmentError
end end
def template(name, scope, &block) def template(_name, _scope)
raise MissingEnvironmentError raise MissingEnvironmentError
end end
def partial(name, scope, &block) def partial(_name, _scope)
raise MissingEnvironmentError raise MissingEnvironmentError
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/equalizer' require "dry/equalizer"
module Dry module Dry
class View class View

View File

@ -1,16 +1,16 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/core/cache' require "dry/core/cache"
require 'dry/equalizer' require "dry/equalizer"
require_relative 'errors' require_relative "errors"
require_relative 'tilt' require_relative "tilt"
module Dry module Dry
class View class View
# @api private # @api private
class Renderer class Renderer
PARTIAL_PREFIX = '_' PARTIAL_PREFIX = "_"
PATH_DELIMITER = '/' PATH_DELIMITER = "/"
extend Dry::Core::Cache extend Dry::Core::Cache

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/equalizer' require "dry/equalizer"
require 'dry/core/constants' require "dry/core/constants"
require_relative 'render_environment_missing' require_relative "render_environment_missing"
module Dry module Dry
class View class View
@ -59,7 +59,11 @@ module Dry
# @return [Scope] # @return [Scope]
# #
# @api public # @api public
def initialize(name: nil, locals: Dry::Core::Constants::EMPTY_HASH, render_env: RenderEnvironmentMissing.new) def initialize(
name: nil,
locals: Dry::Core::Constants::EMPTY_HASH,
render_env: RenderEnvironmentMissing.new
)
@_name = name @_name = name
@_locals = locals @_locals = locals
@_render_env = render_env @_render_env = render_env
@ -83,9 +87,14 @@ module Dry
# @api public # @api public
def render(partial_name = nil, **locals, &block) def render(partial_name = nil, **locals, &block)
partial_name ||= _name partial_name ||= _name
raise ArgumentError, '+partial_name+ must be provided for unnamed scopes' unless partial_name
partial_name = _inflector.underscore(_inflector.demodulize(partial_name.to_s)) if partial_name.is_a?(Class) unless partial_name
raise ArgumentError, "+partial_name+ must be provided for unnamed scopes"
end
if partial_name.is_a?(Class)
partial_name = _inflector.underscore(_inflector.demodulize(partial_name.to_s))
end
_render_env.partial(partial_name, _render_scope(locals), &block) _render_env.partial(partial_name, _render_scope(locals), &block)
end end
@ -153,7 +162,10 @@ module Dry
end end
def respond_to_missing?(name, include_private = false) def respond_to_missing?(name, include_private = false)
_locals.key?(name) || _render_env.context.respond_to?(name) || CONVENIENCE_METHODS.include?(name) || super _locals.key?(name) ||
_render_env.context.respond_to?(name) ||
CONVENIENCE_METHODS.include?(name) ||
super
end end
def _render_scope(**locals) def _render_scope(**locals)
@ -163,7 +175,7 @@ module Dry
self.class.new( self.class.new(
# FIXME: what about `name`? # FIXME: what about `name`?
locals: locals, locals: locals,
render_env: _render_env, render_env: _render_env
) )
end end
end end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/core/cache' require "dry/core/cache"
require 'dry/equalizer' require "dry/equalizer"
require_relative 'scope' require_relative "scope"
module Dry module Dry
class View class View
@ -46,11 +46,11 @@ module Dry
# @return [Dry::View::Scope] # @return [Dry::View::Scope]
# #
# @api private # @api private
def call(name = nil, locals) def call(name = nil, locals) # rubocop:disable Style/OptionalArguments
scope_class(name).new( scope_class(name).new(
name: name, name: name,
locals: locals, locals: locals,
render_env: render_env, render_env: render_env
) )
end end
@ -76,7 +76,7 @@ module Dry
# Give autoloaders a chance to act # Give autoloaders a chance to act
begin begin
klass = namespace.const_get(name) klass = namespace.const_get(name)
rescue NameError rescue NameError # rubocop:disable Lint/HandleExceptions
end end
if !klass && namespace.const_defined?(name, false) if !klass && namespace.const_defined?(name, false)

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/core/cache' require "dry/core/cache"
require 'tilt' require "tilt"
module Dry module Dry
class View class View
@ -11,7 +11,7 @@ module Dry
class << self class << self
def [](path, mapping, **options) def [](path, mapping, **options)
ext = File.extname(path).sub(%r{^.}, '').to_sym ext = File.extname(path).sub(/^./, "").to_sym
activate_adapter ext activate_adapter ext
with_mapping(mapping).new(path, **options) with_mapping(mapping).new(path, **options)
@ -74,5 +74,5 @@ module Dry
end end
end end
require_relative 'tilt/erb' require_relative "tilt/erb"
require_relative 'tilt/haml' require_relative "tilt/haml"

View File

@ -5,7 +5,7 @@ module Dry
module Tilt module Tilt
module ERB module ERB
def self.requirements def self.requirements
['dry/view/tilt/erbse', <<~ERROR] ["dry/view/tilt/erbse", <<~ERROR]
dry-view requires erbse for full compatibility when rendering .erb templates (e.g. implicitly capturing block content when yielding) dry-view requires erbse for full compatibility when rendering .erb templates (e.g. implicitly capturing block content when yielding)
To ignore this and use another engine for .erb templates, deregister this adapter before calling your views: To ignore this and use another engine for .erb templates, deregister this adapter before calling your views:
@ -15,7 +15,7 @@ module Dry
end end
def self.activate def self.activate
Tilt.default_mapping.register ErbseTemplate, 'erb' Tilt.default_mapping.register ErbseTemplate, "erb"
self self
end end
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'tilt/template' require "tilt/template"
require 'erbse' require "erbse"
module Dry module Dry
class View class View
@ -12,7 +12,7 @@ module Dry
@template = ::Erbse::Engine.new @template = ::Erbse::Engine.new
end end
def precompiled_template(locals) def precompiled_template(_locals)
@template.call(data) @template.call(data)
end end
end end

View File

@ -5,7 +5,7 @@ module Dry
module Tilt module Tilt
module Haml module Haml
def self.requirements def self.requirements
['hamlit/block', <<~ERROR] ["hamlit/block", <<~ERROR]
dry-view requires hamlit-block for full compatibility when rendering .haml templates (e.g. implicitly capturing block content when yielding) dry-view requires hamlit-block for full compatibility when rendering .haml templates (e.g. implicitly capturing block content when yielding)
To ignore this and use another engine for .haml templates, dereigster this adapter before calling your views: To ignore this and use another engine for .haml templates, dereigster this adapter before calling your views:

View File

@ -3,6 +3,6 @@
module Dry module Dry
class View class View
# @api private # @api private
VERSION = '0.7.0' VERSION = "0.7.0"
end end
end end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
require 'dry/view/part' require "dry/view/part"
RSpec.describe 'Context' do RSpec.describe "Context" do
it 'Provides decorated attributes for use in templates and parts' do it "Provides decorated attributes for use in templates and parts" do
module Test module Test
class Assets class Assets
def [](path) def [](path)
@ -34,15 +34,15 @@ RSpec.describe 'Context' do
class User < Dry::View::Part class User < Dry::View::Part
def image_tag def image_tag
value[:image_url] || context.assets.image_tag('default.png') value[:image_url] || context.assets.image_tag("default.png")
end end
end end
end end
end end
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/context') config.paths = FIXTURES_PATH.join("integration/context")
config.template = 'decorated_attributes' config.template = "decorated_attributes"
config.part_namespace = Test::Parts config.part_namespace = Test::Parts
expose :user expose :user
@ -51,11 +51,11 @@ RSpec.describe 'Context' do
context = Test::Context.new(assets: Test::Assets.new) context = Test::Context.new(assets: Test::Assets.new)
output = view.( output = view.(
user: { image_url: nil }, user: {image_url: nil},
context: context, context: context
).to_s ).to_s
expect(output.gsub("\n", '')).to eq <<~HTML.gsub("\n", '') expect(output.gsub("\n", "")).to eq <<~HTML.gsub("\n", "")
<img src="hashed/path/to/hello.png"> <img src="hashed/path/to/hello.png">
<div class="user"> <div class="user">
<img src="hashed/path/to/default.png"> <img src="hashed/path/to/default.png">

View File

@ -1,14 +1,14 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
require 'dry/view/part' require "dry/view/part"
RSpec.describe 'exposures' do RSpec.describe "exposures" do
let(:context) { let(:context) {
Class.new(Dry::View::Context) do Class.new(Dry::View::Context) do
def title def title
'dry-view rocks!' "dry-view rocks!"
end end
def assets def assets
@ -17,11 +17,11 @@ RSpec.describe 'exposures' do
end.new end.new
} }
it 'uses exposures with blocks to build view locals' do it "uses exposures with blocks to build view locals" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users do |users:| expose :users do |users:|
@ -32,8 +32,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, context: context).to_s).to eql( expect(view.(users: users, context: context).to_s).to eql(
@ -41,18 +41,18 @@ RSpec.describe 'exposures' do
) )
end end
it 'gives the exposure blocks access to the view instance' do it "gives the exposure blocks access to the view instance" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
attr_reader :prefix attr_reader :prefix
def initialize def initialize
super super
@prefix = 'My friend ' @prefix = "My friend "
end end
expose :users do |users:| expose :users do |users:|
@ -63,8 +63,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, context: context).to_s).to eql( expect(view.(users: users, context: context).to_s).to eql(
@ -72,11 +72,11 @@ RSpec.describe 'exposures' do
) )
end end
it 'supports instance methods as exposures' do it "supports instance methods as exposures" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users expose :users
@ -91,8 +91,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, context: context).to_s).to eql( expect(view.(users: users, context: context).to_s).to eql(
@ -100,19 +100,19 @@ RSpec.describe 'exposures' do
) )
end end
it 'passes matching input data if no proc or instance method is available' do it "passes matching input data if no proc or instance method is available" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users expose :users
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, context: context).to_s).to eql( expect(view.(users: users, context: context).to_s).to eql(
@ -120,14 +120,14 @@ RSpec.describe 'exposures' do
) )
end end
it 'using default values' do it "using default values" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users, default: [{ name: 'John', email: 'john@william.org' }] expose :users, default: [{name: "John", email: "john@william.org"}]
end.new end.new
expect(view.(context: context).to_s).to eql( expect(view.(context: context).to_s).to eql(
@ -135,26 +135,26 @@ RSpec.describe 'exposures' do
) )
end end
it 'having default values but passing nil as value for exposure' do it "having default values but passing nil as value for exposure" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'greeting' config.template = "greeting"
config.default_format = :html config.default_format = :html
expose :greeting, default: 'Hello Dry-rb' expose :greeting, default: "Hello Dry-rb"
end.new end.new
expect(view.(greeting: nil, context: context).to_s).to eql( expect(view.(greeting: nil, context: context).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p></p></body></html>' "<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p></p></body></html>"
) )
end end
it 'allows exposures to depend on each other' do it "allows exposures to depend on each other" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users_with_count' config.template = "users_with_count"
config.default_format = :html config.default_format = :html
expose :users expose :users
@ -165,8 +165,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, context: context).to_s).to eql( expect(view.(users: users, context: context).to_s).to eql(
@ -174,7 +174,7 @@ RSpec.describe 'exposures' do
) )
end end
it 'wraps exposures in view parts before they are supplied as dependencies' do it "wraps exposures in view parts before they are supplied as dependencies" do
module Test module Test
class UserPart < Dry::View::Part class UserPart < Dry::View::Part
def display_name def display_name
@ -184,9 +184,9 @@ RSpec.describe 'exposures' do
end end
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users_with_count' config.template = "users_with_count"
config.default_format = :html config.default_format = :html
expose :users, as: Test::UserPart expose :users, as: Test::UserPart
@ -201,8 +201,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
rendered = view.(users: users, context: context) rendered = view.(users: users, context: context)
@ -210,17 +210,17 @@ RSpec.describe 'exposures' do
expect(rendered[:users]).to be_a(Dry::View::Part) expect(rendered[:users]).to be_a(Dry::View::Part)
expect(rendered[:users][0]).to be_a(Test::UserPart) expect(rendered[:users][0]).to be_a(Test::UserPart)
expect(rendered[:users][0].value).to eq(name: 'Jane', email: 'jane@doe.org') expect(rendered[:users][0].value).to eq(name: "Jane", email: "jane@doe.org")
expect(rendered[:article]).to be_a(Dry::View::Part) expect(rendered[:article]).to be_a(Dry::View::Part)
expect(rendered[:article].to_s).to eq 'Great article from User: Jane' expect(rendered[:article].to_s).to eq "Great article from User: Jane"
end end
it 'allows exposures to depend on each other while still using keywords args to access input data' do it "allows exposures to depend on each other while still using keywords args to access input data" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'greeting' config.template = "greeting"
config.default_format = :html config.default_format = :html
expose :greeting do |prefix, greeting:| expose :greeting do |prefix, greeting:|
@ -228,41 +228,41 @@ RSpec.describe 'exposures' do
end end
expose :prefix do expose :prefix do
'Hello' "Hello"
end end
end.new end.new
expect(view.(greeting: 'From dry-view internals', context: context).to_s).to eql( expect(view.(greeting: "From dry-view internals", context: context).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>Hello From dry-view internals</p></body></html>' "<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>Hello From dry-view internals</p></body></html>"
) )
end end
it 'supports default values for keyword arguments' do it "supports default values for keyword arguments" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'greeting' config.template = "greeting"
config.default_format = :html config.default_format = :html
expose :greeting do |prefix, greeting: 'From the defaults'| expose :greeting do |prefix, greeting: "From the defaults"|
"#{prefix} #{greeting}" "#{prefix} #{greeting}"
end end
expose :prefix do expose :prefix do
'Hello' "Hello"
end end
end.new end.new
expect(view.(context: context).to_s).to eql( expect(view.(context: context).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>Hello From the defaults</p></body></html>' "<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><p>Hello From the defaults</p></body></html>"
) )
end end
it 'only passes keywords arguments that are needed in the block and allows for default values' do it "only passes keywords arguments that are needed in the block and allows for default values" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'edit' config.template = "edit"
config.default_format = :html config.default_format = :html
expose :pretty_id do |id:| expose :pretty_id do |id:|
@ -275,15 +275,15 @@ RSpec.describe 'exposures' do
end.new end.new
expect(view.(id: 1, context: context).to_s).to eql( expect(view.(id: 1, context: context).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><h1>Edit</h1><p>No Errors</p><p>Beautiful 1</p></body></html>' "<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><h1>Edit</h1><p>No Errors</p><p>Beautiful 1</p></body></html>"
) )
end end
it 'supports defining multiple exposures at once' do it "supports defining multiple exposures at once" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users_with_count' config.template = "users_with_count"
config.default_format = :html config.default_format = :html
expose :users, :users_count expose :users, :users_count
@ -296,8 +296,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, context: context).to_s).to eql( expect(view.(users: users, context: context).to_s).to eql(
@ -305,15 +305,15 @@ RSpec.describe 'exposures' do
) )
end end
it 'allows exposures to be hidden from the view' do it "allows exposures to be hidden from the view" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users_with_count' config.template = "users_with_count"
config.default_format = :html config.default_format = :html
private_expose :prefix do private_expose :prefix do
'COUNT: ' "COUNT: "
end end
expose :users expose :users
@ -324,11 +324,11 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
input = { users: users, context: context } input = {users: users, context: context}
expect(view.(input).to_s).to eql( expect(view.(input).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users</div></body></html>' '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users</div></body></html>'
@ -338,15 +338,15 @@ RSpec.describe 'exposures' do
expect(view.(input).locals).not_to include(:prefix) expect(view.(input).locals).not_to include(:prefix)
end end
it 'inherit exposures from parent class' do it "inherit exposures from parent class" do
parent = Class.new(Dry::View) do parent = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users_with_count_inherit' config.template = "users_with_count_inherit"
config.default_format = :html config.default_format = :html
private_expose :prefix do private_expose :prefix do
'COUNT: ' "COUNT: "
end end
expose :users expose :users
@ -358,16 +358,16 @@ RSpec.describe 'exposures' do
child = Class.new(parent) do child = Class.new(parent) do
expose :child_expose do expose :child_expose do
'Child expose' "Child expose"
end end
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
input = { users: users, context: context } input = {users: users, context: context}
expect(child.(input).to_s).to eql( expect(child.(input).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users</div><div class="inherit">Child expose</div></body></html>' '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users</div><div class="inherit">Child expose</div></body></html>'
@ -377,15 +377,15 @@ RSpec.describe 'exposures' do
expect(child.(input).locals).not_to include(:prefix) expect(child.(input).locals).not_to include(:prefix)
end end
it 'inherit exposures from parent class and allow to override them' do it "inherit exposures from parent class and allow to override them" do
parent = Class.new(Dry::View) do parent = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users_with_count_inherit' config.template = "users_with_count_inherit"
config.default_format = :html config.default_format = :html
private_expose :prefix do private_expose :prefix do
'COUNT: ' "COUNT: "
end end
expose :users expose :users
@ -397,7 +397,7 @@ RSpec.describe 'exposures' do
child = Class.new(parent) do child = Class.new(parent) do
expose :child_expose do expose :child_expose do
'Child expose' "Child expose"
end end
expose :users_count do |prefix, users:| expose :users_count do |prefix, users:|
@ -406,11 +406,11 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
input = { users: users, context: context } input = {users: users, context: context}
expect(child.(input).to_s).to eql( expect(child.(input).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users overrided</div><div class="inherit">Child expose</div></body></html>' '<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ul><li>Jane (jane@doe.org)</li><li>Joe (joe@doe.org)</li></ul><div class="count">COUNT: 2 users overrided</div><div class="inherit">Child expose</div></body></html>'
@ -420,11 +420,11 @@ RSpec.describe 'exposures' do
expect(child.(input).locals).not_to include(:prefix) expect(child.(input).locals).not_to include(:prefix)
end end
it 'makes exposures available to layout' do it "makes exposures available to layout" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app_with_users' config.layout = "app_with_users"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users_count, layout: true expose :users_count, layout: true
@ -433,8 +433,8 @@ RSpec.describe 'exposures' do
end.new end.new
users = [ users = [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
expect(view.(users: users, users_count: users.size, context: context).to_s).to eql( expect(view.(users: users, users_count: users.size, context: context).to_s).to eql(

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/core/inflector' require "dry/core/inflector"
require 'dry/view/scope_builder' require "dry/view/scope_builder"
RSpec.describe 'Part / Decorated attributes' do RSpec.describe "Part / Decorated attributes" do
let(:article_class) { let(:article_class) {
Class.new do Class.new do
attr_reader :title, :author, :comments attr_reader :title, :author, :comments
@ -37,16 +37,16 @@ RSpec.describe 'Part / Decorated attributes' do
end end
} }
let (:author) { let(:author) {
author_class.new(name: 'Jane Doe') author_class.new(name: "Jane Doe")
} }
let(:article) { let(:article) {
article_class.new( article_class.new(
title: 'Hello world', title: "Hello world",
author: author, author: author,
comments: [ comments: [
comment_class.new(author: author_class.new(name: 'Sue Smith'), body: 'Great article') comment_class.new(author: author_class.new(name: "Sue Smith"), body: "Great article")
] ]
) )
} }
@ -55,7 +55,7 @@ RSpec.describe 'Part / Decorated attributes' do
article_part_class.new( article_part_class.new(
name: :article, name: :article,
value: article, value: article,
render_env: render_env, render_env: render_env
) )
} }
@ -65,15 +65,15 @@ RSpec.describe 'Part / Decorated attributes' do
inflector: Dry::Inflector.new, inflector: Dry::Inflector.new,
context: Dry::View::Context.new, context: Dry::View::Context.new,
scope_builder: Dry::View::ScopeBuilder.new, scope_builder: Dry::View::ScopeBuilder.new,
part_builder: part_builder, part_builder: part_builder
) )
} }
describe 'using default part builder' do describe "using default part builder" do
let(:part_builder) { Dry::View::PartBuilder.new } let(:part_builder) { Dry::View::PartBuilder.new }
describe 'decorating without options' do describe "decorating without options" do
describe 'multiple declarations' do describe "multiple declarations" do
let(:article_part_class) { let(:article_part_class) {
Class.new(Dry::View::Part) do Class.new(Dry::View::Part) do
decorate :author decorate :author
@ -81,43 +81,43 @@ RSpec.describe 'Part / Decorated attributes' do
end end
} }
it 'decorates attributes with the standard Dry::View::Part class' do it "decorates attributes with the standard Dry::View::Part class" do
expect(article_part.author).to be_a Dry::View::Part expect(article_part.author).to be_a Dry::View::Part
expect(article_part.comments[0]).to be_a Dry::View::Part expect(article_part.comments[0]).to be_a Dry::View::Part
end end
context 'falsey values' do context "falsey values" do
let(:author) { nil } let(:author) { nil }
it 'does not decorate the attributes' do it "does not decorate the attributes" do
expect(article_part.author).to be_nil expect(article_part.author).to be_nil
end end
end end
end end
describe 'single declaration' do describe "single declaration" do
let(:article_part_class) { let(:article_part_class) {
Class.new(Dry::View::Part) do Class.new(Dry::View::Part) do
decorate :author, :comments decorate :author, :comments
end end
} }
it 'decorates attributes with the standard Dry::View::Part class' do it "decorates attributes with the standard Dry::View::Part class" do
expect(article_part.author).to be_a Dry::View::Part expect(article_part.author).to be_a Dry::View::Part
expect(article_part.comments[0]).to be_a Dry::View::Part expect(article_part.comments[0]).to be_a Dry::View::Part
end end
context 'falsey values' do context "falsey values" do
let(:author) { nil } let(:author) { nil }
it 'does not decorate the attributes' do it "does not decorate the attributes" do
expect(article_part.author).to be_nil expect(article_part.author).to be_nil
end end
end end
end end
end end
describe 'decorating with part class specified' do describe "decorating with part class specified" do
before do before do
module Test module Test
class AuthorPart < Dry::View::Part class AuthorPart < Dry::View::Part
@ -135,22 +135,22 @@ RSpec.describe 'Part / Decorated attributes' do
end end
} }
it 'deorates attributes with the specified part class' do it "deorates attributes with the specified part class" do
expect(article_part.author).to be_a Test::AuthorPart expect(article_part.author).to be_a Test::AuthorPart
expect(article_part.comments[0]).to be_a Test::CommentPart expect(article_part.comments[0]).to be_a Test::CommentPart
end end
context 'falsey values' do context "falsey values" do
let(:author) { nil } let(:author) { nil }
it 'does not decorate the attributes' do it "does not decorate the attributes" do
expect(article_part.author).to be_nil expect(article_part.author).to be_nil
end end
end end
end end
end end
describe 'using custom part builder' do describe "using custom part builder" do
let(:article_part_class) { let(:article_part_class) {
Class.new(Dry::View::Part) do Class.new(Dry::View::Part) do
decorate :author decorate :author
@ -183,16 +183,16 @@ RSpec.describe 'Part / Decorated attributes' do
end end
end end
it 'deorates attributes using the custom part builder' do it "deorates attributes using the custom part builder" do
expect(article_part.author).to be_a Test::AuthorPart expect(article_part.author).to be_a Test::AuthorPart
expect(article_part.comments[0]).to be_a Test::CommentPart expect(article_part.comments[0]).to be_a Test::CommentPart
expect(article_part.comments[0].author).to be_a Test::AuthorPart expect(article_part.comments[0].author).to be_a Test::AuthorPart
end end
context 'falsey values' do context "falsey values" do
let(:author) { nil } let(:author) { nil }
it 'does not decorate the attributes' do it "does not decorate the attributes" do
expect(article_part.author).to be_nil expect(article_part.author).to be_nil
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe 'part builder' do RSpec.describe "part builder" do
before do before do
module Test module Test
class Custom < Dry::View::Part class Custom < Dry::View::Part
@ -19,12 +19,12 @@ RSpec.describe 'part builder' do
end end
end end
describe 'default decorator' do describe "default decorator" do
it 'looks up classes from a part namespace' do it "looks up classes from a part namespace" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = nil config.layout = nil
config.template = 'decorated_parts' config.template = "decorated_parts"
config.part_namespace = Test config.part_namespace = Test
expose :customs expose :customs
@ -32,46 +32,46 @@ RSpec.describe 'part builder' do
expose :ordinary expose :ordinary
end.new end.new
expect(view.(customs: ['many things'], custom: 'custom thing', ordinary: 'ordinary thing').to_s).to eql( expect(view.(customs: ["many things"], custom: "custom thing", ordinary: "ordinary thing").to_s).to eql(
'<p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>' "<p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>"
) )
end end
it 'supports wrapping array memebers in custom part classes provided to exposure :as option' do it "supports wrapping array memebers in custom part classes provided to exposure :as option" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = nil config.layout = nil
config.template = 'decorated_parts' config.template = "decorated_parts"
expose :customs, as: Test::CustomPart expose :customs, as: Test::CustomPart
expose :custom, as: Test::CustomPart expose :custom, as: Test::CustomPart
expose :ordinary expose :ordinary
end.new end.new
expect(view.(customs: ['many things'], custom: 'custom thing', ordinary: 'ordinary thing').to_s).to eql( expect(view.(customs: ["many things"], custom: "custom thing", ordinary: "ordinary thing").to_s).to eql(
'<p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>' "<p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>"
) )
end end
it 'supports wrapping an array and its members in custom part classes provided to exposure :as option as a hash' do it "supports wrapping an array and its members in custom part classes provided to exposure :as option as a hash" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = nil config.layout = nil
config.template = 'decorated_parts' config.template = "decorated_parts"
expose :customs, as: [Test::CustomArrayPart, Test::CustomPart] expose :customs, as: [Test::CustomArrayPart, Test::CustomPart]
expose :custom, as: Test::CustomPart expose :custom, as: Test::CustomPart
expose :ordinary expose :ordinary
end.new end.new
expect(view.(customs: ['many things'], custom: 'custom thing', ordinary: 'ordinary thing').to_s).to eql( expect(view.(customs: ["many things"], custom: "custom thing", ordinary: "ordinary thing").to_s).to eql(
'<p>Custom part wrapping many things</p><p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>' "<p>Custom part wrapping many things</p><p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>"
) )
end end
end end
describe 'custom decorator and part classes' do describe "custom decorator and part classes" do
it 'supports wrapping in custom parts based on exposure names' do it "supports wrapping in custom parts based on exposure names" do
part_builder = Class.new(Dry::View::PartBuilder) do part_builder = Class.new(Dry::View::PartBuilder) do
def part_class(name:, **options) def part_class(name:, **options)
name == :custom ? Test::CustomPart : super name == :custom ? Test::CustomPart : super
@ -80,15 +80,15 @@ RSpec.describe 'part builder' do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.part_builder = part_builder config.part_builder = part_builder
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = nil config.layout = nil
config.template = 'decorated_parts' config.template = "decorated_parts"
expose :customs, :custom, :ordinary expose :customs, :custom, :ordinary
end.new end.new
expect(view.(customs: ['many things'], custom: 'custom thing', ordinary: 'ordinary thing').to_s).to eql( expect(view.(customs: ["many things"], custom: "custom thing", ordinary: "ordinary thing").to_s).to eql(
'<p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>' "<p>Custom part wrapping many things</p><p>Custom part wrapping custom thing</p><p>ordinary thing</p>"
) )
end end
end end

View File

@ -1,16 +1,16 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/part' require "dry/view/part"
require 'dry/view/scope' require "dry/view/scope"
RSpec.describe 'Scopes' do RSpec.describe "Scopes" do
let(:base_view) { let(:base_view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/scopes') config.paths = FIXTURES_PATH.join("integration/scopes")
end end
} }
specify 'Custom scope for a view' do specify "Custom scope for a view" do
module Test module Test
class ControllerScope < Dry::View::Scope class ControllerScope < Dry::View::Scope
def hello def hello
@ -20,110 +20,110 @@ RSpec.describe 'Scopes' do
end end
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'custom_view_scope' config.template = "custom_view_scope"
config.scope = Test::ControllerScope config.scope = Test::ControllerScope
expose :text expose :text
end.new end.new
expect(view.(text: 'world').to_s).to eq 'Hello world!' expect(view.(text: "world").to_s).to eq "Hello world!"
end end
specify 'Rendering a partial via an anonymous scope' do specify "Rendering a partial via an anonymous scope" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'anonymous_scope' config.template = "anonymous_scope"
expose :text expose :text
end.new end.new
expect(view.(text: 'Hello').to_s).to eq 'Greeting: Hello' expect(view.(text: "Hello").to_s).to eq "Greeting: Hello"
end end
specify 'Rendering a partial implicitly via a custom named scope' do specify "Rendering a partial implicitly via a custom named scope" do
module Test::Scopes module Test::Scopes
class Greeting < Dry::View::Scope class Greeting < Dry::View::Scope
def greeting def greeting
_locals[:greeting].upcase + '!' _locals[:greeting].upcase + "!"
end end
end end
end end
view = Class.new(base_view) do view = Class.new(base_view) do
config.scope_namespace = Test::Scopes config.scope_namespace = Test::Scopes
config.template = 'named_scope_with_implicit_render' config.template = "named_scope_with_implicit_render"
expose :text expose :text
end.new end.new
expect(view.(text: 'Hello').to_s).to eq 'Greeting: HELLO!' expect(view.(text: "Hello").to_s).to eq "Greeting: HELLO!"
end end
specify 'Rendering a partial implicitly via a custom named scope (provided via a class)' do specify "Rendering a partial implicitly via a custom named scope (provided via a class)" do
module Test::Scopes module Test::Scopes
class Greeting < Dry::View::Scope class Greeting < Dry::View::Scope
def greeting def greeting
_locals[:greeting].upcase + '!' _locals[:greeting].upcase + "!"
end end
end end
end end
view = Class.new(base_view) do view = Class.new(base_view) do
config.scope_namespace = Test::Scopes config.scope_namespace = Test::Scopes
config.template = 'class_named_scope_with_implicit_render' config.template = "class_named_scope_with_implicit_render"
expose :text expose :text
end.new end.new
expect(view.(text: 'Hello').to_s).to eq 'Greeting: HELLO!' expect(view.(text: "Hello").to_s).to eq "Greeting: HELLO!"
end end
specify 'Raising an error when an unnamed partial cannot be rendered implicitly' do specify "Raising an error when an unnamed partial cannot be rendered implicitly" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'unnamed_named_scope_with_implicit_render' config.template = "unnamed_named_scope_with_implicit_render"
end.new end.new
expect { view.().to_s }.to raise_error ArgumentError, '+partial_name+ must be provided for unnamed scopes' expect { view.().to_s }.to raise_error ArgumentError, "+partial_name+ must be provided for unnamed scopes"
end end
specify 'Rendering a partial explicitly via a custom named scope' do specify "Rendering a partial explicitly via a custom named scope" do
module Test::Scopes module Test::Scopes
class Greeting < Dry::View::Scope class Greeting < Dry::View::Scope
def greeting def greeting
_locals[:greeting].upcase + '!' _locals[:greeting].upcase + "!"
end end
end end
end end
view = Class.new(base_view) do view = Class.new(base_view) do
config.scope_namespace = Test::Scopes config.scope_namespace = Test::Scopes
config.template = 'named_scope_with_explicit_render' config.template = "named_scope_with_explicit_render"
expose :text expose :text
end.new end.new
expect(view.(text: 'Hello').to_s).to eq 'Holler: HELLO!' expect(view.(text: "Hello").to_s).to eq "Holler: HELLO!"
end end
specify 'Custom named scope providing defaults for missing locals' do specify "Custom named scope providing defaults for missing locals" do
module Test::Scopes module Test::Scopes
class Greeting < Dry::View::Scope class Greeting < Dry::View::Scope
def greeting def greeting
_locals.fetch(:greeting) { 'Howdy' } _locals.fetch(:greeting) { "Howdy" }
end end
end end
end end
view = Class.new(base_view) do view = Class.new(base_view) do
config.scope_namespace = Test::Scopes config.scope_namespace = Test::Scopes
config.template = 'named_scope_with_defaults' config.template = "named_scope_with_defaults"
expose :text expose :text
end.new end.new
expect(view.().to_s).to eq 'Greeting: Howdy' expect(view.().to_s).to eq "Greeting: Howdy"
end end
specify 'Creating a custom scope from a view part' do specify "Creating a custom scope from a view part" do
module Test::Parts module Test::Parts
class Message < Dry::View::Part class Message < Dry::View::Part
def greeting def greeting
@ -135,7 +135,7 @@ RSpec.describe 'Scopes' do
module Test::Scopes module Test::Scopes
class Greeting < Dry::View::Scope class Greeting < Dry::View::Scope
def greeting def greeting
_locals[:greeting] + '!' _locals[:greeting] + "!"
end end
end end
end end
@ -143,11 +143,11 @@ RSpec.describe 'Scopes' do
view = Class.new(base_view) do view = Class.new(base_view) do
config.part_namespace = Test::Parts config.part_namespace = Test::Parts
config.scope_namespace = Test::Scopes config.scope_namespace = Test::Scopes
config.template = 'scope_from_part' config.template = "scope_from_part"
expose :message expose :message
end.new end.new
expect(view.(message: { text: 'Hello from a part' }).to_s).to eq 'Greeting: Hello from a part!' expect(view.(message: {text: "Hello from a part"}).to_s).to eq "Greeting: Hello from a part!"
end end
end end

View File

@ -1,25 +1,25 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
RSpec.describe 'Template engines / erb (using erbse as default engine)' do RSpec.describe "Template engines / erb (using erbse as default engine)" do
let(:base_view) { let(:base_view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/template_engines/erbse') config.paths = FIXTURES_PATH.join("integration/template_engines/erbse")
end end
} }
context 'with erbse available' do context "with erbse available" do
it 'supports partials that yield' do it "supports partials that yield" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'render_and_yield' config.template = "render_and_yield"
end.new end.new
expect(view.().to_s.gsub(/\n\s*/m, '')).to eq '<wrapper> Yielded</wrapper>' expect(view.().to_s.gsub(/\n\s*/m, "")).to eq "<wrapper> Yielded</wrapper>"
end end
it 'supports context methods that yield' do it "supports context methods that yield" do
context = Class.new(Dry::View::Context) do context = Class.new(Dry::View::Context) do
def wrapper def wrapper
"<wrapper>#{yield}</wrapper>" "<wrapper>#{yield}</wrapper>"
@ -28,19 +28,19 @@ RSpec.describe 'Template engines / erb (using erbse as default engine)' do
view = Class.new(base_view) do view = Class.new(base_view) do
config.default_context = context config.default_context = context
config.template = 'method_with_yield' config.template = "method_with_yield"
end.new end.new
expect(view.().to_s.gsub(/\n\s*/m, '')).to eq '<wrapper> Yielded</wrapper>' expect(view.().to_s.gsub(/\n\s*/m, "")).to eq "<wrapper> Yielded</wrapper>"
end end
end end
context 'with erbse not available' do context "with erbse not available" do
before do before do
@load_path = $LOAD_PATH.dup @load_path = $LOAD_PATH.dup
@loaded_features = $LOADED_FEATURES.dup @loaded_features = $LOADED_FEATURES.dup
$LOAD_PATH.reject! { |path| path =~ %r{erbse} } $LOAD_PATH.reject! { |path| path =~ /erbse/ }
$LOADED_FEATURES.reject! { |path| path =~ %r{erbse|dry/view/tilt/erbse} } $LOADED_FEATURES.reject! { |path| path =~ %r{erbse|dry/view/tilt/erbse} }
Dry::View::Tilt.cache.clear Dry::View::Tilt.cache.clear
@ -53,23 +53,23 @@ RSpec.describe 'Template engines / erb (using erbse as default engine)' do
Dry::View::Tilt.register_adapter :erb, Dry::View::Tilt::ERB Dry::View::Tilt.register_adapter :erb, Dry::View::Tilt::ERB
end end
it 'raises an error explaining the erbse requirement' do it "raises an error explaining the erbse requirement" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'render_and_yield' config.template = "render_and_yield"
end.new end.new
expect { view.() }.to raise_error(LoadError, %r{dry-view requires erbse}m) expect { view.() }.to raise_error(LoadError, /dry-view requires erbse/m)
end end
it 'allows deregistering the adapter to avoid the load error and accept rendering via a less-compatible erb engine' do it "allows deregistering the adapter to avoid the load error and accept rendering via a less-compatible erb engine" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'plain_erb' config.template = "plain_erb"
end.new end.new
Dry::View::Tilt.deregister_adapter :erb Dry::View::Tilt.deregister_adapter :erb
expect { view.() }.not_to raise_error expect { view.() }.not_to raise_error
expect(view.().to_s.strip).to eq 'Hello' expect(view.().to_s.strip).to eq "Hello"
end end
end end
end end

View File

@ -1,30 +1,30 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'erubi' require "erubi"
require 'erubi/capture_end' require "erubi/capture_end"
require 'tilt/erubi' require "tilt/erubi"
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
RSpec.describe 'Template engines / erb (using erubi via an explict engine mapping)' do RSpec.describe "Template engines / erb (using erubi via an explict engine mapping)" do
let(:base_view) { let(:base_view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/template_engines/erubi') config.paths = FIXTURES_PATH.join("integration/template_engines/erubi")
config.renderer_engine_mapping = { erb: Tilt::ErubiTemplate } config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}
config.renderer_options = { engine_class: Erubi::CaptureEndEngine } config.renderer_options = {engine_class: Erubi::CaptureEndEngine}
end end
} }
it 'supports partials that yield' do it "supports partials that yield" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'render_and_yield' config.template = "render_and_yield"
end.new end.new
expect(view.().to_s.gsub(/\n\s*/m, '')).to eq '<wrapper>Yielded</wrapper>' expect(view.().to_s.gsub(/\n\s*/m, "")).to eq "<wrapper>Yielded</wrapper>"
end end
it 'supports context methods that yield' do it "supports context methods that yield" do
context = Class.new(Dry::View::Context) do context = Class.new(Dry::View::Context) do
def wrapper def wrapper
"<wrapper>#{yield}</wrapper>" "<wrapper>#{yield}</wrapper>"
@ -33,9 +33,9 @@ RSpec.describe 'Template engines / erb (using erubi via an explict engine mappin
view = Class.new(base_view) do view = Class.new(base_view) do
config.default_context = context config.default_context = context
config.template = 'method_with_yield' config.template = "method_with_yield"
end.new end.new
expect(view.().to_s.gsub(/\n\s*/m, '')).to eq '<wrapper>Yielded</wrapper>' expect(view.().to_s.gsub(/\n\s*/m, "")).to eq "<wrapper>Yielded</wrapper>"
end end
end end

View File

@ -1,41 +1,41 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
RSpec.describe 'Template engines / haml (using hamlit-block as default engine)' do RSpec.describe "Template engines / haml (using hamlit-block as default engine)" do
let(:base_view) { let(:base_view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/template_engines/hamlit') config.paths = FIXTURES_PATH.join("integration/template_engines/hamlit")
end end
} }
context 'with hamlit-block available' do context "with hamlit-block available" do
it 'supports partials that yield' do it "supports partials that yield" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'render_and_yield' config.template = "render_and_yield"
end.new end.new
expect(view.().to_s.gsub(/\n\s*/m, '')).to eq '<wrapper>Yielded</wrapper>' expect(view.().to_s.gsub(/\n\s*/m, "")).to eq "<wrapper>Yielded</wrapper>"
end end
it 'supports methods that yield' do it "supports methods that yield" do
context = Class.new(Dry::View::Context) do context = Class.new(Dry::View::Context) do
def wrapper(&block) def wrapper
"<wrapper>#{yield}</wrapper>" "<wrapper>#{yield}</wrapper>"
end end
end.new end.new
view = Class.new(base_view) do view = Class.new(base_view) do
config.default_context = context config.default_context = context
config.template = 'method_with_yield' config.template = "method_with_yield"
end.new end.new
expect(view.().to_s.gsub(/\n\s*/m, '')).to eq '<wrapper>Yielded</wrapper>' expect(view.().to_s.gsub(/\n\s*/m, "")).to eq "<wrapper>Yielded</wrapper>"
end end
end end
context 'with hamlit-block not available' do context "with hamlit-block not available" do
before do before do
@load_path = $LOAD_PATH.dup @load_path = $LOAD_PATH.dup
@loaded_features = $LOADED_FEATURES.dup @loaded_features = $LOADED_FEATURES.dup
@ -52,12 +52,12 @@ RSpec.describe 'Template engines / haml (using hamlit-block as default engine)'
$LOADED_FEATURES.replace @loaded_features $LOADED_FEATURES.replace @loaded_features
end end
it 'raises an error explaining the hamlit-block requirement' do it "raises an error explaining the hamlit-block requirement" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'render_and_yield' config.template = "render_and_yield"
end.new end.new
expect { view.() }.to raise_error(LoadError, %r{dry-view requires hamlit-block}m) expect { view.() }.to raise_error(LoadError, /dry-view requires hamlit-block/m)
end end
end end
end end

View File

@ -1,25 +1,25 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'slim' require "slim"
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
RSpec.describe 'Template engines / slim' do RSpec.describe "Template engines / slim" do
let(:base_view) { let(:base_view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/template_engines/slim') config.paths = FIXTURES_PATH.join("integration/template_engines/slim")
end end
} }
it 'supports partials that yield' do it "supports partials that yield" do
view = Class.new(base_view) do view = Class.new(base_view) do
config.template = 'render_and_yield' config.template = "render_and_yield"
end.new end.new
expect(view.().to_s).to eq '<wrapper>Yielded</wrapper>' expect(view.().to_s).to eq "<wrapper>Yielded</wrapper>"
end end
it 'supports context methods that yield' do it "supports context methods that yield" do
context = Class.new(Dry::View::Context) do context = Class.new(Dry::View::Context) do
def wrapper def wrapper
"<wrapper>#{yield}</wrapper>" "<wrapper>#{yield}</wrapper>"
@ -28,9 +28,9 @@ RSpec.describe 'Template engines / slim' do
view = Class.new(base_view) do view = Class.new(base_view) do
config.default_context = context config.default_context = context
config.template = 'method_with_yield' config.template = "method_with_yield"
end.new end.new
expect(view.().to_s).to eq '<wrapper>Yielded</wrapper>' expect(view.().to_s).to eq "<wrapper>Yielded</wrapper>"
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
RSpec.describe 'Testing / parts' do RSpec.describe "Testing / parts" do
let(:part_class) { let(:part_class) {
Class.new(Dry::View::Part) do Class.new(Dry::View::Part) do
end end
@ -9,21 +9,21 @@ RSpec.describe 'Testing / parts' do
specify "Parts can be unit tested without name or rendering (for testing methods that don't require them)" do specify "Parts can be unit tested without name or rendering (for testing methods that don't require them)" do
part_class = Class.new(Dry::View::Part) do part_class = Class.new(Dry::View::Part) do
def breaking_news_title def breaking_news_title
title + '!' title + "!"
end end
end end
article = Struct.new(:title).new('Giant Hand Threatens Beach City') article = Struct.new(:title).new("Giant Hand Threatens Beach City")
article_part = part_class.new(value: article) article_part = part_class.new(value: article)
expect(article_part.breaking_news_title).to eq 'Giant Hand Threatens Beach City!' expect(article_part.breaking_news_title).to eq "Giant Hand Threatens Beach City!"
end end
specify 'Parts can be unit tested with a rendering from a view class' do specify "Parts can be unit tested with a rendering from a view class" do
view_class = Class.new(Dry::View) do view_class = Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/testing') config.paths = FIXTURES_PATH.join("integration/testing")
config.template = 'view' config.template = "view"
end end
part_class = Class.new(Dry::View::Part) do part_class = Class.new(Dry::View::Part) do
@ -32,12 +32,12 @@ RSpec.describe 'Testing / parts' do
end end
end end
article = Struct.new(:title).new('A Guide to Beach City Funland') article = Struct.new(:title).new("A Guide to Beach City Funland")
article_part = part_class.new( article_part = part_class.new(
name: :article, name: :article,
value: article, value: article,
render_env: view_class.template_env, render_env: view_class.template_env
) )
expect(article_part.feature_box).to eq %( expect(article_part.feature_box).to eq %(

View File

@ -1,22 +1,22 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
RSpec.describe 'View / errors' do RSpec.describe "View / errors" do
specify 'Raising an error when paths are not configured' do specify "Raising an error when paths are not configured" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.template = 'hello' config.template = "hello"
end.new end.new
expect { view.() }.to raise_error(Dry::View::UndefinedConfigError, 'no +paths+ configured') expect { view.() }.to raise_error(Dry::View::UndefinedConfigError, "no +paths+ configured")
end end
specify 'Raising an error when template is not configured' do specify "Raising an error when template is not configured" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = FIXTURES_PATH.join('integration/errors') config.paths = FIXTURES_PATH.join("integration/errors")
end.new end.new
expect { view.() }.to raise_error(Dry::View::UndefinedConfigError, 'no +template+ configured') expect { view.() }.to raise_error(Dry::View::UndefinedConfigError, "no +template+ configured")
end end
end end

View File

@ -1,13 +1,13 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/context' require "dry/view/context"
RSpec.describe 'View / exposures' do RSpec.describe "View / exposures" do
specify 'exposures have access to context' do specify "exposures have access to context" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.template = 'greeting' config.template = "greeting"
expose :greeting do |greeting:, context:| expose :greeting do |greeting:, context:|
"#{greeting}, #{context.name}" "#{greeting}, #{context.name}"
@ -16,12 +16,12 @@ RSpec.describe 'View / exposures' do
context = Class.new(Dry::View::Context) do context = Class.new(Dry::View::Context) do
def name def name
'Jane' "Jane"
end end
end.new end.new
local = view.(greeting: 'Hello', context: context).locals[:greeting] local = view.(greeting: "Hello", context: context).locals[:greeting]
expect(local.value).to eq 'Hello, Jane' expect(local.value).to eq "Hello, Jane"
end end
end end

View File

@ -1,32 +1,32 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view' require "dry/view"
require 'dry/view/part' require "dry/view/part"
RSpec.describe 'View / locals' do RSpec.describe "View / locals" do
specify 'locals are decorated with parts by default' do specify "locals are decorated with parts by default" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.template = 'greeting' config.template = "greeting"
expose :greeting expose :greeting
end.new end.new
local = view.(greeting: 'Hello').locals[:greeting] local = view.(greeting: "Hello").locals[:greeting]
expect(local).to be_a(Dry::View::Part) expect(local).to be_a(Dry::View::Part)
end end
specify 'locals are not decorated if their exposure is marked as `decorate: false`' do specify "locals are not decorated if their exposure is marked as `decorate: false`" do
view = Class.new(Dry::View) do view = Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.template = 'greeting' config.template = "greeting"
expose :greeting, decorate: false expose :greeting, decorate: false
end.new end.new
local = view.(greeting: 'Hello').locals[:greeting] local = view.(greeting: "Hello").locals[:greeting]
expect(local).to eq 'Hello' expect(local).to eq "Hello"
end end
end end

View File

@ -1,19 +1,17 @@
# frozen_string_literal: true # frozen_string_literal: true
# coding: utf-8 RSpec.describe "dry-view" do
RSpec.describe 'dry-view' do
let(:view_class) do let(:view_class) do
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'users' config.template = "users"
config.default_format = :html config.default_format = :html
expose :users do expose :users do
[ [
{ name: 'Jane', email: 'jane@doe.org' }, {name: "Jane", email: "jane@doe.org"},
{ name: 'Joe', email: 'joe@doe.org' } {name: "Joe", email: "joe@doe.org"}
] ]
end end
end end
@ -22,7 +20,7 @@ RSpec.describe 'dry-view' do
let(:context) { let(:context) {
Class.new(Dry::View::Context) do Class.new(Dry::View::Context) do
def title def title
'dry-view rocks!' "dry-view rocks!"
end end
def assets def assets
@ -31,7 +29,7 @@ RSpec.describe 'dry-view' do
end.new end.new
} }
it 'renders within a layout and makes the provided context available everywhere' do it "renders within a layout and makes the provided context available everywhere" do
view = view_class.new view = view_class.new
expect(view.(context: context).to_s).to eql( expect(view.(context: context).to_s).to eql(
@ -39,7 +37,7 @@ RSpec.describe 'dry-view' do
) )
end end
it 'renders without a layout' do it "renders without a layout" do
view = Class.new(view_class) do view = Class.new(view_class) do
config.layout = false config.layout = false
end.new end.new
@ -49,18 +47,18 @@ RSpec.describe 'dry-view' do
) )
end end
it 'renders a view with an alternative format and engine' do it "renders a view with an alternative format and engine" do
view = view_class.new view = view_class.new
# FIXME: there should be a "\n\n" before "* Jane", but this is missing due to https://github.com/apotonick/erbse/issues/10 # FIXME: there should be a "\n\n" before "* Jane", but this is missing due to https://github.com/apotonick/erbse/issues/10
expect(view.(context: context, format: 'txt').to_s.strip).to eql( expect(view.(context: context, format: "txt").to_s.strip).to eql(
"# dry-view rocks!* Jane (jane@doe.org)\n* Joe (joe@doe.org)" "# dry-view rocks!* Jane (jane@doe.org)\n* Joe (joe@doe.org)"
) )
end end
it 'renders a view with a template on another view path' do it "renders a view with a template on another view path" do
view = Class.new(view_class) do view = Class.new(view_class) do
config.paths = [SPEC_ROOT.join('fixtures/templates_override')] + Array(config.paths) config.paths = [SPEC_ROOT.join("fixtures/templates_override")] + Array(config.paths)
end.new end.new
expect(view.(context: context).to_s).to eq( expect(view.(context: context).to_s).to eq(
@ -68,9 +66,9 @@ RSpec.describe 'dry-view' do
) )
end end
it 'renders a view that passes arguments to partials' do it "renders a view that passes arguments to partials" do
view = Class.new(view_class) do view = Class.new(view_class) do
config.template = 'parts_with_args' config.template = "parts_with_args"
end.new end.new
expect(view.(context: context).to_s).to eq( expect(view.(context: context).to_s).to eq(
@ -78,47 +76,47 @@ RSpec.describe 'dry-view' do
) )
end end
it 'renders using utf-8 by default' do it "renders using utf-8 by default" do
view = Class.new(view_class) do view = Class.new(view_class) do
config.template = 'utf8' config.template = "utf8"
end.new end.new
expect(view.(context: context).to_s).to eq( expect(view.(context: context).to_s).to eq(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body>ç</body></html>' "<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body>ç</body></html>"
) )
end end
describe 'inheritance' do describe "inheritance" do
let(:parent_view) do let(:parent_view) do
klass = Class.new(Dry::View) klass = Class.new(Dry::View)
klass.setting :paths, SPEC_ROOT.join('fixtures/templates') klass.setting :paths, SPEC_ROOT.join("fixtures/templates")
klass.setting :layout, 'app' klass.setting :layout, "app"
klass.setting :formats, { html: :slim } klass.setting :formats, html: :slim
klass klass
end end
let(:child_view) do let(:child_view) do
Class.new(parent_view) do Class.new(parent_view) do
config.template = 'tasks' config.template = "tasks"
end end
end end
it 'renders within a parent class layout using provided context' do it "renders within a parent class layout using provided context" do
view = Class.new(view_class) do view = Class.new(view_class) do
config.template = 'tasks' config.template = "tasks"
expose :tasks do expose :tasks do
[ [
{ title: 'one' }, {title: "one"},
{ title: 'two' }, {title: "two"}
] ]
end end
end.new end.new
expect(view.(context: context).to_s).to eql( expect(view.(context: context).to_s).to eql(
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ol><li>one</li><li>two</li></ol></body></html>' "<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ol><li>one</li><li>two</li></ol></body></html>"
) )
end end
end end

View File

@ -1,15 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
require_relative 'support/coverage' require_relative "support/coverage"
begin begin
require 'pry-byebug' require "pry-byebug"
rescue LoadError; end rescue LoadError; end
SPEC_ROOT = Pathname(__FILE__).dirname SPEC_ROOT = Pathname(__FILE__).dirname
FIXTURES_PATH = SPEC_ROOT.join('fixtures') FIXTURES_PATH = SPEC_ROOT.join("fixtures")
require 'slim' require "slim"
require 'dry/view' require "dry/view"
module Test module Test
def self.remove_constants def self.remove_constants
@ -34,7 +34,7 @@ RSpec.configure do |config|
Dry::View::Path, Dry::View::Path,
Dry::View::Renderer, Dry::View::Renderer,
Dry::View::ScopeBuilder, Dry::View::ScopeBuilder,
Dry::View::Tilt, Dry::View::Tilt
].each do |klass| ].each do |klass|
klass.cache.clear klass.cache.clear
end end

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/context' require "dry/view/context"
require 'dry/view/part' require "dry/view/part"
require 'dry/view/part_builder' require "dry/view/part_builder"
require 'dry/view/scope_builder' require "dry/view/scope_builder"
RSpec.describe Dry::View::Context do RSpec.describe Dry::View::Context do
let(:context_class) { let(:context_class) {
@ -30,38 +30,38 @@ RSpec.describe Dry::View::Context do
renderer: double(:renderer), renderer: double(:renderer),
context: Dry::View::Context.new, context: Dry::View::Context.new,
part_builder: Dry::View::PartBuilder.new, part_builder: Dry::View::PartBuilder.new,
scope_builder: Dry::View::ScopeBuilder.new, scope_builder: Dry::View::ScopeBuilder.new
) )
} }
subject(:context) { context_class.new(assets: assets, routes: routes) } subject(:context) { context_class.new(assets: assets, routes: routes) }
describe 'attribute readers' do describe "attribute readers" do
it 'provides access to its attributes' do it "provides access to its attributes" do
expect(context.assets).to eql assets expect(context.assets).to eql assets
end end
end end
context 'with render environment' do context "with render environment" do
subject(:context) { subject(:context) {
context_class.new(assets: assets, routes: routes).for_render_env(render_env) context_class.new(assets: assets, routes: routes).for_render_env(render_env)
} }
describe 'attribute readers' do describe "attribute readers" do
it 'provides attributes decorated in view parts' do it "provides attributes decorated in view parts" do
expect(context.assets).to be_a Dry::View::Part expect(context.assets).to be_a Dry::View::Part
expect(context.assets.value).to eql assets expect(context.assets.value).to eql assets
end end
end end
end end
describe '#with' do describe "#with" do
it 'returns a copy of the context with extra options' do it "returns a copy of the context with extra options" do
another_option = double(:another_option) another_option = double(:another_option)
new_context = context.with(another_option: another_option) new_context = context.with(another_option: another_option)
expect(new_context).to be_a(context.class) expect(new_context).to be_a(context.class)
expect(new_context._options).to eq({ assets: context.assets, routes: routes, another_option: another_option }) expect(new_context._options).to eq(assets: context.assets, routes: routes, another_option: another_option)
end end
end end
end end

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/decorated_attributes' require "dry/view/decorated_attributes"
RSpec.describe Dry::View::DecoratedAttributes do RSpec.describe Dry::View::DecoratedAttributes do
subject(:decoratable) { subject(:decoratable) {
@ -19,38 +19,38 @@ RSpec.describe Dry::View::DecoratedAttributes do
let(:attr_2) { double(:attr_2) } let(:attr_2) { double(:attr_2) }
let(:render_env) { spy(:render_env) } let(:render_env) { spy(:render_env) }
context 'with render environment' do context "with render environment" do
it 'returns decorated attributes as parts' do it "returns decorated attributes as parts" do
decoratable.attr_1 decoratable.attr_1
expect(render_env).to have_received(:part).with(:attr_1, attr_1, as: :my_value) expect(render_env).to have_received(:part).with(:attr_1, attr_1, as: :my_value)
decoratable.attr_2 decoratable.attr_2
if RUBY_VERSION >= '2.7' if RUBY_VERSION >= "2.7"
expect(render_env).to have_received(:part).with(:attr_2, attr_2) expect(render_env).to have_received(:part).with(:attr_2, attr_2)
else else
expect(render_env).to have_received(:part).with(:attr_2, attr_2, {}) expect(render_env).to have_received(:part).with(:attr_2, attr_2, {})
end end
end end
it 'raises NoMethodError when an invalid attribute is accessed' do it "raises NoMethodError when an invalid attribute is accessed" do
expect { decoratable.invalid_attr }.to raise_error(NoMethodError) expect { decoratable.invalid_attr }.to raise_error(NoMethodError)
end end
end end
context 'without render environment' do context "without render environment" do
let(:render_env) { nil } let(:render_env) { nil }
it 'returns attributes without decoration' do it "returns attributes without decoration" do
expect(decoratable.attr_1).to be attr_1 expect(decoratable.attr_1).to be attr_1
end end
it 'raises NoMethodError when an invalid attribute is accessed' do it "raises NoMethodError when an invalid attribute is accessed" do
expect { decoratable.invalid_attr }.to raise_error(NoMethodError) expect { decoratable.invalid_attr }.to raise_error(NoMethodError)
end end
end end
it 'prepends a single module to provide the decorated attribute readers' do it "prepends a single module to provide the decorated attribute readers" do
expect(decoratable.class.ancestors.map(&:name).grep(/Test::Decoratable::DecoratedAttributes/).length).to eq 1 expect(decoratable.class.ancestors.map(&:name).grep(/Test::Decoratable::DecoratedAttributes/).length).to eq 1
expect(decoratable.class.ancestors[0].inspect).to eq '#<Dry::View::DecoratedAttributes::Attributes[:attr_1, :attr_2, :invalid_attr]>' expect(decoratable.class.ancestors[0].inspect).to eq "#<Dry::View::DecoratedAttributes::Attributes[:attr_1, :attr_2, :invalid_attr]>"
end end
end end

View File

@ -3,94 +3,94 @@
RSpec.describe Dry::View::Exposure do RSpec.describe Dry::View::Exposure do
subject(:exposure) { described_class.new(:hello, proc, object, **options) } subject(:exposure) { described_class.new(:hello, proc, object, **options) }
let(:proc) { -> input { 'hi' } } let(:proc) { -> _input { "hi" } }
let(:object) { nil } let(:object) { nil }
let(:options) { {} } let(:options) { {} }
describe 'initialization and attributes' do describe "initialization and attributes" do
describe '#name' do describe "#name" do
it 'accepts a name' do it "accepts a name" do
expect(exposure.name).to eql :hello expect(exposure.name).to eql :hello
end end
end end
describe '#proc' do describe "#proc" do
it 'accepts a proc' do it "accepts a proc" do
expect(exposure.proc).to eql proc expect(exposure.proc).to eql proc
end end
it 'allows a nil proc' do it "allows a nil proc" do
expect(described_class.new(:hello).proc).to be_nil expect(described_class.new(:hello).proc).to be_nil
end end
end end
describe '#object' do describe "#object" do
let(:object) { Object.new } let(:object) { Object.new }
it 'accepts an object' do it "accepts an object" do
expect(exposure.object).to eq object expect(exposure.object).to eq object
end end
it 'allows a nil object' do it "allows a nil object" do
expect(described_class.new(:hello).object).to be_nil expect(described_class.new(:hello).object).to be_nil
end end
end end
describe '#decorate?' do describe "#decorate?" do
it 'is true by default' do it "is true by default" do
expect(exposure.decorate?).to eq true expect(exposure.decorate?).to eq true
end end
it 'can be set on initialization' do it "can be set on initialization" do
expect(described_class.new(:hello, decorate: false).decorate?).to eq false expect(described_class.new(:hello, decorate: false).decorate?).to eq false
end end
end end
describe '#private?' do describe "#private?" do
it 'is false by default' do it "is false by default" do
expect(exposure).not_to be_private expect(exposure).not_to be_private
end end
it 'can be set on initialization' do it "can be set on initialization" do
expect(described_class.new(:hello, private: true)).to be_private expect(described_class.new(:hello, private: true)).to be_private
end end
end end
describe '#default_value' do describe "#default_value" do
it 'is nil by default' do it "is nil by default" do
expect(exposure.default_value).to be_nil expect(exposure.default_value).to be_nil
end end
it 'can be set on initialization' do it "can be set on initialization" do
exposuse = described_class.new(:hello, default: 'Hi !') exposuse = described_class.new(:hello, default: "Hi !")
expect(exposuse.default_value).to eq('Hi !') expect(exposuse.default_value).to eq("Hi !")
end end
end end
end end
describe '#bind' do describe "#bind" do
subject(:bound_exposure) { exposure.bind(bind_object) } subject(:bound_exposure) { exposure.bind(bind_object) }
let(:bind_object) { Object.new } let(:bind_object) { Object.new }
it 'returns a new object' do it "returns a new object" do
expect(bound_exposure).not_to eql exposure expect(bound_exposure).not_to eql exposure
end end
it 'retains the bind object' do it "retains the bind object" do
expect(bound_exposure.object).to eq bind_object expect(bound_exposure.object).to eq bind_object
end end
context 'proc is set' do context "proc is set" do
it 'retains the existing proc' do it "retains the existing proc" do
expect(bound_exposure.proc).to eql proc expect(bound_exposure.proc).to eql proc
end end
end end
context 'proc is nil' do context "proc is nil" do
let(:proc) { nil } let(:proc) { nil }
context 'matching instance method' do context "matching instance method" do
let(:bind_object) do let(:bind_object) do
Class.new do Class.new do
def hello(input) def hello(input)
@ -104,31 +104,31 @@ RSpec.describe Dry::View::Exposure do
end end
end end
context 'no matching instance method' do context "no matching instance method" do
let(:object) { Object.new } let(:object) { Object.new }
it 'leaves proc as nil' do it "leaves proc as nil" do
expect(bound_exposure.proc).to be_nil expect(bound_exposure.proc).to be_nil
end end
end end
end end
end end
describe '#dependency_names' do describe "#dependency_names" do
context 'proc provided' do context "proc provided" do
let(:proc) { -> input, foo, bar { 'hi' } } let(:proc) { -> input, foo, bar { "hi" } } # rubocop:disable Lint/UnusedBlockArgument
it "returns an array of exposure dependencies derived from the proc's argument names" do it "returns an array of exposure dependencies derived from the proc's argument names" do
expect(exposure.dependency_names).to eql [:input, :foo, :bar] expect(exposure.dependency_names).to eql [:input, :foo, :bar]
end end
end end
context 'matching instance method' do context "matching instance method" do
let(:proc) { nil } let(:proc) { nil }
let(:object) do let(:object) do
Class.new do Class.new do
def hello(input, bar, baz) def hello(input, bar, baz) # rubocop:disable Lint/UnusedMethodArgument
"hi there, #{input.fetch(:name)}" "hi there, #{input.fetch(:name)}"
end end
end.new end.new
@ -139,73 +139,73 @@ RSpec.describe Dry::View::Exposure do
end end
end end
context 'proc is nil' do context "proc is nil" do
let(:proc) { nil } let(:proc) { nil }
it 'returns no dependencies' do it "returns no dependencies" do
expect(exposure.dependency_names).to eql [] expect(exposure.dependency_names).to eql []
end end
end end
end end
describe '#call' do describe "#call" do
let(:input) { { name: 'Jane' } } let(:input) { {name: "Jane"} }
context 'proc expects input only' do context "proc expects input only" do
let(:proc) { -> name: { name } } let(:proc) { -> name: { name } }
it 'sends the input to the proc' do it "sends the input to the proc" do
expect(exposure.(input)).to eql 'Jane' expect(exposure.(input)).to eql "Jane"
end end
end end
context 'proc expects input and dependencies' do context "proc expects input and dependencies" do
let(:proc) { -> greeting, name: { "#{greeting}, #{name}" } } let(:proc) { -> greeting, name: { "#{greeting}, #{name}" } }
let(:locals) { { greeting: 'Hola' } } let(:locals) { {greeting: "Hola"} }
it 'sends the input and dependency values to the proc' do it "sends the input and dependency values to the proc" do
expect(exposure.(input, locals)).to eq 'Hola, Jane' expect(exposure.(input, locals)).to eq "Hola, Jane"
end end
end end
context 'Default value' do context "Default value" do
let(:options) { { default: 'John' } } let(:options) { {default: "John"} }
context 'use default value' do context "use default value" do
let(:proc) { nil } let(:proc) { nil }
it 'use the default value' do it "use the default value" do
expect(exposure.({})).to eq 'John' expect(exposure.({})).to eq "John"
end end
end end
context 'use input value instead of default' do context "use input value instead of default" do
let(:proc) { nil } let(:proc) { nil }
it 'use the default value' do it "use the default value" do
expect(exposure.({ hello: 'Jane' })).to eq 'Jane' expect(exposure.(hello: "Jane")).to eq "Jane"
end end
end end
context 'use input value over default even when input is nil' do context "use input value over default even when input is nil" do
let(:proc) { nil } let(:proc) { nil }
it 'use the default value' do it "use the default value" do
expect(exposure.({ hello: nil })).to eq nil expect(exposure.(hello: nil)).to eq nil
end end
end end
end end
context 'proc expects dependencies only' do context "proc expects dependencies only" do
let(:proc) { -> greeting, farewell { "#{greeting}, #{farewell}" } } let(:proc) { -> greeting, farewell { "#{greeting}, #{farewell}" } }
let(:locals) { { greeting: 'Hola', farewell: 'Adios' } } let(:locals) { {greeting: "Hola", farewell: "Adios"} }
it 'sends the dependency values to the proc' do it "sends the dependency values to the proc" do
expect(exposure.(input, locals)).to eq 'Hola, Adios' expect(exposure.(input, locals)).to eq "Hola, Adios"
end end
end end
context 'proc accesses object instance' do context "proc accesses object instance" do
let(:proc) { -> name: { "My name is #{name} but call me #{title} #{name}" } } let(:proc) { -> name: { "My name is #{name} but call me #{title} #{name}" } }
let(:object) do let(:object) do
@ -215,23 +215,23 @@ RSpec.describe Dry::View::Exposure do
def initialize(title) def initialize(title)
@title = title @title = title
end end
end.new('Dr') end.new("Dr")
end end
it 'makes the instance available as self' do it "makes the instance available as self" do
expect(exposure.(input)).to eq 'My name is Jane but call me Dr Jane' expect(exposure.(input)).to eq "My name is Jane but call me Dr Jane"
end end
end end
context 'no proc' do context "no proc" do
let(:proc) { nil } let(:proc) { nil }
let(:input) { { hello: 'hi there' } } let(:input) { {hello: "hi there"} }
it 'returns a matching key from the input' do it "returns a matching key from the input" do
expect(exposure.(input)).to eq 'hi there' expect(exposure.(input)).to eq "hi there"
end end
it 'returns nil when no input key matches' do it "returns nil when no input key matches" do
expect(exposure.(nothing_matches_here: true)).to be_nil expect(exposure.(nothing_matches_here: true)).to be_nil
end end
end end

View File

@ -3,15 +3,15 @@
RSpec.describe Dry::View::Exposures do RSpec.describe Dry::View::Exposures do
subject(:exposures) { described_class.new } subject(:exposures) { described_class.new }
describe '#exposures' do describe "#exposures" do
it 'is empty by defalut' do it "is empty by defalut" do
expect(exposures.exposures).to be_empty expect(exposures.exposures).to be_empty
end end
end end
describe '#add' do describe "#add" do
it 'creates and adds an exposure' do it "creates and adds an exposure" do
proc = -> **input { 'hi' } proc = -> **_input { "hi" }
exposures.add :hello, proc exposures.add :hello, proc
expect(exposures[:hello].name).to eq :hello expect(exposures[:hello].name).to eq :hello
@ -19,13 +19,13 @@ RSpec.describe Dry::View::Exposures do
end end
end end
describe '#bind' do describe "#bind" do
subject(:bound_exposures) { exposures.bind(object) } subject(:bound_exposures) { exposures.bind(object) }
let(:object) do let(:object) do
Class.new do Class.new do
def hello(input) def hello(_input)
'hi' "hi"
end end
end.new end.new
end end
@ -34,91 +34,91 @@ RSpec.describe Dry::View::Exposures do
exposures.add(:hello) exposures.add(:hello)
end end
it 'binds each of the exposures' do it "binds each of the exposures" do
expect(bound_exposures[:hello].proc).to eq object.method(:hello) expect(bound_exposures[:hello].proc).to eq object.method(:hello)
end end
it 'returns a new copy of the exposures' do it "returns a new copy of the exposures" do
expect(exposures.exposures).not_to eql(bound_exposures.exposures) expect(exposures.exposures).not_to eql(bound_exposures.exposures)
end end
end end
describe '#call' do describe "#call" do
describe 'in general' do describe "in general" do
before do before do
exposures.add(:greeting, -> greeting: { greeting.upcase }) exposures.add(:greeting, -> greeting: { greeting.upcase })
exposures.add(:farewell, -> greeting { "#{greeting} and goodbye" }) exposures.add(:farewell, -> greeting { "#{greeting} and goodbye" })
end end
subject(:locals) { exposures.(greeting: 'hello') } subject(:locals) { exposures.(greeting: "hello") }
it 'returns the values from calling the exposures' do it "returns the values from calling the exposures" do
expect(locals).to eq(greeting: 'HELLO', farewell: 'HELLO and goodbye') expect(locals).to eq(greeting: "HELLO", farewell: "HELLO and goodbye")
end end
it 'does not include values from private exposures' do it "does not include values from private exposures" do
exposures.add(:hidden, -> **input { 'shh' }, private: true) exposures.add(:hidden, -> **_input { "shh" }, private: true)
expect(locals).to include(:greeting, :farewell) expect(locals).to include(:greeting, :farewell)
expect(locals).not_to include(:hidden) expect(locals).not_to include(:hidden)
end end
end end
describe 'with block provided' do describe "with block provided" do
before do before do
exposures.add(:greeting, -> greeting: { greeting.upcase }) exposures.add(:greeting, -> greeting: { greeting.upcase })
exposures.add(:farewell, -> greeting { "#{greeting} and goodbye" }) exposures.add(:farewell, -> greeting { "#{greeting} and goodbye" })
end end
subject(:locals) { subject(:locals) {
exposures.(greeting: 'hello') do |value, exposure| exposures.(greeting: "hello") do |value, exposure|
"#{value} from #{exposure.name}" "#{value} from #{exposure.name}"
end end
} }
it 'provides values determined from the block' do it "provides values determined from the block" do
expect(locals).to eq( expect(locals).to eq(
greeting: 'HELLO from greeting', greeting: "HELLO from greeting",
farewell: 'HELLO from greeting and goodbye from farewell', farewell: "HELLO from greeting and goodbye from farewell"
) )
end end
end end
describe 'with default exposure values' do describe "with default exposure values" do
it "returns 'default_value' from exposure" do it "returns 'default_value' from exposure" do
exposures.add(:name, default: 'John') exposures.add(:name, default: "John")
locals = exposures.({}) locals = exposures.({})
expect(locals).to eq(name: 'John') expect(locals).to eq(name: "John")
end end
it 'returns values from arguments' do it "returns values from arguments" do
exposures.add(:name, default: 'John') exposures.add(:name, default: "John")
locals = exposures.(name: 'William') locals = exposures.(name: "William")
expect(locals).to eq(name: 'William') expect(locals).to eq(name: "William")
end end
it 'returns values from arguments even when value is nil' do it "returns values from arguments even when value is nil" do
exposures.add(:name, default: 'John') exposures.add(:name, default: "John")
locals = exposures.(name: nil) locals = exposures.(name: nil)
expect(locals).to eq(name: nil) expect(locals).to eq(name: nil)
end end
it 'returns value from proc' do it "returns value from proc" do
exposures.add(:name, -> name: { name.upcase }, default: 'John') exposures.add(:name, -> name: { name.upcase }, default: "John")
locals = exposures.(name: 'William') locals = exposures.(name: "William")
expect(locals).to eq(name: 'WILLIAM') expect(locals).to eq(name: "WILLIAM")
end end
end end
end end
describe '#import' do describe "#import" do
it 'imports an exposure to the set' do it "imports an exposure to the set" do
exposures_b = described_class.new exposures_b = described_class.new
exposures.add(:name, -> name: { name.upcase }, default: 'John') exposures.add(:name, -> name: { name.upcase }, default: "John")
exposures_b.import(:name, exposures[:name]) exposures_b.import(:name, exposures[:name])
expect(exposures_b[:name]).to eq(exposures[:name]) expect(exposures_b[:name]).to eq(exposures[:name])

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/scope_builder' require "dry/view/scope_builder"
RSpec.describe Dry::View::PartBuilder do RSpec.describe Dry::View::PartBuilder do
subject(:part_builder) { render_env.part_builder } subject(:part_builder) { render_env.part_builder }
@ -17,7 +17,7 @@ RSpec.describe Dry::View::PartBuilder do
let(:namespace) { nil } let(:namespace) { nil }
describe '#call' do describe "#call" do
subject(:part) { subject(:part) {
part_builder.(name, value, **options) part_builder.(name, value, **options)
} }
@ -26,31 +26,31 @@ RSpec.describe Dry::View::PartBuilder do
let(:value) { double(:user) } let(:value) { double(:user) }
let(:options) { {} } let(:options) { {} }
shared_examples 'a view part' do shared_examples "a view part" do
let(:part_class) { Dry::View::Part } let(:part_class) { Dry::View::Part }
it 'returns a part' do it "returns a part" do
expect(part).to be_a part_class expect(part).to be_a part_class
end end
it 'wraps the value' do it "wraps the value" do
expect(part._value).to eq value expect(part._value).to eq value
end end
it 'retains the render environment' do it "retains the render environment" do
expect(part._render_env).to eql render_env expect(part._render_env).to eql render_env
end end
end end
shared_examples 'a view part collection' do shared_examples "a view part collection" do
let(:collection_part_class) { Dry::View::Part } let(:collection_part_class) { Dry::View::Part }
let(:item_part_class) { Dry::View::Part } let(:item_part_class) { Dry::View::Part }
it 'returns a part wrapping the collection' do it "returns a part wrapping the collection" do
expect(part).to be_a collection_part_class expect(part).to be_a collection_part_class
end end
it 'wraps the collection and its items' do it "wraps the collection and its items" do
expect(part._value.map(&:_value)).to eq value expect(part._value.map(&:_value)).to eq value
end end
@ -61,70 +61,70 @@ RSpec.describe Dry::View::PartBuilder do
end end
end end
context 'without namespace' do context "without namespace" do
describe 'singular value' do describe "singular value" do
let(:value) { double('user') } let(:value) { double("user") }
it_behaves_like 'a view part' it_behaves_like "a view part"
describe 'alternative name provided via :as option' do describe "alternative name provided via :as option" do
let(:options) { { as: :admin_user } } let(:options) { {as: :admin_user} }
it_behaves_like 'a view part' it_behaves_like "a view part"
end end
describe 'explicit part class provided via as: option' do describe "explicit part class provided via as: option" do
before do before do
Test::UserPart = Class.new(Dry::View::Part) Test::UserPart = Class.new(Dry::View::Part)
end end
let(:options) { { as: Test::UserPart } } let(:options) { {as: Test::UserPart} }
it_behaves_like 'a view part' do it_behaves_like "a view part" do
let(:part_class) { Test::UserPart } let(:part_class) { Test::UserPart }
end end
end end
end end
describe 'array-like value' do describe "array-like value" do
let(:name) { :users } let(:name) { :users }
let(:value) { [double(:user), double(:user)] } let(:value) { [double(:user), double(:user)] }
it_behaves_like 'a view part collection' it_behaves_like "a view part collection"
describe 'alternative name provided via :as option' do describe "alternative name provided via :as option" do
let(:options) { { as: :admin_user } } let(:options) { {as: :admin_user} }
it_behaves_like 'a view part collection' it_behaves_like "a view part collection"
end end
describe 'explicit part class provided via as: option' do describe "explicit part class provided via as: option" do
before do before do
Test::UserPart = Class.new(Dry::View::Part) Test::UserPart = Class.new(Dry::View::Part)
end end
let(:options) { { as: Test::UserPart } } let(:options) { {as: Test::UserPart} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:item_part_class) { Test::UserPart } let(:item_part_class) { Test::UserPart }
end end
end end
describe 'explicit collection part class provided via as: option' do describe "explicit collection part class provided via as: option" do
before do before do
Test::UserCollectionPart = Class.new(Dry::View::Part) Test::UserCollectionPart = Class.new(Dry::View::Part)
end end
let(:options) { { as: [Test::UserCollectionPart] } } let(:options) { {as: [Test::UserCollectionPart]} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::UserCollectionPart } let(:collection_part_class) { Test::UserCollectionPart }
end end
end end
end end
end end
context 'with namespace' do context "with namespace" do
before do before do
module Test module Test
module Parts module Parts
@ -152,100 +152,100 @@ RSpec.describe Dry::View::PartBuilder do
let(:namespace) { Test::Parts } let(:namespace) { Test::Parts }
describe 'singular value' do describe "singular value" do
let(:value) { double('user', profile: 'profile') } let(:value) { double("user", profile: "profile") }
it_behaves_like 'a view part' do it_behaves_like "a view part" do
let(:part_class) { Test::Parts::User } let(:part_class) { Test::Parts::User }
end end
it 'returns decorated attributes in part classes found from the namespace' do it "returns decorated attributes in part classes found from the namespace" do
expect(part.profile).to be_a Test::Parts::Profile expect(part.profile).to be_a Test::Parts::Profile
end end
describe 'alternative name provided via :as option' do describe "alternative name provided via :as option" do
let(:options) { { as: :admin_user } } let(:options) { {as: :admin_user} }
it_behaves_like 'a view part' do it_behaves_like "a view part" do
let(:part_class) { Test::Parts::AdminUser } let(:part_class) { Test::Parts::AdminUser }
end end
end end
describe 'alternative name provided via :as option, when matched constant is not a class inheriting from Dry::View::Part' do describe "alternative name provided via :as option, when matched constant is not a class inheriting from Dry::View::Part" do
let(:options) { { as: :user_module } } let(:options) { {as: :user_module} }
it_behaves_like 'a view part' do it_behaves_like "a view part" do
let(:part_class) { Dry::View::Part } let(:part_class) { Dry::View::Part }
end end
end end
describe 'explicit part class provided via as: option' do describe "explicit part class provided via as: option" do
let(:options) { { as: Test::Parts::AdminUser } } let(:options) { {as: Test::Parts::AdminUser} }
it_behaves_like 'a view part' do it_behaves_like "a view part" do
let(:part_class) { Test::Parts::AdminUser } let(:part_class) { Test::Parts::AdminUser }
end end
end end
end end
describe 'array-like value' do describe "array-like value" do
let(:name) { :users } let(:name) { :users }
let(:value) { [double(:user), double(:user)] } let(:value) { [double(:user), double(:user)] }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::Users } let(:collection_part_class) { Test::Parts::Users }
let(:item_part_class) { Test::Parts::User } let(:item_part_class) { Test::Parts::User }
end end
describe 'alternative element name provided via :as option' do describe "alternative element name provided via :as option" do
let(:options) { { as: :admin_user } } let(:options) { {as: :admin_user} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::Users } let(:collection_part_class) { Test::Parts::Users }
let(:item_part_class) { Test::Parts::AdminUser } let(:item_part_class) { Test::Parts::AdminUser }
end end
end end
describe 'alternative collection name provided via :as option' do describe "alternative collection name provided via :as option" do
let(:options) { { as: [:user_collection] } } let(:options) { {as: [:user_collection]} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::UserCollection } let(:collection_part_class) { Test::Parts::UserCollection }
let(:item_part_class) { Test::Parts::User } let(:item_part_class) { Test::Parts::User }
end end
end end
describe 'alternative collection and element names provided via :as option' do describe "alternative collection and element names provided via :as option" do
let(:options) { { as: [:user_collection, :admin_user] } } let(:options) { {as: [:user_collection, :admin_user]} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::UserCollection } let(:collection_part_class) { Test::Parts::UserCollection }
let(:item_part_class) { Test::Parts::AdminUser } let(:item_part_class) { Test::Parts::AdminUser }
end end
end end
describe 'explicit part class provided via as: option' do describe "explicit part class provided via as: option" do
let(:options) { { as: Test::Parts::AdminUser } } let(:options) { {as: Test::Parts::AdminUser} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::Users } let(:collection_part_class) { Test::Parts::Users }
let(:item_part_class) { Test::Parts::AdminUser } let(:item_part_class) { Test::Parts::AdminUser }
end end
end end
describe 'explicit collection part class provided via as: option' do describe "explicit collection part class provided via as: option" do
let(:options) { { as: [Test::Parts::UserCollection] } } let(:options) { {as: [Test::Parts::UserCollection]} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::UserCollection } let(:collection_part_class) { Test::Parts::UserCollection }
let(:item_part_class) { Test::Parts::User } let(:item_part_class) { Test::Parts::User }
end end
end end
describe 'explicit collection and element part classes provided via :as option' do describe "explicit collection and element part classes provided via :as option" do
let(:options) { { as: [Test::Parts::UserCollection, Test::Parts::AdminUser] } } let(:options) { {as: [Test::Parts::UserCollection, Test::Parts::AdminUser]} }
it_behaves_like 'a view part collection' do it_behaves_like "a view part collection" do
let(:collection_part_class) { Test::Parts::UserCollection } let(:collection_part_class) { Test::Parts::UserCollection }
let(:item_part_class) { Test::Parts::AdminUser } let(:item_part_class) { Test::Parts::AdminUser }
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/scope_builder' require "dry/view/scope_builder"
require 'dry/view/render_environment_missing' require "dry/view/render_environment_missing"
RSpec::Matchers.define :scope do |locals| RSpec::Matchers.define :scope do |locals|
match do |actual| match do |actual|
@ -18,89 +18,89 @@ RSpec.describe Dry::View::Part do
inflector: Dry::Inflector.new, inflector: Dry::Inflector.new,
context: Dry::View::Context.new, context: Dry::View::Context.new,
scope_builder: Dry::View::ScopeBuilder.new, scope_builder: Dry::View::ScopeBuilder.new,
part_builder: Dry::View::ScopeBuilder.new, part_builder: Dry::View::ScopeBuilder.new
) )
} }
let(:renderer) { spy(:renderer, format: :xml) } let(:renderer) { spy(:renderer, format: :xml) }
context 'with a render environment' do context "with a render environment" do
subject(:part) { subject(:part) {
described_class.new( described_class.new(
name: name, name: name,
value: value, value: value,
render_env: render_env, render_env: render_env
) )
} }
describe '#render' do describe "#render" do
it 'renders a partial with the part available in its scope' do it "renders a partial with the part available in its scope" do
part.render(:info) part.render(:info)
expect(renderer).to have_received(:partial).with(:info, scope(user: part)) expect(renderer).to have_received(:partial).with(:info, scope(user: part))
end end
it 'allows the part to be made available on a different name' do it "allows the part to be made available on a different name" do
part.render(:info, as: :admin) part.render(:info, as: :admin)
expect(renderer).to have_received(:partial).with(:info, scope(admin: part)) expect(renderer).to have_received(:partial).with(:info, scope(admin: part))
end end
it 'includes extra locals in the scope' do it "includes extra locals in the scope" do
part.render(:info, extra_local: 'hello') part.render(:info, extra_local: "hello")
expect(renderer).to have_received(:partial).with(:info, scope(user: part, extra_local: 'hello')) expect(renderer).to have_received(:partial).with(:info, scope(user: part, extra_local: "hello"))
end end
end end
describe '#to_s' do describe "#to_s" do
before do before do
allow(value).to receive(:to_s).and_return 'to_s on the value' allow(value).to receive(:to_s).and_return "to_s on the value"
end end
it 'delegates to the wrapped value' do it "delegates to the wrapped value" do
expect(part.to_s).to eq 'to_s on the value' expect(part.to_s).to eq "to_s on the value"
end end
end end
describe '#new' do describe "#new" do
it 'preserves render environment' do it "preserves render environment" do
new_part = part.new(value: 'new value') new_part = part.new(value: "new value")
expect(new_part._render_env).to be part._render_env expect(new_part._render_env).to be part._render_env
end end
end end
describe '#inspect' do describe "#inspect" do
it 'includes the clsas name, name, and value only' do it "includes the clsas name, name, and value only" do
expect(part.inspect).to eq '#<Dry::View::Part name=:user value=#<Double :value>>' expect(part.inspect).to eq "#<Dry::View::Part name=:user value=#<Double :value>>"
end end
end end
describe '#_format' do describe "#_format" do
it "returns the render environment's format" do it "returns the render environment's format" do
expect(part._format).to eq :xml expect(part._format).to eq :xml
end end
end end
describe '#method_missing' do describe "#method_missing" do
let(:value) { double(greeting: 'hello from value') } let(:value) { double(greeting: "hello from value") }
it 'calls a matching method on the value' do it "calls a matching method on the value" do
expect(part.greeting).to eq 'hello from value' expect(part.greeting).to eq "hello from value"
end end
it 'forwards all arguments to the method' do it "forwards all arguments to the method" do
blk = -> {} blk = -> {}
part.greeting 'args', &blk part.greeting "args", &blk
expect(value).to have_received(:greeting).with('args', &blk) expect(value).to have_received(:greeting).with("args", &blk)
end end
it 'raises an error if no method matches' do it "raises an error if no method matches" do
expect { part.farewell }.to raise_error(NoMethodError) expect { part.farewell }.to raise_error(NoMethodError)
end end
end end
describe '#respond_to?' do describe "#respond_to?" do
let(:value) { double(greeting: 'hello from value') } let(:value) { double(greeting: "hello from value") }
it 'handles convenience methods' do it "handles convenience methods" do
expect(part).to respond_to(:format) expect(part).to respond_to(:format)
expect(part).to respond_to(:context) expect(part).to respond_to(:context)
expect(part).to respond_to(:render) expect(part).to respond_to(:render)
@ -108,42 +108,42 @@ RSpec.describe Dry::View::Part do
expect(part).to respond_to(:value) expect(part).to respond_to(:value)
end end
it 'handles value methods' do it "handles value methods" do
expect(part).to respond_to(:greeting) expect(part).to respond_to(:greeting)
end end
end end
end end
context 'without a render environment' do context "without a render environment" do
subject(:part) { subject(:part) {
described_class.new( described_class.new(
name: name, name: name,
value: value, value: value
) )
} }
describe '#format' do describe "#format" do
it 'raises an error' do it "raises an error" do
expect { part.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError) expect { part.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end end
end end
describe '#render' do describe "#render" do
it 'raises an error' do it "raises an error" do
expect { part.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError) expect { part.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end end
end end
describe '#scope' do describe "#scope" do
it 'raises an error' do it "raises an error" do
expect { part.scope(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError) expect { part.scope(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end end
end end
end end
context 'without a name provided' do context "without a name provided" do
describe '#_name' do describe "#_name" do
context 'when class has a name' do context "when class has a name" do
before do before do
Test::MyPart = Class.new(Dry::View::Part) Test::MyPart = Class.new(Dry::View::Part)
end end
@ -152,18 +152,18 @@ RSpec.describe Dry::View::Part do
Test::MyPart.new(value: value) Test::MyPart.new(value: value)
} }
it 'is inferred from the class name' do it "is inferred from the class name" do
expect(part._name).to eq 'my_part' expect(part._name).to eq "my_part"
end end
end end
context 'when class is anonymous' do context "when class is anonymous" do
subject(:part) { subject(:part) {
Class.new(Dry::View::Part).new(value: value) Class.new(Dry::View::Part).new(value: value)
} }
it "defaults to 'part'" do it "defaults to 'part'" do
expect(part._name).to eq 'part' expect(part._name).to eq "part"
end end
end end
end end

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/render_environment' require "dry/view/render_environment"
require 'dry/inflector' require "dry/inflector"
require 'dry/view/context' require "dry/view/context"
require 'dry/view/part_builder' require "dry/view/part_builder"
require 'dry/view/scope_builder' require "dry/view/scope_builder"
RSpec.describe Dry::View::RenderEnvironment do RSpec.describe Dry::View::RenderEnvironment do
subject(:render_env) { described_class.new(**options) } subject(:render_env) { described_class.new(**options) }
@ -16,18 +16,18 @@ RSpec.describe Dry::View::RenderEnvironment do
renderer: Dry::View::Renderer.new([Dry::View::Path.new(FIXTURES_PATH)], format: :html), renderer: Dry::View::Renderer.new([Dry::View::Path.new(FIXTURES_PATH)], format: :html),
context: Dry::View::Context.new, context: Dry::View::Context.new,
part_builder: Dry::View::PartBuilder.new, part_builder: Dry::View::PartBuilder.new,
scope_builder: Dry::View::ScopeBuilder.new, scope_builder: Dry::View::ScopeBuilder.new
} }
} }
describe '#format' do describe "#format" do
it "returns the renderer's format" do it "returns the renderer's format" do
expect(render_env.format).to eq :html expect(render_env.format).to eq :html
end end
end end
describe '#==' do describe "#==" do
it 'is equal when its options are equal' do it "is equal when its options are equal" do
expect(render_env).to eq described_class.new(**options) expect(render_env).to eq described_class.new(**options)
end end
end end

View File

@ -1,38 +1,38 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/rendered' require "dry/view/rendered"
RSpec.describe Dry::View::Rendered do RSpec.describe Dry::View::Rendered do
subject(:rendered) { subject(:rendered) {
described_class.new( described_class.new(
output: 'rendered template output', output: "rendered template output",
locals: { locals: {
user: { name: 'Jane' }, user: {name: "Jane"}
}, }
) )
} }
describe '#to_s' do describe "#to_s" do
it 'returns the rendered output' do it "returns the rendered output" do
expect(rendered.to_s).to eq 'rendered template output' expect(rendered.to_s).to eq "rendered template output"
end end
end end
describe '#to_str' do describe "#to_str" do
it 'returns the rendered output' do it "returns the rendered output" do
expect(rendered.to_str).to eq 'rendered template output' expect(rendered.to_str).to eq "rendered template output"
end end
end end
describe '#locals' do describe "#locals" do
it 'returns the locals hash' do it "returns the locals hash" do
expect(rendered.locals).to eql({ user: { name: 'Jane' } }) expect(rendered.locals).to eql(user: {name: "Jane"})
end end
end end
describe '#[]' do describe "#[]" do
it 'returns the named local' do it "returns the named local" do
expect(rendered[:user]).to eql(name: 'Jane') expect(rendered[:user]).to eql(name: "Jane")
end end
end end
end end

View File

@ -1,68 +1,68 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/path' require "dry/view/path"
require 'dry/view/renderer' require "dry/view/renderer"
RSpec.describe Dry::View::Renderer do RSpec.describe Dry::View::Renderer do
subject(:renderer) do subject(:renderer) do
Dry::View::Renderer.new( Dry::View::Renderer.new(
[Dry::View::Path.new(SPEC_ROOT.join('fixtures/templates'))], [Dry::View::Path.new(SPEC_ROOT.join("fixtures/templates"))],
format: 'html', format: "html",
default_encoding: 'utf-8' default_encoding: "utf-8"
) )
end end
let(:scope) { double(:scope) } let(:scope) { double(:scope) }
describe '#template' do describe "#template" do
it 'renders template in current directory' do it "renders template in current directory" do
expect(renderer.template(:hello, scope)).to eql('<h1>Hello</h1>') expect(renderer.template(:hello, scope)).to eql("<h1>Hello</h1>")
end end
it 'does not include `shared/` subdirectory under root when looking up templates' do it "does not include `shared/` subdirectory under root when looking up templates" do
expect { expect {
renderer.template(:_shared_hello, scope) renderer.template(:_shared_hello, scope)
}.to raise_error(Dry::View::TemplateNotFoundError, /_shared_hello/) }.to raise_error(Dry::View::TemplateNotFoundError, /_shared_hello/)
end end
it 'raises error when template cannot be found' do it "raises error when template cannot be found" do
expect { expect {
renderer.template(:missing_template, scope) renderer.template(:missing_template, scope)
}.to raise_error(Dry::View::TemplateNotFoundError, /missing_template/) }.to raise_error(Dry::View::TemplateNotFoundError, /missing_template/)
end end
end end
describe '#partial' do describe "#partial" do
it 'renders partial in current directory' do it "renders partial in current directory" do
expect(renderer.partial(:hello, scope)).to eql('<h1>Partial hello</h1>') expect(renderer.partial(:hello, scope)).to eql("<h1>Partial hello</h1>")
end end
it 'renders partial in shared/ subdirectory under root' do it "renders partial in shared/ subdirectory under root" do
expect(renderer.chdir('hello').partial(:shared_hello, scope)).to eql('<h1>Hello</h1>') expect(renderer.chdir("hello").partial(:shared_hello, scope)).to eql("<h1>Hello</h1>")
end end
it 'renders partial in shared/ subdirectory when descending from an upper directory' do it "renders partial in shared/ subdirectory when descending from an upper directory" do
expect(renderer.chdir('hello').partial(:shared_hello, scope)).to eql('<h1>Hello</h1>') expect(renderer.chdir("hello").partial(:shared_hello, scope)).to eql("<h1>Hello</h1>")
end end
it 'renders partial in upper directory' do it "renders partial in upper directory" do
expect(renderer.chdir('nested').partial(:hello, scope)).to eql('<h1>Partial hello</h1>') expect(renderer.chdir("nested").partial(:hello, scope)).to eql("<h1>Partial hello</h1>")
end end
it 'renders partial in upper shared/ subdirectory' do it "renders partial in upper shared/ subdirectory" do
expect(renderer.chdir('nested').partial(:shared_hello, scope)).to eql('<h1>Hello</h1>') expect(renderer.chdir("nested").partial(:shared_hello, scope)).to eql("<h1>Hello</h1>")
end end
it 'raises error when partial cannot be found' do it "raises error when partial cannot be found" do
expect { expect {
renderer.partial(:missing_partial, scope) renderer.partial(:missing_partial, scope)
}.to raise_error(Dry::View::TemplateNotFoundError, /_missing_partial/) }.to raise_error(Dry::View::TemplateNotFoundError, /_missing_partial/)
end end
end end
describe '#chdir' do describe "#chdir" do
it 'copies options to new renderer instance' do it "copies options to new renderer instance" do
expect(renderer.chdir('nested').options).to eq(default_encoding: 'utf-8') expect(renderer.chdir("nested").options).to eq(default_encoding: "utf-8")
end end
end end
end end

View File

@ -1,113 +1,113 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'dry/view/scope_builder' require "dry/view/scope_builder"
RSpec.describe Dry::View::Scope do RSpec.describe Dry::View::Scope do
let(:locals) { {} } let(:locals) { {} }
context 'with a render environment' do context "with a render environment" do
subject(:scope) { subject(:scope) {
described_class.new( described_class.new(
locals: locals, locals: locals,
render_env: render_env, render_env: render_env
) )
} }
let(:render_env) { spy(:render_env, format: :xml, context: context) } let(:render_env) { spy(:render_env, format: :xml, context: context) }
let(:context) { double(:context) } let(:context) { double(:context) }
describe '#render' do describe "#render" do
it 'renders a partial with itself as the scope' do it "renders a partial with itself as the scope" do
scope.render(:info) scope.render(:info)
expect(render_env).to have_received(:partial).with(:info, scope) expect(render_env).to have_received(:partial).with(:info, scope)
end end
it 'renders a partial with provided locals' do it "renders a partial with provided locals" do
scope_with_locals = described_class.new( scope_with_locals = described_class.new(
locals: { foo: 'bar' }, locals: {foo: "bar"},
render_env: render_env, render_env: render_env
) )
scope.render(:info, foo: 'bar') scope.render(:info, foo: "bar")
expect(render_env).to have_received(:partial).with(:info, scope_with_locals) expect(render_env).to have_received(:partial).with(:info, scope_with_locals)
end end
end end
describe '#_format' do describe "#_format" do
it "returns the render environments's format" do it "returns the render environments's format" do
expect(scope._format).to eq :xml expect(scope._format).to eq :xml
end end
end end
describe '#_context' do describe "#_context" do
it "returns the render environment's context" do it "returns the render environment's context" do
expect(scope._context).to be context expect(scope._context).to be context
end end
end end
describe '#method_missing' do describe "#method_missing" do
describe 'matching locals' do describe "matching locals" do
let(:locals) { { greeting: 'hello from locals' } } let(:locals) { {greeting: "hello from locals"} }
let(:context) { double(:context, greeting: 'hello from context') } let(:context) { double(:context, greeting: "hello from context") }
it 'returns a matching value from the locals, in favour of a matching method on the context' do it "returns a matching value from the locals, in favour of a matching method on the context" do
expect(scope.greeting).to eq 'hello from locals' expect(scope.greeting).to eq "hello from locals"
end end
end end
describe 'matching context' do describe "matching context" do
let(:context) { double(:context, greeting: 'hello from context') } let(:context) { double(:context, greeting: "hello from context") }
it 'calls the matching method on the context' do it "calls the matching method on the context" do
expect(scope.greeting).to eq 'hello from context' expect(scope.greeting).to eq "hello from context"
end end
it 'forwards all arguments to the method' do it "forwards all arguments to the method" do
blk = -> {} blk = -> {}
scope.greeting 'args', &blk scope.greeting "args", &blk
expect(context).to have_received(:greeting).with('args', &blk) expect(context).to have_received(:greeting).with("args", &blk)
end end
end end
describe 'matching convenience methods' do describe "matching convenience methods" do
it 'provides #context' do it "provides #context" do
expect(scope.context).to be context expect(scope.context).to be context
end end
it 'provides #locals' do it "provides #locals" do
expect(scope.locals).to be locals expect(scope.locals).to be locals
end end
end end
describe 'no matches' do describe "no matches" do
it 'raises an error' do it "raises an error" do
expect { scope.greeting }.to raise_error(NoMethodError) expect { scope.greeting }.to raise_error(NoMethodError)
end end
end end
end end
end end
context 'without a render environment' do context "without a render environment" do
subject(:scope) { subject(:scope) {
described_class.new(locals: locals) described_class.new(locals: locals)
} }
describe '#render' do describe "#render" do
it 'raises an error' do it "raises an error" do
expect { scope.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError) expect { scope.render(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end end
end end
describe '#scope' do describe "#scope" do
it 'raises an error' do it "raises an error" do
expect { scope.scope(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError) expect { scope.scope(:info) }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end end
end end
describe '#_context' do describe "#_context" do
it 'raises an error' do it "raises an error" do
expect { scope._context }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError) expect { scope._context }.to raise_error(Dry::View::RenderEnvironmentMissing::MissingEnvironmentError)
end end
end end

View File

@ -1,20 +1,20 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'tilt/erubi' require "tilt/erubi"
RSpec.describe Dry::View do RSpec.describe Dry::View do
subject(:view) { subject(:view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.layout = 'app' config.layout = "app"
config.template = 'user' config.template = "user"
expose :user do expose :user do
{ name: 'Jane' } {name: "Jane"}
end end
expose :header do expose :header do
{ title: 'User' } {title: "User"}
end end
end.new end.new
} }
@ -22,26 +22,26 @@ RSpec.describe Dry::View do
let(:context) do let(:context) do
Class.new(Dry::View::Context) do Class.new(Dry::View::Context) do
def title def title
'Test' "Test"
end end
end.new end.new
end end
describe '#call' do describe "#call" do
it 'renders template within the layout' do it "renders template within the layout" do
expect(view.(context: context).to_s).to eql( expect(view.(context: context).to_s).to eql(
'<!DOCTYPE html><html><head><title>Test</title></head><body><h1>User</h1><p>Jane</p></body></html>' "<!DOCTYPE html><html><head><title>Test</title></head><body><h1>User</h1><p>Jane</p></body></html>"
) )
end end
end end
describe 'renderer options' do describe "renderer options" do
subject(:view) { subject(:view) {
Class.new(Dry::View) do Class.new(Dry::View) do
config.paths = SPEC_ROOT.join('fixtures/templates') config.paths = SPEC_ROOT.join("fixtures/templates")
config.template = 'view_renderer_options' config.template = "view_renderer_options"
config.renderer_engine_mapping = { erb: Tilt::ErubiTemplate } config.renderer_engine_mapping = {erb: Tilt::ErubiTemplate}
config.renderer_options = { outvar: '@__buf__' } config.renderer_options = {outvar: "@__buf__"}
end.new end.new
} }
@ -49,11 +49,11 @@ RSpec.describe Dry::View do
module Test module Test
class Form class Form
def initialize(action, &block) def initialize(action, &block)
@buf = eval('@__buf__', block.binding) @buf = eval("@__buf__", block.binding, __FILE__, __LINE__)
@buf << "<form action=\"#{action}\" method=\"post\">" @buf << "<form action=\"#{action}\" method=\"post\">"
block.(self) block.(self)
@buf << '</form>' @buf << "</form>"
end end
def text(name) def text(name)
@ -71,13 +71,13 @@ RSpec.describe Dry::View do
end.new end.new
} }
it 'merges configured options with default encoding' do it "merges configured options with default encoding" do
expect(view.class.config.renderer_options[:outvar]).to eq '@__buf__' expect(view.class.config.renderer_options[:outvar]).to eq "@__buf__"
expect(view.class.config.renderer_options[:default_encoding]).to eq 'utf-8' expect(view.class.config.renderer_options[:default_encoding]).to eq "utf-8"
end end
it 'are passed to renderer' do it "are passed to renderer" do
expect(view.(context: context).to_s.gsub(/\n\s*/m, '')).to eq( expect(view.(context: context).to_s.gsub(/\n\s*/m, "")).to eq(
'<form action="/people" method="post"><input type="text" name="name" /></form>' '<form action="/people" method="post"><input type="text" name="name" /></form>'
) )
end end