express_templates 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +53 -0
  4. data/Rakefile +32 -0
  5. data/lib/express_templates/component.rb +92 -0
  6. data/lib/express_templates/components/html_tag.rb +36 -0
  7. data/lib/express_templates/components/wrapper.rb +51 -0
  8. data/lib/express_templates/components/yielder.rb +21 -0
  9. data/lib/express_templates/components.rb +8 -0
  10. data/lib/express_templates/expander.rb +116 -0
  11. data/lib/express_templates/html5_emitter.rb +45 -0
  12. data/lib/express_templates/renderer.rb +16 -0
  13. data/lib/express_templates/template/handler.rb +15 -0
  14. data/lib/express_templates/version.rb +3 -0
  15. data/lib/express_templates.rb +12 -0
  16. data/lib/tasks/gara_tasks.rake +4 -0
  17. data/test/component_test.rb +77 -0
  18. data/test/dummy/Rakefile +6 -0
  19. data/test/dummy/app/assets/javascripts/application.js +13 -0
  20. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  21. data/test/dummy/app/controllers/application_controller.rb +5 -0
  22. data/test/dummy/app/controllers/hello_controller.rb +5 -0
  23. data/test/dummy/app/helpers/application_helper.rb +2 -0
  24. data/test/dummy/app/views/hello/show.html.et +5 -0
  25. data/test/dummy/app/views/layouts/application.html.et +15 -0
  26. data/test/dummy/bin/bundle +3 -0
  27. data/test/dummy/bin/rails +4 -0
  28. data/test/dummy/bin/rake +4 -0
  29. data/test/dummy/config/application.rb +26 -0
  30. data/test/dummy/config/boot.rb +5 -0
  31. data/test/dummy/config/environment.rb +5 -0
  32. data/test/dummy/config/environments/development.rb +37 -0
  33. data/test/dummy/config/environments/production.rb +82 -0
  34. data/test/dummy/config/environments/test.rb +39 -0
  35. data/test/dummy/config/initializers/assets.rb +8 -0
  36. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  37. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  38. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  39. data/test/dummy/config/initializers/inflections.rb +16 -0
  40. data/test/dummy/config/initializers/mime_types.rb +4 -0
  41. data/test/dummy/config/initializers/session_store.rb +3 -0
  42. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  43. data/test/dummy/config/locales/en.yml +23 -0
  44. data/test/dummy/config/routes.rb +4 -0
  45. data/test/dummy/config/secrets.yml +22 -0
  46. data/test/dummy/config.ru +4 -0
  47. data/test/dummy/log/development.log +6478 -0
  48. data/test/dummy/log/test.log +20859 -0
  49. data/test/dummy/public/404.html +67 -0
  50. data/test/dummy/public/422.html +67 -0
  51. data/test/dummy/public/500.html +66 -0
  52. data/test/dummy/public/favicon.ico +0 -0
  53. data/test/dummy/test/controllers/hello_controller_test.rb +14 -0
  54. data/test/dummy/test/helpers/hello_helper_test.rb +4 -0
  55. data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  56. data/test/dummy/tmp/cache/assets/development/sprockets/219e4ef52e71a2dd3142d36271626492 +0 -0
  57. data/test/dummy/tmp/cache/assets/development/sprockets/29331726baea36717bf28565c0be1881 +0 -0
  58. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  59. data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  60. data/test/dummy/tmp/cache/assets/development/sprockets/4584795e7f513db14ffca10dab44759e +0 -0
  61. data/test/dummy/tmp/cache/assets/development/sprockets/72a227156d935c44e2aad8718b3952fb +0 -0
  62. data/test/dummy/tmp/cache/assets/development/sprockets/cfea650552ed221f83ea2c8fc3198c53 +0 -0
  63. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  64. data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  65. data/test/dummy/tmp/cache/assets/development/sprockets/e27275f4020fd1fc40de79a1e5db4c96 +0 -0
  66. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  67. data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  68. data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  69. data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  70. data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  71. data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  72. data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  73. data/test/expander_stack_test.rb +41 -0
  74. data/test/expander_test.rb +56 -0
  75. data/test/fixtures/a_big_page.html +595 -0
  76. data/test/gara_test.rb +19 -0
  77. data/test/handler_test.rb +122 -0
  78. data/test/performance_test.rb +93 -0
  79. data/test/test_helper.rb +17 -0
  80. data/test/wrapper_test.rb +23 -0
  81. metadata +271 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f322ad6cb1fb8bf620577f100a9a70274c6172d1
