dry-view 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +26 -0
- data/.rspec +2 -0
- data/.travis.yml +33 -0
- data/Gemfile +19 -0
- data/LICENSE.md +10 -0
- data/README.md +15 -0
- data/Rakefile +6 -0
- data/benchmarks/templates/button.erb +1 -0
- data/benchmarks/view.rb +25 -0
- data/dry-view.gemspec +30 -0
- data/lib/dry-view.rb +1 -0
- data/lib/dry/view.rb +2 -0
- data/lib/dry/view/layout.rb +122 -0
- data/lib/dry/view/null_part.rb +30 -0
- data/lib/dry/view/part.rb +39 -0
- data/lib/dry/view/renderer.rb +68 -0
- data/lib/dry/view/value_part.rb +50 -0
- data/lib/dry/view/version.rb +5 -0
- data/spec/fixtures/templates/hello.html.slim +1 -0
- data/spec/fixtures/templates/layouts/app.html.slim +6 -0
- data/spec/fixtures/templates/layouts/app.txt.erb +3 -0
- data/spec/fixtures/templates/shared/_index_table.html.slim +2 -0
- data/spec/fixtures/templates/shared/_shared_hello.html.slim +1 -0
- data/spec/fixtures/templates/tasks.html.slim +3 -0
- data/spec/fixtures/templates/user.html.slim +2 -0
- data/spec/fixtures/templates/users.html.slim +5 -0
- data/spec/fixtures/templates/users.txt.erb +5 -0
- data/spec/fixtures/templates/users/_row.html.slim +2 -0
- data/spec/fixtures/templates/users/_tbody.html.slim +5 -0
- data/spec/integration/view_spec.rb +72 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/layout_spec.rb +55 -0
- data/spec/unit/null_part_spec.rb +39 -0
- data/spec/unit/renderer_spec.rb +29 -0
- data/spec/unit/value_part_spec.rb +55 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 03a55b8f70f9a2c483db8b54a34cc46470b67824
|
4
|
+
data.tar.gz: 8bb2990860563bfd3efc8fcc42f2d08a2642b743
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 407e38a1b4c40c3cb95146218771f0099a61345abededc7cde29ecbfbcf1448288208786ef35cd463dcfe8c2d532c44fb57dc66790f62b53b42ba97b5726825e
|
7
|
+
data.tar.gz: 2c4c61cb1325de92fab40a23ede167b93be155f0e0981ff5ef5f51faee9126748756f3a03000962b136132936e78a29746a21371e08095b008a130d716968a63
|
data/.gitignore
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Documentation cache and generated files:
|
13
|
+
/.yardoc/
|
14
|
+
/_yardoc/
|
15
|
+
/doc/
|
16
|
+
/rdoc/
|
17
|
+
|
18
|
+
## Environment normalisation:
|
19
|
+
/.bundle/
|
20
|
+
/lib/bundler/man/
|
21
|
+
|
22
|
+
# For a library or gem, you might want to ignore these files since the code is
|
23
|
+
# intended to run in multiple environments; otherwise, check them in:
|
24
|
+
Gemfile.lock
|
25
|
+
.ruby-version
|
26
|
+
.rvmrc
|
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
language: ruby
|
2
|
+
sudo: false
|
3
|
+
cache: bundler
|
4
|
+
bundler_args: --without tools benchmarks
|
5
|
+
script:
|
6
|
+
- bundle exec rake spec
|
7
|
+
rvm:
|
8
|
+
- 2.0
|
9
|
+
- 2.1
|
10
|
+
- 2.2
|
11
|
+
- 2.3.0
|
12
|
+
- rbx-2
|
13
|
+
- jruby-9000
|
14
|
+
- ruby-head
|
15
|
+
- jruby-head
|
16
|
+
env:
|
17
|
+
global:
|
18
|
+
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
19
|
+
matrix:
|
20
|
+
allow_failures:
|
21
|
+
- rvm: ruby-head
|
22
|
+
- rvm: jruby-head
|
23
|
+
notifications:
|
24
|
+
email: false
|
25
|
+
webhooks:
|
26
|
+
urls:
|
27
|
+
- https://webhooks.gitter.im/e/19098b4253a72c9796db
|
28
|
+
on_success: change # options: [always|never|change] default: always
|
29
|
+
on_failure: always # options: [always|never|change] default: always
|
30
|
+
on_start: false # default: false
|
31
|
+
addons:
|
32
|
+
code_climate:
|
33
|
+
repo_token: c1c8f334714254acf5da901ca4da6afe24b15408074faae27159e3d14a450b62
|
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :test do
|
6
|
+
gem 'byebug', platform: :mri
|
7
|
+
gem 'slim'
|
8
|
+
|
9
|
+
gem 'codeclimate-test-reporter', platform: :rbx
|
10
|
+
end
|
11
|
+
|
12
|
+
group :tools do
|
13
|
+
gem 'pry'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :benchmarks do
|
17
|
+
gem 'benchmark-ips'
|
18
|
+
gem 'actionview'
|
19
|
+
end
|
data/LICENSE.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Piotr Solnica
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
6
|
+
furnished to do so, subject to the following conditions:
|
7
|
+
|
8
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
9
|
+
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
[gitter]: https://gitter.im/dry-rb/chat
|
2
|
+
[gem]: https://rubygems.org/gems/dry-view
|
3
|
+
[travis]: https://travis-ci.org/dry-rb/dry-view
|
4
|
+
[code_climate]: https://codeclimate.com/github/dry-rb/dry-view
|
5
|
+
[inch]: http://inch-ci.org/github/dry-rb/dry-view
|
6
|
+
|
7
|
+
# dry-view [![Join the Gitter chat](https://badges.gitter.im/Join%20Chat.svg)][gitter]
|
8
|
+
|
9
|
+
[![Gem Version](https://img.shields.io/gem/v/dry-view.svg)][gem]
|
10
|
+
[![Build Status](https://img.shields.io/travis/dry-rb/dry-view.svg)][travis]
|
11
|
+
[![Code Climate](https://img.shields.io/codeclimate/github/dry-rb/dry-view.svg)][code_climate]
|
12
|
+
[![Test Coverage](https://img.shields.io/codeclimate/coverage/github/dry-rb/dry-view.svg)][code_climate]
|
13
|
+
[![API Documentation Coverage](http://inch-ci.org/github/dry-rb/dry-view.svg)][inch]
|
14
|
+
|
15
|
+
Data-oriented view rendering system.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
<a href="/users/1">User</a>
|
data/benchmarks/view.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'benchmark/ips'
|
3
|
+
require 'dry/view/renderer'
|
4
|
+
require 'action_view'
|
5
|
+
|
6
|
+
class ActionRender
|
7
|
+
include ActionView::Helpers
|
8
|
+
|
9
|
+
def button
|
10
|
+
link_to('User', '/users/1')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
action_renderer = ActionRender.new
|
15
|
+
rodakase_renderer = Dry::View::Renderer.new(Pathname(__FILE__).dirname.join('templates'), engine: :erb)
|
16
|
+
|
17
|
+
template = rodakase_renderer.dir.join('button.erb')
|
18
|
+
|
19
|
+
SCOPE = {}
|
20
|
+
|
21
|
+
Benchmark.ips do |x|
|
22
|
+
x.report('actionview') { action_renderer.button }
|
23
|
+
x.report('rodakase') { rodakase_renderer.render(template, SCOPE) }
|
24
|
+
x.compare!
|
25
|
+
end
|
data/dry-view.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'dry/view/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "dry-view"
|
8
|
+
spec.version = Dry::View::VERSION
|
9
|
+
spec.authors = ["Piotr Solnica"]
|
10
|
+
spec.email = ["piotr.solnica@gmail.com"]
|
11
|
+
spec.summary = "Lightweight web application stack on top of Roda"
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/dry-rb/dry-view"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "inflecto", "~> 0"
|
22
|
+
spec.add_runtime_dependency "tilt", "~> 2.0"
|
23
|
+
spec.add_runtime_dependency "dry-configurable", "~> 0.1"
|
24
|
+
spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
27
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
29
|
+
spec.add_development_dependency "capybara", "~> 2.5"
|
30
|
+
end
|
data/lib/dry-view.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'dry/view'
|
data/lib/dry/view.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'dry-configurable'
|
2
|
+
require 'dry-equalizer'
|
3
|
+
require 'inflecto'
|
4
|
+
|
5
|
+
require 'dry/view/part'
|
6
|
+
require 'dry/view/value_part'
|
7
|
+
require 'dry/view/null_part'
|
8
|
+
require 'dry/view/renderer'
|
9
|
+
|
10
|
+
module Dry
|
11
|
+
module View
|
12
|
+
class Layout
|
13
|
+
include Dry::Equalizer(:config)
|
14
|
+
|
15
|
+
Scope = Struct.new(:page)
|
16
|
+
|
17
|
+
DEFAULT_DIR = 'layouts'.freeze
|
18
|
+
|
19
|
+
extend Dry::Configurable
|
20
|
+
|
21
|
+
setting :root
|
22
|
+
setting :name
|
23
|
+
setting :template
|
24
|
+
setting :formats, { html: :erb }
|
25
|
+
setting :scope
|
26
|
+
|
27
|
+
attr_reader :config, :scope, :layout_dir, :layout_path, :template_path,
|
28
|
+
:default_format
|
29
|
+
|
30
|
+
def self.renderer(format = default_format)
|
31
|
+
unless config.formats.key?(format.to_sym)
|
32
|
+
raise ArgumentError, "format +#{format}+ is not configured"
|
33
|
+
end
|
34
|
+
|
35
|
+
renderers[format]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.renderers
|
39
|
+
@renderers ||= Hash.new do |h, key|
|
40
|
+
h[key.to_sym] = Renderer.new(
|
41
|
+
config.root, format: key, engine: config.formats[key.to_sym]
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.default_format
|
47
|
+
config.formats.keys.first
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialize
|
51
|
+
@config = self.class.config
|
52
|
+
@default_format = self.class.default_format
|
53
|
+
@layout_dir = DEFAULT_DIR
|
54
|
+
@layout_path = "#{layout_dir}/#{config.name}"
|
55
|
+
@template_path = config.template
|
56
|
+
@scope = config.scope
|
57
|
+
end
|
58
|
+
|
59
|
+
def call(options = {})
|
60
|
+
renderer = self.class.renderer(options.fetch(:format, default_format))
|
61
|
+
|
62
|
+
renderer.(layout_path, layout_scope(options, renderer)) do
|
63
|
+
renderer.(template_path, template_scope(options, renderer))
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def locals(options)
|
68
|
+
options.fetch(:locals, {})
|
69
|
+
end
|
70
|
+
|
71
|
+
def parts(locals, renderer)
|
72
|
+
return empty_part(template_path, renderer) unless locals.any?
|
73
|
+
|
74
|
+
part_hash = locals.each_with_object({}) do |(key, value), result|
|
75
|
+
part =
|
76
|
+
case value
|
77
|
+
when Array
|
78
|
+
el_key = Inflecto.singularize(key).to_sym
|
79
|
+
|
80
|
+
template_part(
|
81
|
+
key, renderer,
|
82
|
+
value.map { |element| template_part(el_key, renderer, element) }
|
83
|
+
)
|
84
|
+
else
|
85
|
+
template_part(key, renderer, value)
|
86
|
+
end
|
87
|
+
|
88
|
+
result[key] = part
|
89
|
+
end
|
90
|
+
|
91
|
+
part(template_path, renderer, part_hash)
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def layout_scope(options, renderer)
|
97
|
+
Scope.new(layout_part(:page, renderer, options.fetch(:scope, scope)))
|
98
|
+
end
|
99
|
+
|
100
|
+
def template_scope(options, renderer)
|
101
|
+
parts(locals(options), renderer)
|
102
|
+
end
|
103
|
+
|
104
|
+
def layout_part(name, renderer, value)
|
105
|
+
part(layout_dir, renderer, { name => value })
|
106
|
+
end
|
107
|
+
|
108
|
+
def template_part(name, renderer, value)
|
109
|
+
part(template_path, renderer, { name => value })
|
110
|
+
end
|
111
|
+
|
112
|
+
def part(dir, renderer, value = {})
|
113
|
+
part_class = value.values[0] ? ValuePart : NullPart
|
114
|
+
part_class.new(renderer.chdir(dir), value)
|
115
|
+
end
|
116
|
+
|
117
|
+
def empty_part(dir, renderer)
|
118
|
+
Part.new(renderer.chdir(dir))
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'dry-equalizer'
|
2
|
+
require 'dry/view/value_part'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module View
|
6
|
+
class NullPart < ValuePart
|
7
|
+
def [](key)
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def respond_to_missing?(*)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def method_missing(meth, *args, &block)
|
20
|
+
template_path = template?("#{meth}_missing")
|
21
|
+
|
22
|
+
if template_path
|
23
|
+
render(template_path)
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'dry-equalizer'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module View
|
5
|
+
class Part
|
6
|
+
include Dry::Equalizer(:renderer)
|
7
|
+
|
8
|
+
attr_reader :renderer
|
9
|
+
|
10
|
+
def initialize(renderer)
|
11
|
+
@renderer = renderer
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(path, &block)
|
15
|
+
renderer.render(path, self, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def template?(name)
|
19
|
+
renderer.lookup("_#{name}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(meth, include_private = false)
|
23
|
+
super || template?(meth)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def method_missing(meth, *args, &block)
|
29
|
+
template_path = template?(meth)
|
30
|
+
|
31
|
+
if template_path
|
32
|
+
render(template_path, &block)
|
33
|
+
else
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
require 'dry-equalizer'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module View
|
6
|
+
class Renderer
|
7
|
+
include Dry::Equalizer(:dir, :root, :engine)
|
8
|
+
|
9
|
+
TemplateNotFoundError = Class.new(StandardError)
|
10
|
+
|
11
|
+
attr_reader :dir, :root, :format, :engine, :tilts
|
12
|
+
|
13
|
+
def self.tilts
|
14
|
+
@__engines__ ||= {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(dir, options = {})
|
18
|
+
@dir = dir
|
19
|
+
@root = options.fetch(:root, dir)
|
20
|
+
@format = options[:format]
|
21
|
+
@engine = options[:engine]
|
22
|
+
@tilts = self.class.tilts
|
23
|
+
end
|
24
|
+
|
25
|
+
def call(template, scope, &block)
|
26
|
+
path = lookup(template)
|
27
|
+
|
28
|
+
if path
|
29
|
+
render(path, scope, &block)
|
30
|
+
else
|
31
|
+
raise TemplateNotFoundError, "Template #{template} could not be looked up within #{root}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def render(path, scope, &block)
|
36
|
+
tilt(path).render(scope, &block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def tilt(path)
|
40
|
+
tilts.fetch(path) { tilts[path] = Tilt[engine].new(path) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def lookup(name)
|
44
|
+
template?(name) || template?("shared/#{name}") || !root? && chdir('..').lookup(name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def root?
|
48
|
+
dir == root
|
49
|
+
end
|
50
|
+
|
51
|
+
def template?(name)
|
52
|
+
template_path = path(name)
|
53
|
+
|
54
|
+
if File.exist?(template_path)
|
55
|
+
template_path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def path(name)
|
60
|
+
dir.join("#{name}.#{format}.#{engine}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def chdir(dirname)
|
64
|
+
self.class.new(dir.join(dirname), engine: engine, format: format, root: root)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'dry-equalizer'
|
2
|
+
require 'dry/view/part'
|
3
|
+
|
4
|
+
module Dry
|
5
|
+
module View
|
6
|
+
class ValuePart < Part
|
7
|
+
include Dry::Equalizer(:renderer, :_data, :_value)
|
8
|
+
|
9
|
+
attr_reader :_data, :_value
|
10
|
+
|
11
|
+
def initialize(renderer, data)
|
12
|
+
super(renderer)
|
13
|
+
@_data = data
|
14
|
+
@_value = data.values[0]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_s
|
18
|
+
_value.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](key)
|
22
|
+
_value[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(&block)
|
26
|
+
_value.each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def respond_to_missing?(meth, include_private = false)
|
30
|
+
_data.key?(meth) || super
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def method_missing(meth, *args, &block)
|
36
|
+
template_path = template?(meth)
|
37
|
+
|
38
|
+
if template_path
|
39
|
+
render(template_path, &block)
|
40
|
+
elsif _data.key?(meth)
|
41
|
+
_data[meth]
|
42
|
+
elsif _value.respond_to?(meth)
|
43
|
+
_value.public_send(meth, *args, &block)
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
h1 Hello
|
@@ -0,0 +1 @@
|
|
1
|
+
h1 Hello
|
@@ -0,0 +1,72 @@
|
|
1
|
+
RSpec.describe 'dry-view' do
|
2
|
+
let(:view_class) do
|
3
|
+
klass = Class.new(Dry::View::Layout)
|
4
|
+
|
5
|
+
klass.configure do |config|
|
6
|
+
config.root = SPEC_ROOT.join('fixtures/templates')
|
7
|
+
config.name = 'app'
|
8
|
+
config.template = 'users'
|
9
|
+
config.formats = {html: :slim, txt: :erb}
|
10
|
+
end
|
11
|
+
|
12
|
+
klass
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:scope) do
|
16
|
+
Struct.new(:title).new('dry-view rocks!')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'renders within a layout using provided scope' do
|
20
|
+
view = view_class.new
|
21
|
+
|
22
|
+
users = [
|
23
|
+
{ name: 'Jane', email: 'jane@doe.org' },
|
24
|
+
{ name: 'Joe', email: 'joe@doe.org' }
|
25
|
+
]
|
26
|
+
|
27
|
+
expect(view.(scope: scope, locals: { subtitle: "Users List", users: users })).to eql(
|
28
|
+
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><h2>Users List</h2><div class="users"><table><tbody><tr><td>Jane</td><td>jane@doe.org</td></tr><tr><td>Joe</td><td>joe@doe.org</td></tr></tbody></table></div></body></html>'
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'renders a view with an alternative format and engine' do
|
33
|
+
view = view_class.new
|
34
|
+
|
35
|
+
users = [
|
36
|
+
{ name: 'Jane', email: 'jane@doe.org' },
|
37
|
+
{ name: 'Joe', email: 'joe@doe.org' }
|
38
|
+
]
|
39
|
+
|
40
|
+
expect(view.(scope: scope, locals: { subtitle: 'Users List', users: users }, format: 'txt').strip).to eql(
|
41
|
+
"# dry-view rocks!\n\n## Users List\n\n* Jane (jane@doe.org)\n* Joe (joe@doe.org)"
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'inheritance' do
|
46
|
+
let(:parent_view) do
|
47
|
+
klass = Class.new(Dry::View::Layout)
|
48
|
+
|
49
|
+
klass.setting :root, SPEC_ROOT.join('fixtures/templates')
|
50
|
+
klass.setting :name, 'app'
|
51
|
+
klass.setting :formats, {html: :slim}
|
52
|
+
|
53
|
+
klass
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:child_view) do
|
57
|
+
Class.new(parent_view) do
|
58
|
+
configure do |config|
|
59
|
+
config.template = 'tasks'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'renders within a parent class layout using provided scope' do
|
65
|
+
view = child_view.new
|
66
|
+
|
67
|
+
expect(view.(scope: scope, locals: { tasks: [{ title: 'one' }, { title: 'two' }] })).to eql(
|
68
|
+
'<!DOCTYPE html><html><head><title>dry-view rocks!</title></head><body><ol><li>one</li><li>two</li></ol></body></html>'
|
69
|
+
)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
if RUBY_ENGINE == "rbx"
|
2
|
+
require "codeclimate-test-reporter"
|
3
|
+
CodeClimate::TestReporter.start
|
4
|
+
end
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'byebug'
|
8
|
+
rescue LoadError; end
|
9
|
+
|
10
|
+
SPEC_ROOT = Pathname(__FILE__).dirname
|
11
|
+
|
12
|
+
require 'dry-view'
|
13
|
+
require 'slim'
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.disable_monkey_patching!
|
17
|
+
|
18
|
+
config.order = :random
|
19
|
+
Kernel.srand config.seed
|
20
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
RSpec.describe Dry::View::Layout do
|
2
|
+
subject(:layout) { layout_class.new }
|
3
|
+
|
4
|
+
let(:layout_class) do
|
5
|
+
klass = Class.new(Dry::View::Layout)
|
6
|
+
|
7
|
+
klass.configure do |config|
|
8
|
+
config.root = SPEC_ROOT.join('fixtures/templates')
|
9
|
+
config.name = 'app'
|
10
|
+
config.template = 'user'
|
11
|
+
config.formats = {html: :slim}
|
12
|
+
end
|
13
|
+
|
14
|
+
klass
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:page) do
|
18
|
+
double(:page, title: 'Test')
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:options) do
|
22
|
+
{ scope: page, locals: { user: { name: 'Jane' }, header: { title: 'User' } } }
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:renderer) do
|
26
|
+
layout.class.renderers[:html]
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#call' do
|
30
|
+
it 'renders template within the layout' do
|
31
|
+
expect(layout.(options)).to eql(
|
32
|
+
'<!DOCTYPE html><html><head><title>Test</title></head><body><h1>User</h1><p>Jane</p></body></html>'
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#parts' do
|
38
|
+
it 'returns view parts' do
|
39
|
+
part = layout.parts({ user: { id: 1, name: 'Jane' } }, renderer)
|
40
|
+
|
41
|
+
expect(part[:id]).to be(1)
|
42
|
+
expect(part[:name]).to eql('Jane')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'builds null parts for nil values' do
|
46
|
+
part = layout.parts({ user: nil }, renderer)
|
47
|
+
|
48
|
+
expect(part[:id]).to be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns empty part when no locals are passed' do
|
52
|
+
expect(layout.parts({}, renderer)).to be_instance_of(Dry::View::Part)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'dry/view/null_part'
|
2
|
+
|
3
|
+
RSpec.describe Dry::View::NullPart do
|
4
|
+
subject(:part) do
|
5
|
+
Dry::View::NullPart.new(renderer, data)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:name) { :user }
|
9
|
+
let(:data) { { user: nil } }
|
10
|
+
|
11
|
+
let(:renderer) { double(:renderer) }
|
12
|
+
|
13
|
+
describe '#[]' do
|
14
|
+
it 'returns nil for any data value names' do
|
15
|
+
expect(part[:email]).to eql(nil)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#method_missing' do
|
20
|
+
it 'renders a template with the _missing suffix' do
|
21
|
+
expect(renderer).to receive(:lookup).with('_row_missing').and_return('_row_missing.slim')
|
22
|
+
expect(renderer).to receive(:render).with('_row_missing.slim', part)
|
23
|
+
|
24
|
+
part.row
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'renders a _missing template within another when block is passed' do
|
28
|
+
block = proc { part.fields }
|
29
|
+
|
30
|
+
expect(renderer).to receive(:lookup).with('_form_missing').and_return('form_missing.slim')
|
31
|
+
expect(renderer).to receive(:lookup).with('_fields_missing').and_return('fields_missing.slim')
|
32
|
+
|
33
|
+
expect(renderer).to receive(:render).with('form_missing.slim', part, &block)
|
34
|
+
expect(renderer).to receive(:render).with('fields_missing.slim', part)
|
35
|
+
|
36
|
+
part.form(block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'dry/view/renderer'
|
2
|
+
|
3
|
+
RSpec.describe Dry::View::Renderer do
|
4
|
+
subject(:renderer) do
|
5
|
+
Dry::View::Renderer.new(SPEC_ROOT.join('fixtures/templates'), format: 'html', engine: :slim)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:scope) { double(:scope) }
|
9
|
+
|
10
|
+
describe '#call' do
|
11
|
+
it 'renders template' do
|
12
|
+
expect(renderer.('hello', scope)).to eql('<h1>Hello</h1>')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'looks up shared template in current dir' do
|
16
|
+
expect(renderer.('_shared_hello', scope)).to eql('<h1>Hello</h1>')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'looks up shared template in upper dir' do
|
20
|
+
expect(renderer.chdir('greetings').('_shared_hello', scope)).to eql('<h1>Hello</h1>')
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises error when template was not found' do
|
24
|
+
expect {
|
25
|
+
renderer.('not_found', scope)
|
26
|
+
}.to raise_error(Dry::View::Renderer::TemplateNotFoundError, /not_found/)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'dry/view/part'
|
2
|
+
|
3
|
+
RSpec.describe Dry::View::ValuePart do
|
4
|
+
subject(:part) do
|
5
|
+
Dry::View::ValuePart.new(renderer, data)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:name) { :user }
|
9
|
+
let(:data) { { user: { email: 'jane@doe.org' } } }
|
10
|
+
|
11
|
+
let(:renderer) { double(:renderer) }
|
12
|
+
|
13
|
+
describe '#[]' do
|
14
|
+
it 'gives access to data values' do
|
15
|
+
expect(part[:email]).to eql('jane@doe.org')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#render' do
|
20
|
+
it 'renders given template' do
|
21
|
+
expect(renderer).to receive(:render).with('row.slim', part)
|
22
|
+
|
23
|
+
part.render('row.slim')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#template?' do
|
28
|
+
it 'asks renderer if there is a valid template for a given identifier' do
|
29
|
+
expect(renderer).to receive(:lookup).with('_row').and_return('row.slim')
|
30
|
+
|
31
|
+
expect(part.template?('row')).to eql('row.slim')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#method_missing' do
|
36
|
+
it 'renders template' do
|
37
|
+
expect(renderer).to receive(:lookup).with('_row').and_return('_row.slim')
|
38
|
+
expect(renderer).to receive(:render).with('_row.slim', part)
|
39
|
+
|
40
|
+
part.row
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'renders template within another when block is passed' do
|
44
|
+
block = proc { part.fields }
|
45
|
+
|
46
|
+
expect(renderer).to receive(:lookup).with('_form').and_return('form.slim')
|
47
|
+
expect(renderer).to receive(:lookup).with('_fields').and_return('fields.slim')
|
48
|
+
|
49
|
+
expect(renderer).to receive(:render).with('form.slim', part, &block)
|
50
|
+
expect(renderer).to receive(:render).with('fields.slim', part)
|
51
|
+
|
52
|
+
part.form(block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dry-view
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Piotr Solnica
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: inflecto
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: tilt
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-configurable
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.1'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dry-equalizer
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.1'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.1'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: capybara
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2.5'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2.5'
|
125
|
+
description: Lightweight web application stack on top of Roda
|
126
|
+
email:
|
127
|
+
- piotr.solnica@gmail.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".gitignore"
|
133
|
+
- ".rspec"
|
134
|
+
- ".travis.yml"
|
135
|
+
- Gemfile
|
136
|
+
- LICENSE.md
|
137
|
+
- README.md
|
138
|
+
- Rakefile
|
139
|
+
- benchmarks/templates/button.erb
|
140
|
+
- benchmarks/view.rb
|
141
|
+
- dry-view.gemspec
|
142
|
+
- lib/dry-view.rb
|
143
|
+
- lib/dry/view.rb
|
144
|
+
- lib/dry/view/layout.rb
|
145
|
+
- lib/dry/view/null_part.rb
|
146
|
+
- lib/dry/view/part.rb
|
147
|
+
- lib/dry/view/renderer.rb
|
148
|
+
- lib/dry/view/value_part.rb
|
149
|
+
- lib/dry/view/version.rb
|
150
|
+
- spec/fixtures/templates/hello.html.slim
|
151
|
+
- spec/fixtures/templates/layouts/app.html.slim
|
152
|
+
- spec/fixtures/templates/layouts/app.txt.erb
|
153
|
+
- spec/fixtures/templates/shared/_index_table.html.slim
|
154
|
+
- spec/fixtures/templates/shared/_shared_hello.html.slim
|
155
|
+
- spec/fixtures/templates/tasks.html.slim
|
156
|
+
- spec/fixtures/templates/user.html.slim
|
157
|
+
- spec/fixtures/templates/users.html.slim
|
158
|
+
- spec/fixtures/templates/users.txt.erb
|
159
|
+
- spec/fixtures/templates/users/_row.html.slim
|
160
|
+
- spec/fixtures/templates/users/_tbody.html.slim
|
161
|
+
- spec/integration/view_spec.rb
|
162
|
+
- spec/spec_helper.rb
|
163
|
+
- spec/unit/layout_spec.rb
|
164
|
+
- spec/unit/null_part_spec.rb
|
165
|
+
- spec/unit/renderer_spec.rb
|
166
|
+
- spec/unit/value_part_spec.rb
|
167
|
+
homepage: https://github.com/dry-rb/dry-view
|
168
|
+
licenses:
|
169
|
+
- MIT
|
170
|
+
metadata: {}
|
171
|
+
post_install_message:
|
172
|
+
rdoc_options: []
|
173
|
+
require_paths:
|
174
|
+
- lib
|
175
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
version: '0'
|
185
|
+
requirements: []
|
186
|
+
rubyforge_project:
|
187
|
+
rubygems_version: 2.5.1
|
188
|
+
signing_key:
|
189
|
+
specification_version: 4
|
190
|
+
summary: Lightweight web application stack on top of Roda
|
191
|
+
test_files:
|
192
|
+
- spec/fixtures/templates/hello.html.slim
|
193
|
+
- spec/fixtures/templates/layouts/app.html.slim
|
194
|
+
- spec/fixtures/templates/layouts/app.txt.erb
|
195
|
+
- spec/fixtures/templates/shared/_index_table.html.slim
|
196
|
+
- spec/fixtures/templates/shared/_shared_hello.html.slim
|
197
|
+
- spec/fixtures/templates/tasks.html.slim
|
198
|
+
- spec/fixtures/templates/user.html.slim
|
199
|
+
- spec/fixtures/templates/users.html.slim
|
200
|
+
- spec/fixtures/templates/users.txt.erb
|
201
|
+
- spec/fixtures/templates/users/_row.html.slim
|
202
|
+
- spec/fixtures/templates/users/_tbody.html.slim
|
203
|
+
- spec/integration/view_spec.rb
|
204
|
+
- spec/spec_helper.rb
|
205
|
+
- spec/unit/layout_spec.rb
|
206
|
+
- spec/unit/null_part_spec.rb
|
207
|
+
- spec/unit/renderer_spec.rb
|
208
|
+
- spec/unit/value_part_spec.rb
|