dry-view 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|
[][code_climate]
|
13
13
|
[][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.
|