dry-view 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +18 -0
  3. data/.travis.yml +15 -10
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +60 -1
  6. data/Gemfile +15 -5
  7. data/README.md +38 -13
  8. data/bin/setup +5 -0
  9. data/bin/setup_helpers.rb +27 -0
  10. data/dry-view.gemspec +8 -9
  11. data/lib/dry-view.rb +3 -1
  12. data/lib/dry/view.rb +503 -2
  13. data/lib/dry/view/context.rb +80 -0
  14. data/lib/dry/view/decorated_attributes.rb +81 -0
  15. data/lib/dry/view/exposure.rb +15 -2
  16. data/lib/dry/view/exposures.rb +15 -5
  17. data/lib/dry/view/part.rb +154 -61
  18. data/lib/dry/view/part_builder.rb +136 -0
  19. data/lib/dry/view/path.rb +22 -5
  20. data/lib/dry/view/render_environment.rb +62 -0
  21. data/lib/dry/view/render_environment_missing.rb +44 -0
  22. data/lib/dry/view/rendered.rb +55 -0
  23. data/lib/dry/view/renderer.rb +22 -19
  24. data/lib/dry/view/scope.rb +146 -14
  25. data/lib/dry/view/scope_builder.rb +98 -0
  26. data/lib/dry/view/tilt.rb +78 -0
  27. data/lib/dry/view/tilt/erb.rb +26 -0
  28. data/lib/dry/view/tilt/erbse.rb +21 -0
  29. data/lib/dry/view/tilt/haml.rb +26 -0
  30. data/lib/dry/view/version.rb +5 -2
  31. metadata +50 -88
  32. data/benchmarks/templates/button.html.erb +0 -1
  33. data/benchmarks/view.rb +0 -24
  34. data/lib/dry/view/controller.rb +0 -159
  35. data/lib/dry/view/decorator.rb +0 -45
  36. data/lib/dry/view/missing_renderer.rb +0 -15
  37. data/spec/fixtures/templates/_hello.html.slim +0 -1
  38. data/spec/fixtures/templates/controller_renderer_options.html.erb +0 -3
  39. data/spec/fixtures/templates/decorated_parts.html.slim +0 -4
  40. data/spec/fixtures/templates/edit.html.slim +0 -11
  41. data/spec/fixtures/templates/empty.html.slim +0 -1
  42. data/spec/fixtures/templates/greeting.html.slim +0 -2
  43. data/spec/fixtures/templates/hello.html.slim +0 -1
  44. data/spec/fixtures/templates/layouts/app.html.slim +0 -6
  45. data/spec/fixtures/templates/layouts/app.txt.erb +0 -3
  46. data/spec/fixtures/templates/parts_with_args.html.slim +0 -3
  47. data/spec/fixtures/templates/parts_with_args/_box.html.slim +0 -3
  48. data/spec/fixtures/templates/shared/_index_table.html.slim +0 -2
  49. data/spec/fixtures/templates/shared/_shared_hello.html.slim +0 -1
  50. data/spec/fixtures/templates/tasks.html.slim +0 -3
  51. data/spec/fixtures/templates/user.html.slim +0 -2
  52. data/spec/fixtures/templates/users.html.slim +0 -5
  53. data/spec/fixtures/templates/users.txt.erb +0 -3
  54. data/spec/fixtures/templates/users/_row.html.slim +0 -2
  55. data/spec/fixtures/templates/users/_tbody.html.slim +0 -5
  56. data/spec/fixtures/templates/users_with_count.html.slim +0 -5
  57. data/spec/fixtures/templates/users_with_count_inherit.html.slim +0 -6
  58. data/spec/fixtures/templates_override/_hello.html.slim +0 -1
  59. data/spec/fixtures/templates_override/users.html.slim +0 -5
  60. data/spec/integration/decorator_spec.rb +0 -80
  61. data/spec/integration/exposures_spec.rb +0 -392
  62. data/spec/integration/part/decorated_attributes_spec.rb +0 -193
  63. data/spec/integration/view_spec.rb +0 -133
  64. data/spec/spec_helper.rb +0 -46
  65. data/spec/unit/controller_spec.rb +0 -83
  66. data/spec/unit/decorator_spec.rb +0 -61
  67. data/spec/unit/exposure_spec.rb +0 -227
  68. data/spec/unit/exposures_spec.rb +0 -103
  69. data/spec/unit/part_spec.rb +0 -104
  70. data/spec/unit/renderer_spec.rb +0 -57
  71. data/spec/unit/scope_spec.rb +0 -53
