dry-view 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +15 -11
- data/CHANGELOG.md +23 -0
- data/Gemfile +6 -5
- data/README.md +8 -1
- data/benchmarks/templates/{button.erb → button.html.erb} +0 -0
- data/benchmarks/view.rb +3 -4
- data/bin/console +7 -0
- data/dry-view.gemspec +7 -6
- data/lib/dry/view/controller.rb +107 -0
- data/lib/dry/view/exposure.rb +61 -0
- data/lib/dry/view/exposures.rb +50 -0
- data/lib/dry/view/path.rb +40 -0
- data/lib/dry/view/renderer.rb +20 -28
- data/lib/dry/view/scope.rb +55 -0
- data/lib/dry/view/version.rb +1 -1
- data/lib/dry/view.rb +1 -1
- data/spec/fixtures/templates/empty.html.slim +1 -0
- data/spec/fixtures/templates/layouts/app.html.slim +1 -1
- data/spec/fixtures/templates/layouts/app.txt.erb +1 -1
- data/spec/fixtures/templates/parts_with_args/_box.html.slim +3 -0
- data/spec/fixtures/templates/parts_with_args.html.slim +3 -0
- data/spec/fixtures/templates/users/_tbody.html.slim +1 -1
- data/spec/fixtures/templates/users.html.slim +4 -4
- data/spec/fixtures/templates/users.txt.erb +0 -2
- data/spec/fixtures/templates/users_with_count.html.slim +5 -0
- data/spec/fixtures/templates_override/users.html.slim +5 -0
- data/spec/integration/exposures_spec.rb +178 -0
- data/spec/integration/view_spec.rb +83 -20
- data/spec/spec_helper.rb +13 -3
- data/spec/unit/controller_spec.rb +36 -0
- data/spec/unit/exposure_spec.rb +146 -0
- data/spec/unit/exposures_spec.rb +63 -0
- data/spec/unit/renderer_spec.rb +2 -1
- data/spec/unit/scope_spec.rb +98 -0
- metadata +36 -46
- data/lib/dry/view/layout.rb +0 -126
- data/lib/dry/view/null_part.rb +0 -30
- data/lib/dry/view/part.rb +0 -39
- data/lib/dry/view/value_part.rb +0 -50
- data/spec/unit/layout_spec.rb +0 -55
- data/spec/unit/null_part_spec.rb +0 -39
- data/spec/unit/value_part_spec.rb +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5cf179c08c3deb29086bc87197420a301595a70f
|
4
|
+
data.tar.gz: 1c6eccdfb39502b559748c8c75d4c4b127b16019
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 390c86f0b801d44bfe8a5958a982da88a52030dbfbf4403380af90c82c1279d3603a0f4376d1b2f349f3e42f36628c3a551b37e3fe8d2d75874b77d1e4d1efb3
|
7
|
+
data.tar.gz: cfdc7df2e7d8b63cda8611d203d9ec39da0a31d9237e742a00e861244556b2cfc5854b09a312abb358f77c572d4102b56d707d74ee27a61154fb7d6c44952756
|
data/.travis.yml
CHANGED
@@ -1,25 +1,29 @@
|
|
1
1
|
language: ruby
|
2
|
+
dist: trusty
|
2
3
|
sudo: false
|
3
4
|
cache: bundler
|
4
5
|
bundler_args: --without tools benchmarks
|
6
|
+
before_install:
|
7
|
+
- gem update --system
|
8
|
+
- rvm @global do gem uninstall bundler -a -x
|
9
|
+
- rvm @global do gem install bundler -v 1.13.7
|
5
10
|
script:
|
6
|
-
- bundle exec rake
|
11
|
+
- bundle exec rake
|
12
|
+
after_success:
|
13
|
+
# Send coverage report from the job #1 == current MRI release
|
14
|
+
- '[ "${TRAVIS_JOB_NUMBER#*.}" = "1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
|
7
15
|
rvm:
|
8
|
-
- 2.0
|
9
|
-
- 2.
|
10
|
-
- 2.2
|
11
|
-
- 2.
|
12
|
-
-
|
13
|
-
- jruby-9000
|
14
|
-
- ruby-head
|
15
|
-
- jruby-head
|
16
|
+
- 2.4.0
|
17
|
+
- 2.3.3
|
18
|
+
- 2.2.6
|
19
|
+
- 2.1.10
|
20
|
+
- jruby-9.1.6.0
|
16
21
|
env:
|
17
22
|
global:
|
18
23
|
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
19
24
|
matrix:
|
20
25
|
allow_failures:
|
21
|
-
- rvm:
|
22
|
-
- rvm: jruby-head
|
26
|
+
- rvm: jruby-9.1.6.0
|
23
27
|
notifications:
|
24
28
|
email: false
|
25
29
|
webhooks:
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,30 @@
|
|
1
|
+
# 0.2.0 / 2017-01-30
|
2
|
+
|
3
|
+
This release is a major reorientation for dry-view, and it should allow for more natural, straightforward template authoring.
|
4
|
+
|
5
|
+
### Changed
|
6
|
+
|
7
|
+
- [BREAKING] `Dry::View::Layout` renamed to `Dry::View::Controller`. The "view controller" name better represents this object's job: to (timriley)
|
8
|
+
- [BREAKING] `Dry::View::Controller`'s `name` setting is replaced by `template`, which also supports falsey values to disable layout rendering entirely (timriley)
|
9
|
+
- [BREAKING] `Dry::View::Controller`'s `formats` setting is replaced by `default_format`, which expects a simple string or symbol. The default value is `:html`. (timriley)
|
10
|
+
- [BREAKING] `Dry::View::Controller`'s `root` setting is replaced by `paths`, which can now accept an array of one or more directories. These will be searched for templates in order, with the first match winning (timriley)
|
11
|
+
- [BREAKING] `Dry::View::Controller`'s `scope` setting is removed and replaced by `context`, which will be made available to all templates rendered from a view controller (layouts and partials inculded), not just the layout (timriley)
|
12
|
+
- [BREAKING] View parts have been replaced by a simple `Scope`. Data passed to the templates can be accessed directly, rather than wrapped up in a view part. (timriley)
|
13
|
+
- [BREAKING] With view parts removed, partials can only be rendered by top-level method calls within templates (timriley)
|
14
|
+
- Ruby version 2.1.0 is now the earliest supported version (timriley)
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
- Will render templates using any Tilt-supported engine, based on the template's final file extension (e.g. `hello.html.slim` will use Slim). For thread-safety, be sure to explicitly require any engine gems you intend to use. (timriley)
|
19
|
+
- `expose` (and `expose_private`) `Dry::View::Controller` class methods allow you to more easily declare and prepare the data for your template (timriley)
|
20
|
+
- Added `Dry::View::Scope`, which is the scope used for rendering templates. This includes the data from the exposures along with the context object (timriley)
|
21
|
+
|
1
22
|
# 0.1.1 / 2016-07-07
|
2
23
|
|
3
24
|
### Changed
|
4
25
|
|
26
|
+
- Wrap `page` object exposed to layout templates in a part object, so it offers behaviour that is consistent with the part objects that template authors work with on other templates (timriley)
|
27
|
+
- Render template content first, before passing that content to the layout. This makes "content_for"-style behaviours possible, where the template stores some data that the layout can then use later (timriley)
|
5
28
|
- Configure default template encoding to be UTF-8, fixing some issues with template rendering on deployed sites (gotar)
|
6
29
|
|
7
30
|
# 0.1.0 / 2016-03-28
|
data/Gemfile
CHANGED
@@ -2,16 +2,17 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
+
group :tools do
|
6
|
+
gem 'pry'
|
7
|
+
end
|
8
|
+
|
5
9
|
group :test do
|
6
10
|
gem 'byebug', platform: :mri
|
7
11
|
gem 'rack', '>= 1.0.0', '<= 2.0.0'
|
8
12
|
gem 'slim'
|
9
13
|
|
10
|
-
gem '
|
11
|
-
|
12
|
-
|
13
|
-
group :tools do
|
14
|
-
gem 'pry'
|
14
|
+
gem 'simplecov'
|
15
|
+
gem 'codeclimate-test-reporter'
|
15
16
|
end
|
16
17
|
|
17
18
|
group :benchmarks do
|
data/README.md
CHANGED
@@ -12,4 +12,11 @@
|
|
12
12
|
[![Test Coverage](https://img.shields.io/codeclimate/coverage/github/dry-rb/dry-view.svg)][code_climate]
|
13
13
|
[![API Documentation Coverage](http://inch-ci.org/github/dry-rb/dry-view.svg)][inch]
|
14
14
|
|
15
|
-
|
15
|
+
|
16
|
+
A simple, standalone view rendering system built around functional view
|
17
|
+
controllers and templates. dry-view allows you to model your views as stateless
|
18
|
+
transformations, accepting user input and returning your rendered view.
|
19
|
+
|
20
|
+
## Links
|
21
|
+
|
22
|
+
* [Documentation](http://dry-rb.org/gems/dry-view)
|
File without changes
|
data/benchmarks/view.rb
CHANGED
@@ -12,14 +12,13 @@ class ActionRender
|
|
12
12
|
end
|
13
13
|
|
14
14
|
action_renderer = ActionRender.new
|
15
|
-
|
16
|
-
|
17
|
-
template = rodakase_renderer.dir.join('button.erb')
|
15
|
+
dry_view_renderer = Dry::View::Renderer.new(Pathname(__FILE__).dirname.join('templates'), format: :html)
|
18
16
|
|
17
|
+
template = Pathname(__FILE__).dirname.join('templates').join('button.html.erb')
|
19
18
|
SCOPE = {}
|
20
19
|
|
21
20
|
Benchmark.ips do |x|
|
22
21
|
x.report('actionview') { action_renderer.button }
|
23
|
-
x.report('
|
22
|
+
x.report('dry-view') { dry_view_renderer.render(template, SCOPE) }
|
24
23
|
x.compare!
|
25
24
|
end
|
data/bin/console
ADDED
data/dry-view.gemspec
CHANGED
@@ -6,19 +6,21 @@ require 'dry/view/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "dry-view"
|
8
8
|
spec.version = Dry::View::VERSION
|
9
|
-
spec.authors = ["Piotr Solnica"]
|
10
|
-
spec.email = ["piotr.solnica@gmail.com"]
|
11
|
-
spec.summary = "
|
9
|
+
spec.authors = ["Piotr Solnica", "Tim Riley"]
|
10
|
+
spec.email = ["piotr.solnica@gmail.com", "tim@icelab.com.au"]
|
11
|
+
spec.summary = "Functional view rendering system"
|
12
12
|
spec.description = spec.summary
|
13
13
|
spec.homepage = "https://github.com/dry-rb/dry-view"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
|
-
spec.
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
20
|
spec.require_paths = ["lib"]
|
20
21
|
|
21
|
-
spec.
|
22
|
+
spec.required_ruby_version = '>= 2.1.0'
|
23
|
+
|
22
24
|
spec.add_runtime_dependency "tilt", "~> 2.0"
|
23
25
|
spec.add_runtime_dependency "dry-configurable", "~> 0.1"
|
24
26
|
spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
|
@@ -26,5 +28,4 @@ Gem::Specification.new do |spec|
|
|
26
28
|
spec.add_development_dependency "bundler", "~> 1.7"
|
27
29
|
spec.add_development_dependency "rake", "~> 10.0"
|
28
30
|
spec.add_development_dependency "rspec", "~> 3.1"
|
29
|
-
spec.add_development_dependency "capybara", "~> 2.5"
|
30
31
|
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'dry-configurable'
|
2
|
+
require 'dry-equalizer'
|
3
|
+
|
4
|
+
require 'dry/view/path'
|
5
|
+
require 'dry/view/exposures'
|
6
|
+
require 'dry/view/renderer'
|
7
|
+
require 'dry/view/scope'
|
8
|
+
|
9
|
+
module Dry
|
10
|
+
module View
|
11
|
+
class Controller
|
12
|
+
DEFAULT_LAYOUTS_DIR = 'layouts'.freeze
|
13
|
+
DEFAULT_CONTEXT = Object.new.freeze
|
14
|
+
EMPTY_LOCALS = {}.freeze
|
15
|
+
|
16
|
+
include Dry::Equalizer(:config)
|
17
|
+
|
18
|
+
extend Dry::Configurable
|
19
|
+
|
20
|
+
setting :paths
|
21
|
+
setting :layout, false
|
22
|
+
setting :context, DEFAULT_CONTEXT
|
23
|
+
setting :template
|
24
|
+
setting :default_format, :html
|
25
|
+
|
26
|
+
attr_reader :config
|
27
|
+
attr_reader :layout_dir
|
28
|
+
attr_reader :layout_path
|
29
|
+
attr_reader :template_path
|
30
|
+
attr_reader :exposures
|
31
|
+
|
32
|
+
def self.paths
|
33
|
+
Array(config.paths).map { |path| Dry::View::Path.new(path) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.renderer(format)
|
37
|
+
renderers.fetch(format) {
|
38
|
+
renderers[format] = Renderer.new(paths, format: format)
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.renderers
|
43
|
+
@renderers ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.expose(*names, **options, &block)
|
47
|
+
if names.length == 1
|
48
|
+
exposures.add(names.first, block, **options)
|
49
|
+
else
|
50
|
+
names.each do |name|
|
51
|
+
exposures.add(name, nil, **options)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.private_expose(*names, &block)
|
57
|
+
expose(*names, to_view: false, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.exposures
|
61
|
+
@exposures ||= Exposures.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
@config = self.class.config
|
66
|
+
@layout_dir = DEFAULT_LAYOUTS_DIR
|
67
|
+
@layout_path = "#{layout_dir}/#{config.layout}"
|
68
|
+
@template_path = config.template
|
69
|
+
@exposures = self.class.exposures.bind(self)
|
70
|
+
end
|
71
|
+
|
72
|
+
def call(format: config.default_format, **input)
|
73
|
+
renderer = self.class.renderer(format)
|
74
|
+
|
75
|
+
template_content = renderer.(template_path, template_scope(renderer, **input))
|
76
|
+
|
77
|
+
return template_content unless layout?
|
78
|
+
|
79
|
+
renderer.(layout_path, layout_scope(renderer, **input)) do
|
80
|
+
template_content
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def locals(locals: EMPTY_LOCALS, **input)
|
85
|
+
exposures.locals(input).merge(locals)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def layout?
|
91
|
+
!!config.layout
|
92
|
+
end
|
93
|
+
|
94
|
+
def layout_scope(renderer, context: config.context, **)
|
95
|
+
scope(renderer.chdir(layout_dir), EMPTY_LOCALS, context)
|
96
|
+
end
|
97
|
+
|
98
|
+
def template_scope(renderer, context: config.context, **input)
|
99
|
+
scope(renderer.chdir(template_path), locals(**input), context)
|
100
|
+
end
|
101
|
+
|
102
|
+
def scope(renderer, locals, context)
|
103
|
+
Scope.new(renderer, locals, context)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Dry
|
2
|
+
module View
|
3
|
+
class Exposure
|
4
|
+
SUPPORTED_PARAMETER_TYPES = [:req, :opt].freeze
|
5
|
+
|
6
|
+
attr_reader :name
|
7
|
+
attr_reader :proc
|
8
|
+
attr_reader :to_view
|
9
|
+
|
10
|
+
def initialize(name, proc = nil, to_view: true)
|
11
|
+
ensure_proc_parameters(proc) if proc
|
12
|
+
|
13
|
+
@name = name
|
14
|
+
@proc = proc
|
15
|
+
@to_view = to_view
|
16
|
+
end
|
17
|
+
|
18
|
+
def bind(obj)
|
19
|
+
proc ? self : with_default_proc(obj)
|
20
|
+
end
|
21
|
+
|
22
|
+
def dependencies
|
23
|
+
proc.parameters.map(&:last)
|
24
|
+
end
|
25
|
+
|
26
|
+
alias_method :to_view?, :to_view
|
27
|
+
|
28
|
+
def call(input, locals = {})
|
29
|
+
params = dependencies.map.with_index { |name, position|
|
30
|
+
if position.zero?
|
31
|
+
locals.fetch(name) { input }
|
32
|
+
else
|
33
|
+
locals.fetch(name)
|
34
|
+
end
|
35
|
+
}
|
36
|
+
|
37
|
+
proc.(*params)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def with_default_proc(obj)
|
43
|
+
self.class.new(name, build_default_proc(obj), to_view: to_view)
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_default_proc(obj)
|
47
|
+
if obj.respond_to?(name, _include_private = true)
|
48
|
+
obj.method(name)
|
49
|
+
else
|
50
|
+
-> input { input.fetch(name) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def ensure_proc_parameters(proc)
|
55
|
+
if proc.parameters.any? { |type, _| !SUPPORTED_PARAMETER_TYPES.include?(type) }
|
56
|
+
raise ArgumentError, "+proc+ must take positional arugments only"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "tsort"
|
2
|
+
require "dry/view/exposure"
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module View
|
6
|
+
class Exposures
|
7
|
+
include TSort
|
8
|
+
|
9
|
+
attr_reader :exposures
|
10
|
+
|
11
|
+
def initialize(exposures = {})
|
12
|
+
@exposures = exposures
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](name)
|
16
|
+
exposures[name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(name, proc = nil, **options)
|
20
|
+
exposures[name] = Exposure.new(name, proc, **options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def bind(obj)
|
24
|
+
bound_exposures = Hash[exposures.map { |name, exposure|
|
25
|
+
[name, exposure.bind(obj)]
|
26
|
+
}]
|
27
|
+
|
28
|
+
self.class.new(bound_exposures)
|
29
|
+
end
|
30
|
+
|
31
|
+
def locals(input)
|
32
|
+
tsort.each_with_object({}) { |name, memo|
|
33
|
+
memo[name] = self[name].(input, memo) if exposures.key?(name)
|
34
|
+
}.each_with_object({}) { |(name, val), memo|
|
35
|
+
memo[name] = val if self[name].to_view?
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def tsort_each_node(&block)
|
42
|
+
exposures.each_key(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
def tsort_each_child(name, &block)
|
46
|
+
self[name].dependencies.each(&block) if exposures.key?(name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module View
|
5
|
+
class Path
|
6
|
+
include Dry::Equalizer(:dir, :root)
|
7
|
+
|
8
|
+
attr_reader :dir, :root
|
9
|
+
|
10
|
+
def initialize(dir, options = {})
|
11
|
+
@dir = Pathname(dir)
|
12
|
+
@root = Pathname(options.fetch(:root, dir))
|
13
|
+
end
|
14
|
+
|
15
|
+
def lookup(name, format)
|
16
|
+
template?(name, format) || template?("shared/#{name}", format) || !root? && chdir('..').lookup(name, format)
|
17
|
+
end
|
18
|
+
|
19
|
+
def chdir(dirname)
|
20
|
+
self.class.new(dir.join(dirname), root: root)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
dir
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def root?
|
30
|
+
dir == root
|
31
|
+
end
|
32
|
+
|
33
|
+
# Search for a template using a wildcard for the engine extension
|
34
|
+
def template?(name, format)
|
35
|
+
glob = dir.join("#{name}.#{format}.*")
|
36
|
+
Dir[glob].first
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/dry/view/renderer.rb
CHANGED
@@ -4,21 +4,19 @@ require 'dry-equalizer'
|
|
4
4
|
module Dry
|
5
5
|
module View
|
6
6
|
class Renderer
|
7
|
-
include Dry::Equalizer(:
|
7
|
+
include Dry::Equalizer(:paths, :format)
|
8
8
|
|
9
9
|
TemplateNotFoundError = Class.new(StandardError)
|
10
10
|
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :paths, :format, :engine, :tilts
|
12
12
|
|
13
13
|
def self.tilts
|
14
14
|
@__engines__ ||= {}
|
15
15
|
end
|
16
16
|
|
17
|
-
def initialize(
|
18
|
-
@
|
19
|
-
@
|
20
|
-
@format = options[:format]
|
21
|
-
@engine = options[:engine]
|
17
|
+
def initialize(paths, format:)
|
18
|
+
@paths = paths
|
19
|
+
@format = format
|
22
20
|
@tilts = self.class.tilts
|
23
21
|
end
|
24
22
|
|
@@ -28,7 +26,8 @@ module Dry
|
|
28
26
|
if path
|
29
27
|
render(path, scope, &block)
|
30
28
|
else
|
31
|
-
|
29
|
+
msg = "Template #{template.inspect} could not be found in paths:\n#{paths.map { |pa| "- #{pa.to_s}" }.join("\n")}"
|
30
|
+
raise TemplateNotFoundError, msg
|
32
31
|
end
|
33
32
|
end
|
34
33
|
|
@@ -36,32 +35,25 @@ module Dry
|
|
36
35
|
tilt(path).render(scope, &block)
|
37
36
|
end
|
38
37
|
|
39
|
-
def
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
def lookup(name)
|
44
|
-
template?(name) || template?("shared/#{name}") || !root? && chdir('..').lookup(name)
|
45
|
-
end
|
38
|
+
def chdir(dirname)
|
39
|
+
new_paths = paths.map { |path| path.chdir(dirname) }
|
46
40
|
|
47
|
-
|
48
|
-
dir == root
|
41
|
+
self.class.new(new_paths, format: format)
|
49
42
|
end
|
50
43
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
template_path
|
56
|
-
end
|
44
|
+
def lookup(name)
|
45
|
+
paths.inject(false) { |result, path|
|
46
|
+
result || path.lookup(name, format)
|
47
|
+
}
|
57
48
|
end
|
58
49
|
|
59
|
-
|
60
|
-
dir.join("#{name}.#{format}.#{engine}")
|
61
|
-
end
|
50
|
+
private
|
62
51
|
|
63
|
-
|
64
|
-
|
52
|
+
# TODO: make default_encoding configurable
|
53
|
+
def tilt(path)
|
54
|
+
tilts.fetch(path) {
|
55
|
+
tilts[path] = Tilt.new(path, nil, default_encoding: "utf-8")
|
56
|
+
}
|
65
57
|
end
|
66
58
|
end
|
67
59
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'dry-equalizer'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module View
|
5
|
+
class Scope
|
6
|
+
include Dry::Equalizer(:_renderer, :_data)
|
7
|
+
|
8
|
+
attr_reader :_renderer
|
9
|
+
attr_reader :_data
|
10
|
+
attr_reader :_context
|
11
|
+
|
12
|
+
def initialize(renderer, data, context = nil)
|
13
|
+
@_renderer = renderer
|
14
|
+
@_data = data.to_hash
|
15
|
+
@_context = context
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to_missing?(name, include_private = false)
|
19
|
+
_template?(name) || _data.key?(name) || _context.respond_to?(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def method_missing(name, *args, &block)
|
25
|
+
if _data.key?(name)
|
26
|
+
_data[name]
|
27
|
+
elsif _context.respond_to?(name)
|
28
|
+
_context.public_send(name, *args, &block)
|
29
|
+
elsif (template_path = _template?(name))
|
30
|
+
_render(template_path, *args, &block)
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def _template?(name)
|
37
|
+
_renderer.lookup("_#{name}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def _render(path, *args, &block)
|
41
|
+
_renderer.render(path, _render_args(*args), &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def _render_args(*args)
|
45
|
+
if args.empty?
|
46
|
+
self
|
47
|
+
elsif args.length == 1 && args.first.respond_to?(:to_hash)
|
48
|
+
self.class.new(_renderer, args.first, _context)
|
49
|
+
else
|
50
|
+
raise ArgumentError, "render argument must be a Hash"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/dry/view/version.rb
CHANGED
data/lib/dry/view.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
require 'dry/view/renderer'
|
2
|
-
require 'dry/view/
|
2
|
+
require 'dry/view/controller'
|
@@ -0,0 +1 @@
|
|
1
|
+
p This is a view with no locals.
|