express_templates 0.2.0

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 (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