curly-templates 2.0.1 → 2.1.0.beta1
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/CHANGELOG.md +6 -0
- data/Gemfile +2 -0
- data/README.md +85 -5
- data/curly-templates.gemspec +37 -8
- data/lib/curly.rb +1 -1
- data/lib/curly/{attribute_parser.rb → attribute_scanner.rb} +6 -4
- data/lib/curly/compiler.rb +81 -72
- data/lib/curly/component_compiler.rb +37 -31
- data/lib/curly/component_scanner.rb +19 -0
- data/lib/curly/incomplete_block_error.rb +0 -7
- data/lib/curly/incorrect_ending_error.rb +0 -21
- data/lib/curly/parser.rb +171 -0
- data/lib/curly/presenter.rb +1 -1
- data/lib/curly/scanner.rb +23 -9
- data/spec/attribute_scanner_spec.rb +46 -0
- data/spec/collection_blocks_spec.rb +88 -0
- data/spec/compiler/context_blocks_spec.rb +42 -0
- data/spec/component_compiler_spec.rb +26 -77
- data/spec/component_scanner_spec.rb +19 -0
- data/spec/{integration/components_spec.rb → components_spec.rb} +0 -0
- data/spec/{integration/conditional_blocks_spec.rb → conditional_blocks_spec.rb} +0 -0
- data/spec/dummy/.gitignore +1 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/controllers/dashboards_controller.rb +13 -0
- data/spec/dummy/app/helpers/application_helper.rb +5 -0
- data/spec/dummy/app/presenters/dashboards/collection_presenter.rb +7 -0
- data/spec/dummy/app/presenters/dashboards/item_presenter.rb +7 -0
- data/spec/dummy/app/presenters/dashboards/new_presenter.rb +19 -0
- data/spec/dummy/app/presenters/dashboards/partials_presenter.rb +5 -0
- data/spec/dummy/app/presenters/dashboards/show_presenter.rb +12 -0
- data/spec/dummy/app/presenters/layouts/application_presenter.rb +9 -0
- data/spec/dummy/app/views/dashboards/_item.html.curly +1 -0
- data/spec/dummy/app/views/dashboards/collection.html.curly +5 -0
- data/spec/dummy/app/views/dashboards/new.html.curly +3 -0
- data/spec/dummy/app/views/dashboards/partials.html.curly +3 -0
- data/spec/dummy/app/views/dashboards/show.html.curly +3 -0
- data/spec/dummy/app/views/layouts/application.html.curly +8 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +12 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/integration/application_layout_spec.rb +21 -0
- data/spec/integration/collection_blocks_spec.rb +17 -78
- data/spec/integration/context_blocks_spec.rb +21 -0
- data/spec/integration/partials_spec.rb +23 -0
- data/spec/parser_spec.rb +95 -0
- data/spec/scanner_spec.rb +24 -14
- data/spec/spec_helper.rb +4 -3
- metadata +49 -14
- data/lib/curly/component_parser.rb +0 -13
- data/spec/attribute_parser_spec.rb +0 -46
- data/spec/incorrect_ending_error_spec.rb +0 -13
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Collection block components" do
|
4
|
+
include RenderingSupport
|
5
|
+
|
6
|
+
before do
|
7
|
+
item_presenter do
|
8
|
+
presents :item
|
9
|
+
|
10
|
+
def name
|
11
|
+
@item
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
example "with neither identifier nor attributes" do
|
17
|
+
presenter do
|
18
|
+
def items
|
19
|
+
["one", "two", "three"]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
render("{{*items}}<{{name}}>{{/items}}").should == "<one><two><three>"
|
24
|
+
end
|
25
|
+
|
26
|
+
example "with an identifier" do
|
27
|
+
presenter do
|
28
|
+
def items(filter = nil)
|
29
|
+
if filter == "even"
|
30
|
+
["two"]
|
31
|
+
elsif filter == "odd"
|
32
|
+
["one", "three"]
|
33
|
+
else
|
34
|
+
["one", "two", "three"]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
render("{{*items.even}}<{{name}}>{{/items.even}}").should == "<two>"
|
40
|
+
render("{{*items.odd}}<{{name}}>{{/items.odd}}").should == "<one><three>"
|
41
|
+
render("{{*items}}<{{name}}>{{/items}}").should == "<one><two><three>"
|
42
|
+
end
|
43
|
+
|
44
|
+
example "with attributes" do
|
45
|
+
presenter do
|
46
|
+
def items(length: "1")
|
47
|
+
["x"] * length.to_i
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
render("{{*items length=3}}<{{name}}>{{/items}}").should == "<x><x><x>"
|
52
|
+
render("{{*items}}<{{name}}>{{/items}}").should == "<x>"
|
53
|
+
end
|
54
|
+
|
55
|
+
example "with nested collection blocks" do
|
56
|
+
presenter do
|
57
|
+
def items
|
58
|
+
[{ parts: [1, 2] }, { parts: [3, 4] }]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
item_presenter do
|
63
|
+
presents :item
|
64
|
+
|
65
|
+
def parts
|
66
|
+
@item[:parts]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
part_presenter do
|
71
|
+
presents :part
|
72
|
+
|
73
|
+
def number
|
74
|
+
@part
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
render("{{*items}}<{{*parts}}[{{number}}]{{/parts}}>{{/items}}").should == "<[1][2]><[3][4]>"
|
79
|
+
end
|
80
|
+
|
81
|
+
def item_presenter(&block)
|
82
|
+
stub_const("ItemPresenter", Class.new(Curly::Presenter, &block))
|
83
|
+
end
|
84
|
+
|
85
|
+
def part_presenter(&block)
|
86
|
+
stub_const("ItemPresenter::PartPresenter", Class.new(Curly::Presenter, &block))
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Curly::Compiler do
|
4
|
+
include CompilationSupport
|
5
|
+
|
6
|
+
let(:presenter_class) do
|
7
|
+
Class.new(Curly::Presenter) do
|
8
|
+
def form(&block)
|
9
|
+
"<form>".html_safe + block.call("yo") + "</form>".html_safe
|
10
|
+
end
|
11
|
+
|
12
|
+
def invalid
|
13
|
+
"uh oh!"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:context_presenter_class) do
|
19
|
+
Class.new(Curly::Presenter) do
|
20
|
+
presents :form
|
21
|
+
|
22
|
+
def text
|
23
|
+
@form.upcase
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:context) { double("context") }
|
29
|
+
let(:presenter) { presenter_class.new(context, {}) }
|
30
|
+
|
31
|
+
before do
|
32
|
+
stub_const("FormPresenter", context_presenter_class)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "compiles context blocks" do
|
36
|
+
evaluate('{{@form}}{{text}}{{/form}}').should == '<form>YO</form>'
|
37
|
+
end
|
38
|
+
|
39
|
+
it "fails if the component is not a context block" do
|
40
|
+
expect { evaluate('{{@invalid}}yo{{/invalid}}') }.to raise_exception(Curly::Error)
|
41
|
+
end
|
42
|
+
end
|
@@ -1,71 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Curly::ComponentCompiler do
|
4
|
-
describe ".
|
5
|
-
let(:presenter_class) do
|
6
|
-
Class.new do
|
7
|
-
def monday?
|
8
|
-
true
|
9
|
-
end
|
10
|
-
|
11
|
-
def tuesday?
|
12
|
-
false
|
13
|
-
end
|
14
|
-
|
15
|
-
def day?(name)
|
16
|
-
name == "monday"
|
17
|
-
end
|
18
|
-
|
19
|
-
def season?(name:)
|
20
|
-
name == "summer"
|
21
|
-
end
|
22
|
-
|
23
|
-
def hello
|
24
|
-
"hello"
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.component_available?(name)
|
28
|
-
true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
it "compiles simple components" do
|
34
|
-
evaluate("monday?").should == true
|
35
|
-
evaluate("tuesday?").should == false
|
36
|
-
end
|
37
|
-
|
38
|
-
it "compiles components with an identifier" do
|
39
|
-
evaluate("day.monday?").should == true
|
40
|
-
evaluate("day.tuesday?").should == false
|
41
|
-
end
|
42
|
-
|
43
|
-
it "compiles components with attributes" do
|
44
|
-
evaluate("season? name=summer").should == true
|
45
|
-
evaluate("season? name=winter").should == false
|
46
|
-
end
|
47
|
-
|
48
|
-
it "fails if the component is missing a question mark" do
|
49
|
-
expect { evaluate("hello") }.to raise_exception(Curly::Error)
|
50
|
-
end
|
51
|
-
|
52
|
-
def evaluate(component, &block)
|
53
|
-
method, argument, attributes = Curly::ComponentParser.parse(component)
|
54
|
-
code = Curly::ComponentCompiler.compile_conditional(presenter_class, method, argument, attributes)
|
55
|
-
presenter = presenter_class.new
|
56
|
-
context = double("context", presenter: presenter)
|
57
|
-
|
58
|
-
context.instance_eval(<<-RUBY)
|
59
|
-
def self.render
|
60
|
-
#{code}
|
61
|
-
end
|
62
|
-
RUBY
|
63
|
-
|
64
|
-
context.render(&block)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
describe ".compile_component" do
|
4
|
+
describe ".compile" do
|
69
5
|
let(:presenter_class) do
|
70
6
|
Class.new do
|
71
7
|
def title
|
@@ -95,6 +31,10 @@ describe Curly::ComponentCompiler do
|
|
95
31
|
s
|
96
32
|
end
|
97
33
|
|
34
|
+
def form(&block)
|
35
|
+
"some form"
|
36
|
+
end
|
37
|
+
|
98
38
|
def self.component_available?(name)
|
99
39
|
true
|
100
40
|
end
|
@@ -118,6 +58,10 @@ describe Curly::ComponentCompiler do
|
|
118
58
|
evaluate("widget color=blue size=50px").should == "Widget (50px) - blue"
|
119
59
|
end
|
120
60
|
|
61
|
+
it "compiles context block components" do
|
62
|
+
evaluate("form", type: :context).should == "some form"
|
63
|
+
end
|
64
|
+
|
121
65
|
it "allows both identifier and attributes" do
|
122
66
|
evaluate("i18n.hello fallback=yolo").should == "yolo"
|
123
67
|
end
|
@@ -142,19 +86,24 @@ describe Curly::ComponentCompiler do
|
|
142
86
|
expect { evaluate("invalid") }.to raise_exception(Curly::Error)
|
143
87
|
end
|
144
88
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
context = double("context", presenter: presenter)
|
89
|
+
it "fails when a context block component is used with a method that doesn't take a block" do
|
90
|
+
expect { evaluate("title", type: :context) }.to raise_exception(Curly::Error)
|
91
|
+
end
|
92
|
+
end
|
150
93
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
94
|
+
def evaluate(text, type: nil, &block)
|
95
|
+
name, identifier, attributes = Curly::ComponentScanner.scan(text)
|
96
|
+
component = Curly::Parser::Component.new(name, identifier, attributes)
|
97
|
+
code = Curly::ComponentCompiler.compile(presenter_class, component, type: type)
|
98
|
+
presenter = presenter_class.new
|
99
|
+
context = double("context", presenter: presenter)
|
156
100
|
|
157
|
-
|
158
|
-
|
101
|
+
context.instance_eval(<<-RUBY)
|
102
|
+
def self.render
|
103
|
+
#{code}
|
104
|
+
end
|
105
|
+
RUBY
|
106
|
+
|
107
|
+
context.render(&block)
|
159
108
|
end
|
160
109
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Curly::ComponentScanner do
|
4
|
+
it "scans the component name, identifier, and attributes" do
|
5
|
+
scan('hello.world weather="sunny"').should == [
|
6
|
+
"hello",
|
7
|
+
"world",
|
8
|
+
{ "weather" => "sunny" }
|
9
|
+
]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "allows a question mark after the identifier" do
|
13
|
+
scan('hello.world?').should == ["hello?", "world", {}]
|
14
|
+
end
|
15
|
+
|
16
|
+
def scan(component)
|
17
|
+
described_class.scan(component)
|
18
|
+
end
|
19
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
log
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Dashboards::NewPresenter < Curly::Presenter
|
2
|
+
presents :name
|
3
|
+
|
4
|
+
def form(&block)
|
5
|
+
form_for(:dashboard, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
class FormPresenter < Curly::Presenter
|
9
|
+
presents :form, :name
|
10
|
+
|
11
|
+
def name_label
|
12
|
+
"Name"
|
13
|
+
end
|
14
|
+
|
15
|
+
def name_input
|
16
|
+
@form.text_field :name, value: @name
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<li>{{item}}</li>
|