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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile +2 -0
  4. data/README.md +85 -5
  5. data/curly-templates.gemspec +37 -8
  6. data/lib/curly.rb +1 -1
  7. data/lib/curly/{attribute_parser.rb → attribute_scanner.rb} +6 -4
  8. data/lib/curly/compiler.rb +81 -72
  9. data/lib/curly/component_compiler.rb +37 -31
  10. data/lib/curly/component_scanner.rb +19 -0
  11. data/lib/curly/incomplete_block_error.rb +0 -7
  12. data/lib/curly/incorrect_ending_error.rb +0 -21
  13. data/lib/curly/parser.rb +171 -0
  14. data/lib/curly/presenter.rb +1 -1
  15. data/lib/curly/scanner.rb +23 -9
  16. data/spec/attribute_scanner_spec.rb +46 -0
  17. data/spec/collection_blocks_spec.rb +88 -0
  18. data/spec/compiler/context_blocks_spec.rb +42 -0
  19. data/spec/component_compiler_spec.rb +26 -77
  20. data/spec/component_scanner_spec.rb +19 -0
  21. data/spec/{integration/components_spec.rb → components_spec.rb} +0 -0
  22. data/spec/{integration/conditional_blocks_spec.rb → conditional_blocks_spec.rb} +0 -0
  23. data/spec/dummy/.gitignore +1 -0
  24. data/spec/dummy/app/controllers/application_controller.rb +2 -0
  25. data/spec/dummy/app/controllers/dashboards_controller.rb +13 -0
  26. data/spec/dummy/app/helpers/application_helper.rb +5 -0
  27. data/spec/dummy/app/presenters/dashboards/collection_presenter.rb +7 -0
  28. data/spec/dummy/app/presenters/dashboards/item_presenter.rb +7 -0
  29. data/spec/dummy/app/presenters/dashboards/new_presenter.rb +19 -0
  30. data/spec/dummy/app/presenters/dashboards/partials_presenter.rb +5 -0
  31. data/spec/dummy/app/presenters/dashboards/show_presenter.rb +12 -0
  32. data/spec/dummy/app/presenters/layouts/application_presenter.rb +9 -0
  33. data/spec/dummy/app/views/dashboards/_item.html.curly +1 -0
  34. data/spec/dummy/app/views/dashboards/collection.html.curly +5 -0
  35. data/spec/dummy/app/views/dashboards/new.html.curly +3 -0
  36. data/spec/dummy/app/views/dashboards/partials.html.curly +3 -0
  37. data/spec/dummy/app/views/dashboards/show.html.curly +3 -0
  38. data/spec/dummy/app/views/layouts/application.html.curly +8 -0
  39. data/spec/dummy/config.ru +4 -0
  40. data/spec/dummy/config/application.rb +12 -0
  41. data/spec/dummy/config/boot.rb +5 -0
  42. data/spec/dummy/config/environment.rb +5 -0
  43. data/spec/dummy/config/environments/test.rb +36 -0
  44. data/spec/dummy/config/routes.rb +6 -0
  45. data/spec/integration/application_layout_spec.rb +21 -0
  46. data/spec/integration/collection_blocks_spec.rb +17 -78
  47. data/spec/integration/context_blocks_spec.rb +21 -0
  48. data/spec/integration/partials_spec.rb +23 -0
  49. data/spec/parser_spec.rb +95 -0
  50. data/spec/scanner_spec.rb +24 -14
  51. data/spec/spec_helper.rb +4 -3
  52. metadata +49 -14
  53. data/lib/curly/component_parser.rb +0 -13
  54. data/spec/attribute_parser_spec.rb +0 -46
  55. 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 ".compile_conditional" do
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
- def evaluate(component, &block)
146
- method, argument, attributes = Curly::ComponentParser.parse(component)
147
- code = Curly::ComponentCompiler.compile_component(presenter_class, method, argument, attributes)
148
- presenter = presenter_class.new
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
- context.instance_eval(<<-RUBY)
152
- def self.render
153
- #{code}
154
- end
155
- RUBY
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
- context.render(&block)
158
- end
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
@@ -0,0 +1 @@
1
+ log
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,13 @@
1
+ class DashboardsController < ApplicationController
2
+ def show
3
+ @message = "Hello, World!"
4
+ end
5
+
6
+ def collection
7
+ @items = ["uno", "dos", "tres!"]
8
+ end
9
+
10
+ def new
11
+ @name = "test"
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module ApplicationHelper
2
+ def welcome_message
3
+ "Welcome!"
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class Dashboards::CollectionPresenter < Curly::Presenter
2
+ presents :items
3
+
4
+ def items
5
+ @items
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ class Dashboards::ItemPresenter < Curly::Presenter
2
+ presents :item
3
+
4
+ def item
5
+ @item
6
+ end
7
+ end
@@ -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,5 @@
1
+ class Dashboards::PartialsPresenter < Curly::Presenter
2
+ def items
3
+ render partial: 'item', collection: ["One", "Two"]
4
+ end
5
+ end
@@ -0,0 +1,12 @@
1
+ class Dashboards::ShowPresenter < Curly::Presenter
2
+ presents :message
3
+
4
+ def message
5
+ @message
6
+ end
7
+
8
+ def welcome
9
+ # This is a helper method:
10
+ welcome_message
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ class Layouts::ApplicationPresenter < Curly::Presenter
2
+ def title
3
+ "Dummy app"
4
+ end
5
+
6
+ def content
7
+ yield
8
+ end
9
+ end
@@ -0,0 +1 @@
1
+ <li>{{item}}</li>
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ {{*items}}
3
+ <li>{{item}}</li>
4
+ {{/items}}
5
+ </ul>
@@ -0,0 +1,3 @@
1
+ {{@form}}
2
+ <b>{{name_label}}</b> {{name_input}}
3
+ {{/form}}
@@ -0,0 +1,3 @@
1
+ <ul>
2
+ {{items}}
3
+ </ul>
@@ -0,0 +1,3 @@
1
+ <h1>Dashboard</h1>
2
+ <p>{{message}}</p>
3
+ <p>{{welcome}}</p>
@@ -0,0 +1,8 @@
1
+ <html>
2
+ <head>
3
+ <title>{{title}}</title>
4
+ </head>
5
+ <body>
6
+ {{content}}
7
+ </body>
8
+ </html>
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Rails.application
@@ -0,0 +1,12 @@
1
+ require File.expand_path('../boot', __FILE__)
2
+
3
+ require 'action_controller/railtie'
4
+
5
+ Bundler.require(*Rails.groups)
6
+ require "curly"
7
+
8
+ module Dummy
9
+ class Application < Rails::Application
10
+ end
11
+ end
12
+
@@ -0,0 +1,5 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
5
+ $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__)