@@ -1,103 +0,0 @@
1
- RSpec.describe Dry::View::Exposures do
2
- subject(:exposures) { described_class.new }
3
-
4
- describe "#exposures" do
5
- it "is empty by defalut" do
6
- expect(exposures.exposures).to be_empty
7
- end
8
- end
9
-
10
- describe "#add" do
11
- it "creates and adds an exposure" do
12
- proc = -> **input { "hi" }
13
- exposures.add :hello, proc
14
-
15
- expect(exposures[:hello].name).to eq :hello
16
- expect(exposures[:hello].proc).to eq proc
17
- end
18
- end
19
-
20
- describe "#bind" do
21
- subject(:bound_exposures) { exposures.bind(object) }
22
-
23
- let(:object) do
24
- Class.new do
25
- def hello(input)
26
- "hi"
27
- end
28
- end.new
29
- end
30
-
31
- before do
32
- exposures.add(:hello)
33
- end
34
-
35
- it "binds each of the exposures" do
36
- expect(bound_exposures[:hello].proc).to eq object.method(:hello)
37
- end
38
-
39
- it "returns a new copy of the exposures" do
40
- expect(exposures.exposures).not_to eql(bound_exposures.exposures)
41
- end
42
- end
43
-
44
- describe "#locals" do
45
- before do
46
- exposures.add(:greeting, -> greeting: { greeting.upcase })
47
- exposures.add(:farewell, -> greeting { "#{greeting} and goodbye" })
48
- end
49
-
50
- subject(:locals) { exposures.locals(greeting: "hello") }
51
-
52
- it "returns the values from the exposures' procs" do
53
- expect(locals).to eq(greeting: "HELLO", farewell: "HELLO and goodbye")
54
- end
55
-
56
- it "does not return any values from private exposures" do
57
- exposures.add(:hidden, -> **input { "shh" }, private: true)
58
-
59
- expect(locals).to include(:greeting, :farewell)
60
- expect(locals).not_to include(:hidden)
61
- end
62
- end
63
-
64
- describe "#locals default value" do
65
- it "returns 'default_value' from exposure" do
66
- exposures.add(:name, default: 'John')
67
- locals = exposures.locals({})
68
-
69
- expect(locals).to eq(:name=>"John")
70
- end
71
-
72
- it "returns values from arguments" do
73
- exposures.add(:name, default: 'John')
74
- locals = exposures.locals(name: 'William')
75
-
76
- expect(locals).to eq(:name=>"William")
77
- end
78
-
79
- it "returns values from arguments even when value is nil" do
80
- exposures.add(:name, default: 'John')
81
- locals = exposures.locals(name: nil)
82
-
83
- expect(locals).to eq(:name=>nil)
84
- end
85
-
86
- it "returns value from proc" do
87
- exposures.add(:name, -> name: { name.upcase }, default: 'John')
88
- locals = exposures.locals(name: 'William')
89
-
90
- expect(locals).to eq(:name=>"WILLIAM")
91
- end
92
- end
93
-
94
- describe "#import" do
95
- it "imports a exposure to the exposures" do
96
- exposures_b = described_class.new
97
- exposures.add(:name, -> name: { name.upcase }, default: 'John')
98
- exposures_b.import(:name, exposures[:name])
99
-
100
- expect(exposures_b[:name]).to eq(exposures[:name])
101
- end
102
- end
103
- end
@@ -1,104 +0,0 @@
1
- RSpec::Matchers.define :template_scope do |locals|
2
- match do |actual|
3
- locals == locals.map { |k,v| [k, actual.send(k)] }.to_h
4
- end
5
- end
6
-
7
- RSpec.describe Dry::View::Part do
8
- context 'with a renderer' do
9
- subject(:part) { described_class.new(name: name, value: value, renderer: renderer, context: context) }
10
-
11
- let(:name) { :user }
12
- let(:value) { double(:value) }
13
- let(:context) { double(:context) }
14
- let(:renderer) { spy(:renderer) }
15
-
16
- describe '#render' do
17
- it 'renders a partial with the part available in its scope' do
18
- part.render(:info)
19
- expect(renderer).to have_received(:partial).with(:info, template_scope(user: part))
20
- end
21
-
22
- it 'allows the part to be made available on a different name' do
23
- part.render(:info, as: :admin)
24
- expect(renderer).to have_received(:partial).with(:info, template_scope(admin: part))
25
- end
26
-
27
- it 'includes extra locals in the scope' do
28
- part.render(:info, extra_local: "hello")
29
- expect(renderer).to have_received(:partial).with(:info, template_scope(user: part, extra_local: "hello"))
30
- end
31
- end
32
-
33
- describe '#to_s' do
34
- before do
35
- allow(value).to receive(:to_s).and_return 'to_s on the value'
36
- end
37
-
38
- it 'delegates to the wrapped value' do
39
- expect(part.to_s).to eq 'to_s on the value'
40
- end
41
- end
42
-
43
- describe '#new' do
44
- it 'preserves decorator, renderer, and context' do
45
- new_part = part.new(value: 'new value')
46
-
47
- expect(new_part._decorator).to eql part._decorator
48
- expect(new_part._renderer).to eql part._renderer
49
- expect(new_part._context).to eql part._context
50
- end
51
- end
52
-
53
- describe '#method_missing' do
54
- let(:value) { double(greeting: 'hello from value') }
55
-
56
- it 'calls a matching method on the value' do
57
- expect(part.greeting).to eq 'hello from value'
58
- end
59
-
60
- it 'forwards all arguments to the method' do
61
- blk = -> { }
62
- part.greeting 'args', &blk
63
-
64
- expect(value).to have_received(:greeting).with('args', &blk)
65
- end
66
-
67
- it 'raises an error if no metho matches' do
68
- expect { part.farewell }.to raise_error(NoMethodError)
69
- end
70
- end
71
-
72
- describe '#respond_to_missing?' do
73
- let(:value) { double(greeting: 'hello from value') }
74
-
75
- it 'handles convenience methods' do
76
- expect(part).to respond_to(:context)
77
- expect(part).to respond_to(:render)
78
- expect(part).to respond_to(:value)
79
- end
80
-
81
- it 'handles value methods' do
82
- expect(part).to respond_to(:greeting)
83
- end
84
- end
85
- end
86
-
87
- context 'without a renderer' do
88
- subject(:part) { described_class.new(name: name, value: value, context: context) }
89
-
90
- let(:name) { :user }
91
- let(:value) { double('value') }
92
- let(:context) { double('context') }
93
-
94
- describe '#initialize' do
95
- it 'can be initialized' do
96
- expect(part).to be_an_instance_of(Dry::View::Part)
97
- end
98
-
99
- it 'raises an exception when render is called' do
100
- expect { part.render(:info) }.to raise_error(Dry::View::MissingRendererError).with_message('No renderer provided')
101
- end
102
- end
103
- end
104
- end
@@ -1,57 +0,0 @@
1
- require 'dry/view/path'
2
- require 'dry/view/renderer'
3
-
4
- RSpec.describe Dry::View::Renderer do
5
- subject(:renderer) do
6
- Dry::View::Renderer.new(
7
- [Dry::View::Path.new(SPEC_ROOT.join('fixtures/templates'))],
8
- format: 'html'
9
- )
10
- end
11
-
12
- let(:scope) { double(:scope) }
13
-
14
- describe '#template' do
15
- it 'renders template in current directory' do
16
- expect(renderer.template(:hello, scope)).to eql('<h1>Hello</h1>')
17
- end
18
-
19
- it 'renders template in shared/ subdirectory' do
20
- expect(renderer.template(:_shared_hello, scope)).to eql('<h1>Hello</h1>')
21
- end
22
-
23
- it 'renders template in upper directory' do
24
- expect(renderer.chdir('nested').template(:_shared_hello, scope)).to eql('<h1>Hello</h1>')
25
- end
26
-
27
- it 'raises error when template cannot be found' do
28
- expect {
29
- renderer.template(:missing_template, scope)
30
- }.to raise_error(Dry::View::Renderer::TemplateNotFoundError, /missing_template/)
31
- end
32
- end
33
-
34
- describe '#partial' do
35
- it 'renders partial in current directory' do
36
- expect(renderer.partial(:hello, scope)).to eql('<h1>Partial hello</h1>')
37
- end
38
-
39
- it 'renders partial in shared/ subdirectory' do
40
- expect(renderer.partial(:shared_hello, scope)).to eql('<h1>Hello</h1>')
41
- end
42
-
43
- it 'renders partial in upper directory' do
44
- expect(renderer.chdir('nested').partial(:hello, scope)).to eql('<h1>Partial hello</h1>')
45
- end
46
-
47
- it 'renders partial in upper shared/ subdirectory' do
48
- expect(renderer.chdir('nested').partial(:shared_hello, scope)).to eql('<h1>Hello</h1>')
49
- end
50
-
51
- it 'raises error when partial cannot be found' do
52
- expect {
53
- renderer.partial(:missing_partial, scope)
54
- }.to raise_error(Dry::View::Renderer::TemplateNotFoundError, /_missing_partial/)
55
- end
56
- end
57
- end
@@ -1,53 +0,0 @@
1
- RSpec.describe Dry::View::Scope do
2
- subject(:scope) { described_class.new(renderer: renderer, context: context, locals: locals) }
3
-
4
- let(:locals) { {} }
5
- let(:context) { double(:context) }
6
- let(:renderer) { spy(:renderer) }
7
-
8
- describe '#render' do
9
- it 'renders a partial with itself as the scope' do
10
- scope.render(:info)
11
- expect(renderer).to have_received(:partial).with(:info, scope)
12
- end
13
-
14
- it 'renders a partial with provided locals' do
15
- scope_with_locals = described_class.new(renderer: renderer, context: context, locals: {foo: 'bar'})
16
-
17
- scope.render(:info, foo: 'bar')
18
- expect(renderer).to have_received(:partial).with(:info, scope_with_locals)
19
- end
20
- end
21
-
22
- describe '#method_missing' do
23
- context 'matching locals' do
24
- let(:locals) { {greeting: 'hello from locals'} }
25
- let(:context) { double('context', greeting: 'hello from context') }
26
-
27
- it 'returns a matching value from the locals, in favour of a matching method on the context' do
28
- expect(scope.greeting).to eq 'hello from locals'
29
- end
30
- end
31
-
32
- context 'matching context' do
33
- let(:context) { double('context', greeting: 'hello from context') }
34
-
35
- it 'calls the matching method on the context' do
36
- expect(scope.greeting).to eq 'hello from context'
37
- end
38
-
39
- it 'forwards all arguments to the method' do
40
- blk = -> { }
41
- scope.greeting 'args', &blk
42
-
43
- expect(context).to have_received(:greeting).with('args', &blk)
44
- end
45
- end
46
-
47
- describe 'no matches' do
48
- it 'raises an error' do
49
- expect { scope.greeting }.to raise_error(NoMethodError)
50
- end
51
- end
52
- end
53
- end