4
+ data.tar.gz: ea903155b79430c8e5ec5f557ea80ee7461d9fdd
5
+ SHA512:
6
+ metadata.gz: 9b71ed00ff0f5a2424a94b84da6c4104b988942b9fa37d7e36ec7c892118ccc33be46893c862be0e5ffec0b2ded2c5ed844d713e7650b3f5d0c82f0f78d0e726
7
+ data.tar.gz: d026690d15f79913cd90f112f437990449af06c795e718cf512a2d1a34d037ffa43e8e8161b20f9a7db4ebe9cf4aee9ea34421f628449d5f51b2584f98460446
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 ÆLOGICA | Build it better here.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # ExpressTemplates
2
+
3
+ Provides a macro-ish DSL for use in constructing html templates in plain Ruby as an alternative to Erb or HAML.
4
+
5
+ ## Usage
6
+
7
+ Add this to your gemfile:
8
+
9
+ gem 'express_templates'
10
+
11
+ Rename your application.html.erb to application.html.et.
12
+
13
+ Change your template to look like this.
14
+
15
+ html(lang: "en") {
16
+ head {
17
+ meta charset: 'utf-8'
18
+ meta name: 'viewport', content: "width=device-width, initial-scale=1.0"
19
+ title {
20
+ content_for(:title)
21
+ }
22
+ stylesheet_link_tag "application", media: 'all', 'data-turbolinks-track' => true
23
+ csrf_meta_tags
24
+ }
25
+ body {
26
+ yield
27
+ javascript_include_tag "application"
28
+ }
29
+ }
30
+
31
+ Everything should work as you would expect.
32
+
33
+ Set your editor syntax for .et files to Ruby.
34
+
35
+ ### How It Works
36
+
37
+ ExpressTemplates works via a good-enough stand in for a true macro system in Ruby which would make this type of thing considerably easier.
38
+
39
+ Basically, we use these "macros" and Ruby's block structure to build up a tree of components corresponding to the HTML structure of a document fragment. Each HTML5 tag is a component available in the form of a macro. Unrecognized identifiers are wrapped for later evaluation, presumably in a ViewContext.
40
+
41
+ yield and local variables which we may expect to be available in a view context are also wrapped for evaluation later.
42
+
43
+ ## Background
44
+
45
+ Sufficent motivation for this gem can be explained thusly: The bondage of HAML is unnecessary. The clutter of Erb is unsightly.
46
+
47
+ I generall prefer "one syntax per file" for reasons of cognative load and maintainability.
48
+
49
+ Ultimately my objective with ExpressTemplates is to get away from writing HTML directly and to use this as a substrate for building pages out of higher-level, reusable components which include not only DOM elements but also behaviors.
50
+
51
+ ExpressTemplates are part of the AppExpress platform at [appexpress.io](http://appexpress.io).
52
+
53
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'ExpressTemplates'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+ Bundler::GemHelper.install_tasks
21
+
22
+ require 'rake/testtask'
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << 'lib'
26
+ t.libs << 'test'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = false
29
+ end
30
+
31
+
32
+ task default: :test
@@ -0,0 +1,92 @@
1
+ module ExpressTemplates
2
+ class Component
3
+
4
+ attr_accessor :children
5
+
6
+ INDENT = ' '
7
+
8
+ def initialize(*children_or_options)
9
+ @children = []
10
+ @options = {}.with_indifferent_access
11
+ _process(*children_or_options)
12
+ end
13
+
14
+ def self.macro_name
15
+ @macro_name ||= to_s.split('::').last.underscore
16
+ end
17
+
18
+ def macro_name ; self.class.macro_name end
19
+
20
+ def html_options
21
+ @options.each_pair.map do |name, value|
22
+ %Q(#{name}=\\"#{value}\\")
23
+ end.join(" ")
24
+ end
25
+
26
+ def start_tag
27
+ "<#{macro_name}#{html_options.empty? ? '' : ' '+html_options}>"
28
+ end
29
+
30
+ def close_tag
31
+ "</#{macro_name}>"
32
+ end
33
+
34
+ def add_css_class(css_class)
35
+ @options['class'] ||= ''
36
+ @options['class'] = (@options['class'].split + [css_class]).join(" ")
37
+ end
38
+
39
+ def method_missing(name, *args)
40
+ add_css_class(name)
41
+ _process(*args) unless args.empty?
42
+ return self
43
+ end
44
+
45
+ def compile
46
+ ruby_fragments = @children.map do |child|
47
+ if child.respond_to?(:compile)
48
+ child.compile
49
+ else
50
+ %Q("#{child}")
51
+ end
52
+ end
53
+ ruby_fragments.unshift %Q("#{start_tag}")
54
+ ruby_fragments.push %Q("#{close_tag}")
55
+ ruby_fragments.reject {|frag| frag.empty? }.join("+")
56
+ end
57
+
58
+ def to_template(depth = 0)
59
+ template_fragments = @children.map do |child|
60
+ if child.kind_of?(ExpressTemplates::Component)
61
+ child.to_template(depth+1)
62
+ else
63
+ child
64
+ end
65
+ end
66
+ indent = INDENT*(depth+1)
67
+ macro_name + _blockify(template_fragments.join("\n#{indent}"), depth)
68
+ end
69
+
70
+ private
71
+ def _indent(code)
72
+ code.split("\n").map {|line| INDENT + line }.join("\n")
73
+ end
74
+
75
+ def _blockify(code, depth)
76
+ indent = INDENT*depth
77
+ code.empty? ? code : " {\n#{_indent(code)}\n}\n"
78
+ end
79
+
80
+ def _process(*children_or_options)
81
+ children_or_options.each do |child_or_option|
82
+ if child_or_option.kind_of?(Hash)
83
+ @options.merge!(child_or_option)
84
+ else
85
+ @children << child_or_option
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ end
92
+ end
@@ -0,0 +1,36 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ class HtmlTag < ExpressTemplates::Component
4
+ TAGS = [ :a, :abbr, :address, :area, :article, :aside, :audio,
5
+ :b, :base, :bdi, :bdo, :blockquote, :body, :br, :button,
6
+ :canvas, :caption, :cite, :code, :col, :colgroup,
7
+ :data, :datalist, :dd, :del, :details, :dfn, :div, :dl, :dt,
8
+ :em, :embed,
9
+ :fieldset, :figcaption, :figure, :footer, :form,
10
+ :h1, :h2, :h3, :h4, :h5, :h6, :head, :header, :hr, :html,
11
+ :i, :iframe, :img, :input, :ins,
12
+ :kbd, :keygen,
13
+ :label, :legend, :li, :link,
14
+ :main, :map, :mark, :math, :menu, :menuitem, :meta, :meter,
15
+ :nav, :noscript,
16
+ :object, :ol, :optgroup, :option, :output,
17
+ :p, :param, :pre, :progress,
18
+ :q,
19
+ :rp, :rt, :ruby,
20
+ :s, :samp, :script, :section, :select, :small, :source,
21
+ :span, :strong, :style, :sub, :sup, :summary, :svg,
22
+ :table, :tbody, :td, :textarea, :tfoot, :th, :thead, :time, :title,
23
+ :tr, :track,
24
+ :u, :ul,
25
+ :var, :video,
26
+ :wbr]
27
+
28
+ end
29
+
30
+ HtmlTag::TAGS.each do |tag|
31
+ klass = tag.to_s.titleize
32
+ ExpressTemplates::Components.module_eval "class #{klass} < HtmlTag ; end"
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,51 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ # wrap locals and helpers for evaluation during render
4
+ class Wrapper
5
+
6
+ attr_accessor :name, :args
7
+
8
+ def initialize(name, *args)
9
+ @name = name
10
+ @args = args
11
+ end
12
+
13
+ def compile
14
+ # insure nils do not blow up view
15
+ %Q("\#\{#{_compile}\}")
16
+ end
17
+
18
+ def to_template
19
+ _compile
20
+ end
21
+
22
+ def children
23
+ []
24
+ end
25
+
26
+ private
27
+ def _compile
28
+ if @args.empty?
29
+ return name
30
+ else
31
+ args_string = args.slice(0..-2).map(&:inspect).join(', ')
32
+ last_arg = ''
33
+ if args.last.is_a?(Hash) # expand a hash
34
+ unless args.last.empty?
35
+ # This approach has limitations - will only work on structures of
36
+ # immediate values
37
+ last_arg = args.last.inspect.match(/^\{(.*)\}$/)[1]
38
+ last_arg.gsub!(/:(\w+)=>/, '\1: ') # use ruby 2 hash syntax
39
+ else
40
+ last_arg = "{}" # empty hash
41
+ end
42
+ else
43
+ last_arg = args.last.inspect
44
+ end
45
+ args_string << (args_string.empty? ? last_arg : ", #{last_arg}")
46
+ return "#{name}(#{args_string})"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ class Yielder
4
+ def initialize(*args)
5
+ @arg = args.first
6
+ end
7
+
8
+ def compile
9
+ if @arg
10
+ "yield(#{@arg})"
11
+ else
12
+ "yield"
13
+ end
14
+ end
15
+
16
+ def to_template
17
+ compile
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ module ExpressTemplates
2
+ module Components
3
+ end
4
+ end
5
+ require 'express_templates/component'
6
+ require 'express_templates/components/html_tag'
7
+ require 'express_templates/components/wrapper'
8
+ require 'express_templates/components/yielder'
@@ -0,0 +1,116 @@
1
+ require 'express_templates/components'
2
+ module ExpressTemplates
3
+ class Expander
4
+
5
+ cattr_accessor :module_search_space
6
+
7
+ attr_accessor :stack
8
+
9
+ def self.expand(template, source)
10
+ expanded = new(template).expand(source)
11
+ compiled = expanded.map(&:compile)
12
+ return compiled.join("+")
13
+ end
14
+
15
+ def initialize(template)
16
+ @template = template
17
+ @stack = Stack.new
18
+ end
19
+
20
+ def expand(source=nil, &block)
21
+ if source
22
+ modified = source.gsub(/(\W)(yield)(\.*)?/, '\1 (stack << ExpressTemplates::Components::Yielder.new(\3))')
23
+ modified.gsub!(/(\W)(@\w+)(\W)?/, '\1 (stack << ExpressTemplates::Components::Wrapper.new("\2") )\3')
24
+ instance_eval(modified, @template.inspect)
25
+ stack.current
26
+ else
27
+ instance_eval &block
28
+ stack.current
29
+ end
30
+ end
31
+
32
+ # define a "macro" method for a component
33
+ # these methods accept args which are passed to the
34
+ # initializer for the component
35
+ # blocks supplied are evaluated and any returned objects are
36
+ # added as children to the component
37
+ def self.register_macros_for(*components)
38
+ components.each do |component|
39
+ define_method(component.macro_name.to_sym) do |*args, &block|
40
+ stack << if block
41
+ begin
42
+ stack.descend!
43
+ block.call
44
+ # anything stored on stack.current or on stack.next is added as a child
45
+ # this is a bit problematic in the case where we would have
46
+ # blocks and helpers or locals mixed
47
+ component.new(*(args.push(*(stack.current))))
48
+ ensure
49
+ stack.ascend!
50
+ end
51
+ else
52
+ component.new(*(args))
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ @module_search_space = [ExpressTemplates::Components]
60
+
61
+ @module_search_space.each do |mod|
62
+ register_macros_for(*
63
+ mod.constants.map { |const| [mod.to_s, const.to_s].join("::").constantize }.
64
+ select { |klass| klass.ancestors.include? (ExpressTemplates::Component) }
65
+ )
66
+ end
67
+
68
+ def method_missing(name, *args)
69
+ stack.current << ExpressTemplates::Components::Wrapper.new(name.to_s, *args)
70
+ nil
71
+ end
72
+
73
+ class Stack
74
+ def initialize
75
+ @stack = [[]]
76
+ @frame = 0
77
+ end
78
+
79
+ def all
80
+ @stack
81
+ end
82
+
83
+ def dump
84
+ puts "Current frame: #{@frame}"
85
+ puts all.map(&:inspect).join("\n")
86
+ end
87
+
88
+ def <<(child)
89
+ current << child
90
+ child
91
+ end
92
+
93
+ def current
94
+ @stack[@frame]
95
+ end
96
+
97
+ def next
98
+ @stack[@frame+1] ||= []
99
+ end
100
+
101
+ def descend!
102
+ @frame += 1
103
+ @stack[@frame] ||= []
104
+ @stack[@frame].clear
105
+ @frame
106
+ end
107
+
108
+ def ascend!
109
+ raise "Cannot ascend" if @frame <= 0
110
+ current.clear ;
111
+ self.next.clear
112
+ @frame -= 1
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,45 @@
1
+ module ExpressTemplates
2
+
3
+ HTML5_TAGS = [ :a, :abbr, :address, :area, :article, :aside, :audio,
4
+ :b, :base, :bdi, :bdo, :blockquote, :body, :br, :button,
5
+ :canvas, :caption, :cite, :code, :col, :colgroup,
6
+ :data, :datalist, :dd, :del, :details, :dfn, :div, :dl, :dt,
7
+ :em, :embed,
8
+ :fieldset, :figcaption, :figure, :footer, :form,
9
+ :h1, :h2, :h3, :h4, :h5, :h6, :head, :header, :hr, :html,
10
+ :i, :iframe, :img, :input, :ins,
11
+ :kbd, :keygen,
12
+ :label, :legend, :li, :link,
13
+ :main, :map, :mark, :math, :menu, :menuitem, :meta, :meter,
14
+ :nav, :noscript,
15
+ :object, :ol, :optgroup, :option, :output,
16
+ :p, :param, :pre, :progress,
17
+ :q,
18
+ :rp, :rt, :ruby,
19
+ :s, :samp, :script, :section, :select, :small, :source,
20
+ :span, :strong, :style, :sub, :sup, :summary, :svg,
21
+ :table, :tbody, :td, :textarea, :tfoot, :th, :thead, :time, :title,
22
+ :tr, :track,
23
+ :u, :ul,
24
+ :var, :video,
25
+ :wbr]
26
+
27
+ module Html5Emitter
28
+
29
+ HTML5_TAGS.each do |tag|
30
+ define_method(tag) do |*args, &block|
31
+ if block
32
+ content_tag(tag, capture(&block), *args)
33
+ else
34
+ # adjust args because content_tag expects options in the
35
+ # second position when have nil content
36
+ if args.first.is_a?(Hash)
37
+ content_tag(tag, *(args.unshift(nil)))
38
+ else
39
+ content_tag(tag, *args)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ module ExpressTemplates
2
+ module Renderer
3
+ # render accepts source or block, expands to macros
4
+ # compiles the resulting macros
5
+ # and then evaluates the resulting string of ruby in the context provided
6
+ def render context=nil, template_source=nil, &block
7
+ expander = ExpressTemplates::Expander.new(nil)
8
+ expanded_template = if block
9
+ (expander.expand(&block).map(&:compile).join(';'))
10
+ else
11
+ expander.expand(template_source).map(&:compile).join(";")
12
+ end
13
+ context.instance_eval expanded_template
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ module ExpressTemplates
2
+ module Template
3
+ class Handler
4
+ def self.call(template)
5
+ new.call(template)
6
+ end
7
+
8
+ def call(template)
9
+ # returns a string to be eval'd
10
+ ExpressTemplates::Expander.expand(template, template.source)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module ExpressTemplates
2
+ VERSION = "0.2.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ module ExpressTemplates
2
+ require 'express_templates/template/handler'
3
+ require 'express_templates/html5_emitter'
4
+ require 'express_templates/renderer'
5
+ require 'express_templates/expander'
6
+ require 'express_templates/components'
7
+ extend Renderer
8
+ if defined?(Rails)
9
+ ::ActionView::Template.register_template_handler :et, ExpressTemplates::Template::Handler
10
+ end
11
+
12
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :express_templates do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,77 @@
1
+ require 'test_helper'
2
+
3
+ class ComponentTest < ActiveSupport::TestCase
4
+
5
+ class Bare < ExpressTemplates::Component ; end
6
+ class Sub < ExpressTemplates::Component ; end
7
+
8
+ def bare_component(*args)
9
+ Bare.new(*args)
10
+ end
11
+
12
+ def sub_component(*args)
13
+ Sub.new(*args)
14
+ end
15
+
16
+
17
+ test "#macro_name returns the name of the class" do
18
+ assert_equal 'bare', bare_component.macro_name
19
+ end
20
+
21
+ test "#compile returns a string" do
22
+ assert_kind_of String, bare_component.compile
23
+ end
24
+
25
+ test "has no children" do
26
+ assert_empty bare_component.children
27
+ end
28
+
29
+ def bare_with_2_children
30
+ component = bare_component "child1", "child2"
31
+ end
32
+
33
+ test "can be created with children" do
34
+ assert_equal 2, bare_with_2_children.children.size
35
+ assert_equal "child2", bare_with_2_children.children.last
36
+ end
37
+
38
+ test "#compile on bare_with_2_children yields '\"<bare>\"+\"child1\"+\"child2\"+\"</bare>\"'" do
39
+ assert_equal '"<bare>"+"child1"+"child2"+"</bare>"', bare_with_2_children.compile
40
+ end
41
+
42
+ test "#start_tag is my macro_name as an xml start tag" do
43
+ assert_equal "<#{bare_component.macro_name}>", bare_component.start_tag
44
+ end
45
+
46
+ test "#close_tag is my macro_name as an xml close tag" do
47
+ assert_equal "</#{bare_component.macro_name}>", bare_component.close_tag
48
+ end
49
+
50
+ def component_with_subcomponent
51
+ bare_component sub_component
52
+ end
53
+
54
+ test "#compile on component_with_subcomponent returns a string which when eval'd looks like '<bare><sub></sub></bare>'" do
55
+ assert_equal '<bare><sub></sub></bare>', eval(component_with_subcomponent.compile)
56
+ end
57
+
58
+ test "#to_template on bare_component returns 'bare'" do
59
+ assert_equal 'bare', bare_component.to_template
60
+ end
61
+
62
+ test "#to_template on component_with_subcomponent returns 'bare {\n sub\n}\n'" do
63
+ assert_equal "bare {\n sub\n}\n", component_with_subcomponent.to_template
64
+ end
65
+
66
+ test "#to_template on nested components indents properly'" do
67
+ expected = %Q(bare {
68
+ sub {
69
+ sub
70
+ }
71
+ }
72
+ )
73
+ assert_equal expected, Bare.new(Sub.new(Sub.new)).to_template
74
+ end
75
+
76
+
77
+ end
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end
@@ -0,0 +1,5 @@
1
+ class HelloController < ApplicationController
2
+ def show
3
+ @hello = "Hi there"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end