Skip to content
This repository
Browse code

added Sass/SCSS support; added HAML eval context providing metamodel …

…access; support for logger in Server
  • Loading branch information...
commit 0ade31a8e91c30306163dc71c176ffc03a606a07 1 parent 8306977
mthiede authored
4 CHANGELOG
@@ -30,7 +30,7 @@
30 30 * Fixed marker (cursor) positioning for editor nodes with offset
31 31 * Fixed exception in case fold control nodes are not present
32 32
33   -=0.x.x
  33 +=0.2.1
34 34
35   -* Added support for HAML style HTML templates
  35 +* Added support for HAML style HTML templates and stylesheets based on SASS
36 36
12 doc/concrete_developers_guide.html
@@ -753,13 +753,15 @@ <h3 id="_concrete_syntaxes">Concrete Syntaxes</h3><div style="clear:left"></div>
753 753 templates.html
754 754 &lt;syntax 2&gt;
755 755 style.css
756   - templates.html</tt></pre>
  756 + templates.html
  757 +...
  758 +&lt;syntax root dir n&gt;</tt></pre>
757 759 </div></div>
758   -<div class="paragraph"><p>There can be several syntax root directories (e.g. one which is deployed with your editor and one in the user&#8217;s home directory). The display name of a specific syntax is derived from the syntax directory within a root directory. The syntax directory should contain a file "templates.html" or "templates.haml" in case the syntax contains a HTML part. It should also contain a CSS file "style.css" and it may contain more resources (e.g. images) referenced from that stylesheet.</p></div>
759   -<div class="paragraph"><p>HTML templates may be specified in HAML format to make them more concise and readable. For this to work, <em>haml</em> needs to be installed as a gem (use "gem install haml") and the HTML templates file must be named "templates.haml".</p></div>
  760 +<div class="paragraph"><p>There can be several syntax root directories (e.g. one which is deployed with your editor and one in the user&#8217;s home directory). The display name of a specific syntax is derived from the syntax directory within a root directory. The syntax directory should contain a file "templates.html" or "templates.haml" in case the syntax contains a HTML part. It should also contain a CSS, SASS or SCSS file and it may contain more resources (e.g. images) referenced from that stylesheet.</p></div>
  761 +<div class="paragraph"><p>HTML templates and stylesheets may be specified in HAML and SASS or SCSS format to make them more concise and readable. For this to work, <em>haml</em> needs to be installed as a gem (use "gem install haml"), the HTML templates file must be named "templates.haml" and the SASS and SCSS filenames must end with .sass and .scss. If the server is given a <em>haml eval contect</em> object (see below) the HAML templates will evaluate in this context.</p></div>
760 762 <div class="paragraph"><p>The server will make the files in the directory of the selected syntax available via the path prefix "/syntax". It will also insert the contents of the file "template.html" in the main HTML file by replacing the placeholder comment "html templates". See below for an example.</p></div>
761 763 <h3 id="_setting_up_the_server">Setting up the Server</h3><div style="clear:left"></div>
762   -<div class="paragraph"><p>With a working set, data provider and syntax provider in place the server can be instantiated. As an additional argument, it needs the location of its HTML root directory. The root directory should contain a file named "editor.html" which is the main HTML file as described in the next section.</p></div>
  764 +<div class="paragraph"><p>With a working set, data provider and syntax provider in place the server can be instantiated. As an additional argument, it needs the location of its HTML root directory. The root directory should contain a file named "editor.html" which is the main HTML file as described in the next section. A further option to the server is the HAML eval context which can be provided using the named argument <em>:hamlEvalContext</em>.</p></div>
763 765 <div class="paragraph"><p>The following example code sets up an instance of <em>Concrete::Server</em>. It reads the module names as file names from the command line and uses the RGen builtin ECore meta-metamodel as metamodel (in fact this is taken form the "mmedit" metamodel editor project). It uses the <em>Concrete::IndexBuilder</em> to derive the index metamodel and the index model and it uses a <em>Concrete::Config</em> to store the preferences in the user&#8217;s home directory.</p></div>
764 766 <div class="listingblock">
765 767 <div class="content">
@@ -1048,7 +1050,7 @@ <h2 id="_css_class_reference">CSS Class Reference</h2>
1048 1050 </div>
1049 1051 <div id="footer">
1050 1052 <div id="footer-text">
1051   -Last updated 2010-06-25 11:36:59 WEDT
  1053 +Last updated 2010-06-30 08:10:12 WEDT
1052 1054 </div>
1053 1055 </div>
1054 1056 </body>
8 doc/concrete_developers_guide.txt
@@ -353,18 +353,20 @@ The workbench server supports selecting a concrete syntax from a set of availabl
353 353 <syntax 2>
354 354 style.css
355 355 templates.html
  356 +...
  357 +<syntax root dir n>
356 358 ----
357 359
358   -There can be several syntax root directories (e.g. one which is deployed with your editor and one in the user's home directory). The display name of a specific syntax is derived from the syntax directory within a root directory. The syntax directory should contain a file "templates.html" or "templates.haml" in case the syntax contains a HTML part. It should also contain a CSS file "style.css" and it may contain more resources (e.g. images) referenced from that stylesheet.
  360 +There can be several syntax root directories (e.g. one which is deployed with your editor and one in the user's home directory). The display name of a specific syntax is derived from the syntax directory within a root directory. The syntax directory should contain a file "templates.html" or "templates.haml" in case the syntax contains a HTML part. It should also contain a CSS, SASS or SCSS file and it may contain more resources (e.g. images) referenced from that stylesheet.
359 361
360   -HTML templates may be specified in HAML format to make them more concise and readable. For this to work, _haml_ needs to be installed as a gem (use "gem install haml") and the HTML templates file must be named "templates.haml".
  362 +HTML templates and stylesheets may be specified in HAML and SASS or SCSS format to make them more concise and readable. For this to work, _haml_ needs to be installed as a gem (use "gem install haml"), the HTML templates file must be named "templates.haml" and the SASS and SCSS filenames must end with .sass and .scss. If the server is given a _haml eval contect_ object (see below) the HAML templates will evaluate in this context.
361 363
362 364 The server will make the files in the directory of the selected syntax available via the path prefix "/syntax". It will also insert the contents of the file "template.html" in the main HTML file by replacing the placeholder comment "html templates". See below for an example.
363 365
364 366
365 367 === Setting up the Server ===
366 368
367   -With a working set, data provider and syntax provider in place the server can be instantiated. As an additional argument, it needs the location of its HTML root directory. The root directory should contain a file named "editor.html" which is the main HTML file as described in the next section.
  369 +With a working set, data provider and syntax provider in place the server can be instantiated. As an additional argument, it needs the location of its HTML root directory. The root directory should contain a file named "editor.html" which is the main HTML file as described in the next section. A further option to the server is the HAML eval context which can be provided using the named argument _:hamlEvalContext_.
368 370
369 371 The following example code sets up an instance of _Concrete::Server_. It reads the module names as file names from the command line and uses the RGen builtin ECore meta-metamodel as metamodel (in fact this is taken form the "mmedit" metamodel editor project). It uses the _Concrete::IndexBuilder_ to derive the index metamodel and the index model and it uses a _Concrete::Config_ to store the preferences in the user's home directory.
370 372
32 lib/concrete/concrete_syntax_provider.rb
... ... @@ -1,15 +1,11 @@
1 1 require 'andand'
2   -begin
3   - require 'haml'
4   -rescue LoadError
5   -end
6 2
7 3 module Concrete
8 4
9 5 class ConcreteSyntaxProvider
10 6
11 7 class ConcreteSyntax
12   - attr_accessor :ident, :dir, :name, :desc, :htmlTemplates, :cssStyleFile
  8 + attr_accessor :ident, :dir, :name, :desc
13 9 end
14 10
15 11 def initialize(configDirs, logger, config=nil)
@@ -44,43 +40,17 @@ def syntaxes
44 40 Dir.entries(cd).sort.each do |sd|
45 41 next if sd == "." || sd == ".."
46 42 syntaxDir = cd+"/"+sd
47   - templatesData = templatesData(syntaxDir)
48   - styleFile = syntaxDir + "/style.css"
49   - unless templatesData || File.exist?(styleFile)
50   - @logger.warn("Concrete syntax dir without a templates.haml (and HAML installed), templates.html or a style.css: #{syntaxDir}")
51   - next
52   - end
53 43 s = ConcreteSyntax.new
54 44 s.ident = syntaxDir.gsub("\\","/")
55 45 s.dir = syntaxDir
56 46 s.name = sd.split(/[_\W]/).collect{|w| w.capitalize}.join(" ")
57 47 s.desc = ""
58   - s.cssStyleFile = styleFile if File.exist?(styleFile)
59   - s.htmlTemplates = templatesData
60 48 result << s
61 49 end
62 50 end
63 51 result
64 52 end
65 53
66   - private
67   -
68   - def templatesData(syntaxDir)
69   - if haml && File.exist?(syntaxDir + "/templates.haml")
70   - engine = haml::Engine.new(File.read(syntaxDir + "/templates.haml"))
71   - engine.render
72   - elsif File.exist?(syntaxDir + "/templates.html")
73   - File.read(syntaxDir + "/templates.html")
74   - end
75   - end
76   -
77   - def haml
78   - begin
79   - Haml
80   - rescue NameError
81   - end
82   - end
83   -
84 54 end
85 55
86 56 end
57 lib/concrete/haml_eval_context.rb
... ... @@ -0,0 +1,57 @@
  1 +require 'rgen/ecore/ecore'
  2 +
  3 +module Concrete
  4 +
  5 +# Objects of this class are meant to be used as HAML eval context
  6 +# Context methods provide convenient access to the metamodel
  7 +# The metamodel is expected to be a RGen ECore metamodel
  8 +class HamlEvalContext
  9 + def initialize(mm)
  10 + @mm = mm
  11 + @metaclassesByName = nil
  12 + end
  13 +
  14 + def metaclasses
  15 + loadMetaclasses unless @metaclassesByName
  16 + @metaclassesByName.values
  17 + end
  18 +
  19 + def metaclass(name)
  20 + loadMetaclasses unless @metaclassesByName
  21 + @metaclassesByName[name]
  22 + end
  23 +
  24 + def metaclassFeatures(name, options={})
  25 + exclusions = options[:except] || []
  26 + if exclusions.is_a?(Array)
  27 + metaclass(name).eAllStructuralFeatures.reject{|f| exclusions.include?(f.name)}
  28 + else
  29 + metaclass(name).eAllStructuralFeatures.reject{|f| exclusions == f.name}
  30 + end
  31 + end
  32 +
  33 + def metaclassContainments(name, options={})
  34 + metaclassFeatures(name, options).select{|f| f.is_a?(RGen::ECore::EReference) && f.containment}
  35 + end
  36 +
  37 + def metaclassReferences(name, options={})
  38 + metaclassFeatures(name, options).select{|f| f.is_a?(RGen::ECore::EReference) && !f.containment}
  39 + end
  40 +
  41 + def metaclassAttributes(name, options={})
  42 + metaclassFeatures(name, options).select{|f| f.is_a?(RGen::ECore::EAttribute)}
  43 + end
  44 +
  45 + private
  46 +
  47 + def loadMetaclasses
  48 + @metaclassesByName = {}
  49 + @mm.eAllClasses.each do |c|
  50 + @metaclassesByName[c.name] = c
  51 + end
  52 + end
  53 +
  54 +end
  55 +
  56 +end
  57 +
45 lib/concrete/server.rb
... ... @@ -1,4 +1,9 @@
1 1 require 'webrick'
  2 +begin
  3 + require 'haml'
  4 + require 'sass'
  5 +rescue LoadError
  6 +end
2 7
3 8 module Concrete
4 9
@@ -9,6 +14,8 @@ def initialize(workingSet, dataProvider, syntaxProvider, htmlRoot, options={})
9 14 @dataProvider = dataProvider
10 15 @syntaxProvider = syntaxProvider
11 16 @htmlRoot = htmlRoot
  17 + @logger = options[:logger]
  18 + @hamlEvalContext = options[:hamlEvalContext]
12 19 @mutex = Mutex.new
13 20 @server = WEBrick::HTTPServer.new(:Port => (options[:port] || 1234))
14 21 @server.mount_proc("/") do |req, res|
@@ -27,16 +34,15 @@ def start
27 34 def handleRequest(req, res)
28 35 if req.path == "/"
29 36 editorHtml = File.read(@htmlRoot+"/editor.html")
30   - editorHtml.sub!(/<!--\s+html templates\s+-->/, @syntaxProvider.selectedSyntax.htmlTemplates) if @syntaxProvider.andand.selectedSyntax.andand.htmlTemplates
  37 + templatesData = htmlTemplatesData(@syntaxProvider.selectedSyntax.dir) if @syntaxProvider.selectedSyntax
  38 + editorHtml.sub!(/<!--\s+html templates\s+-->/, templatesData) if templatesData
31 39 res.body = editorHtml
32 40 elsif req.path =~ /^\/html\/(.*)/
33 41 File.open(@htmlRoot+"/"+$1, "rb") do |f|
34 42 res.body = f.read
35 43 end
36 44 elsif req.path =~ /^\/syntax\/(.*)/ && @syntaxProvider.selectedSyntax
37   - File.open(@syntaxProvider.selectedSyntax.dir+"/"+$1, "rb") do |f|
38   - res.body = f.read
39   - end
  45 + res.body = syntaxFileData(@syntaxProvider.selectedSyntax.dir+"/"+$1)
40 46 elsif req.path =~ /^\/concrete\/(.*)/
41 47 File.open(File.dirname(__FILE__)+"/../../"+$1, "rb") do |f|
42 48 res.body = f.read
@@ -85,7 +91,36 @@ def handleRequest(req, res)
85 91 # error
86 92 end
87 93 end
88   -
  94 +
  95 + def syntaxFileData(fileName)
  96 + if haveHaml? && fileName =~ /(\.sass|\.scss)$/
  97 + Sass::Engine.new(File.read(fileName)).render
  98 + else
  99 + File.open(fileName, "rb") do |f|
  100 + f.read
  101 + end
  102 + end
  103 + end
  104 +
  105 + def htmlTemplatesData(syntaxDir)
  106 + if haveHaml? && File.exist?(syntaxDir + "/templates.haml")
  107 + templatesFile = syntaxDir + "/templates.haml"
  108 + @logger.info("Using HAML templates file #{templatesFile}") if @logger
  109 + engine = Haml::Engine.new(File.read(templatesFile))
  110 + engine.render(@hamlEvalContext || Object.new)
  111 + elsif File.exist?(syntaxDir + "/templates.html")
  112 + templatesFile = syntaxDir + "/templates.html"
  113 + @logger.info("Using HTML templates file #{templatesFile}") if @logger
  114 + File.read(templatesFile)
  115 + end
  116 + end
  117 +
  118 + def haveHaml?
  119 + begin
  120 + Haml
  121 + rescue NameError
  122 + end
  123 + end
89 124 end
90 125
91 126 end
46 test/concrete_syntax_provider_test.rb
@@ -65,10 +65,12 @@ def test_syntaxes_no_syntaxes
65 65 def test_syntaxes_empty_syntax
66 66 logger = LoggerMock.new
67 67 sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir1"], logger)
68   - assert_equal [], sp.syntaxes
69   - assert_equal 1, logger.messages.size
70   - assert_match /WARN: Concrete syntax dir without a templates/, logger.messages.first
71   - assert_equal '{ "syntaxes": [], "selected": "" }', sp.syntaxesAsJson
  68 + assert_equal 1, sp.syntaxes.size
  69 + assert_equal "Empty Syntax", sp.syntaxes.first.name
  70 + assert_equal [], logger.messages
  71 + assert_equal '{ "syntaxes": [' +
  72 + '{ "ident": "'+TestDir+'/syntaxDir1/empty_syntax", "name": "Empty Syntax" }' +
  73 + '], "selected": "" }', sp.syntaxesAsJson
72 74 end
73 75
74 76 def test_syntaxes_common
@@ -77,30 +79,13 @@ def test_syntaxes_common
77 79 syntaxes = sp.syntaxes
78 80 assert_equal 1, syntaxes.size
79 81 assert_equal "First Syntax", syntaxes.first.name
80   - assert_equal "./concrete_syntax_provider_test/syntaxDir2/first_syntax/style.css", syntaxes.first.cssStyleFile
81   - assert_equal "./concrete_syntax_provider_test/syntaxDir2/first_syntax", syntaxes.first.ident
82   - assert_nil syntaxes.first.htmlTemplates
  82 + assert_equal TestDir+"/syntaxDir2/first_syntax", syntaxes.first.ident
83 83 assert_equal '{ "syntaxes": [' +
84   - '{ "ident": "./concrete_syntax_provider_test/syntaxDir2/first_syntax", "name": "First Syntax" }' +
  84 + '{ "ident": "'+TestDir+'/syntaxDir2/first_syntax", "name": "First Syntax" }' +
85 85 '], "selected": "" }', sp.syntaxesAsJson
86 86 assert_equal [], logger.messages
87 87 end
88 88
89   - def test_syntaxes_templates
90   - logger = LoggerMock.new
91   - sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir3"], logger)
92   - syntaxes = sp.syntaxes
93   - if haveHaml?
94   - assert_equal 2, syntaxes.size
95   - assert_equal "<p>Haml code!</p>", syntaxes.find{|s| s.name == "Haml Syntax"}.htmlTemplates.strip
96   - assert_equal "<div>Template Content</div>", syntaxes.find{|s| s.name == "Html Syntax"}.htmlTemplates.strip
97   - else
98   - assert_equal 1, syntaxes.size
99   - assert_equal "<div>Template Content</div>", syntaxes.first.htmlTemplates.strip
100   - assert logger.messages.any?{|m| m =~ /Concrete syntax dir without.*and HAML installed/}
101   - end
102   - end
103   -
104 89 def test_selectSyntax_no_syntax
105 90 logger = LoggerMock.new
106 91 sp = Concrete::ConcreteSyntaxProvider.new([], logger)
@@ -117,14 +102,14 @@ def test_selectSyntax_no_config
117 102 sp.selectSyntax("dummy")
118 103 end
119 104 assert_equal "First Syntax", sp.selectedSyntax.name
120   - sp.selectSyntax("./concrete_syntax_provider_test/syntaxDir3/haml_syntax")
  105 + sp.selectSyntax(TestDir+"/syntaxDir3/haml_syntax")
121 106 assert_equal "Haml Syntax", sp.selectedSyntax.name
122 107 end
123 108
124 109 def test_selectSyntax_load_config
125 110 logger = LoggerMock.new
126 111 config = ConfigMock.new
127   - config.values["concrete_syntax"] = "./concrete_syntax_provider_test/syntaxDir3/haml_syntax"
  112 + config.values["concrete_syntax"] = TestDir+"/syntaxDir3/haml_syntax"
128 113 sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir2", TestDir+"/syntaxDir3"], logger, config)
129 114 assert_equal "Haml Syntax", sp.selectedSyntax.name
130 115 end
@@ -135,15 +120,8 @@ def test_selectSyntax_store_config
135 120 sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir2", TestDir+"/syntaxDir3"], logger, config)
136 121 sp.selectSyntax("dummy")
137 122 assert_equal({}, config.values)
138   - sp.selectSyntax("./concrete_syntax_provider_test/syntaxDir3/haml_syntax" )
139   - assert_equal({"concrete_syntax" => "./concrete_syntax_provider_test/syntaxDir3/haml_syntax" }, config.values)
140   - end
141   -
142   - def haveHaml?
143   - begin
144   - Haml
145   - rescue NameError
146   - end
  123 + sp.selectSyntax(TestDir+"/syntaxDir3/haml_syntax" )
  124 + assert_equal({"concrete_syntax" => TestDir+"/syntaxDir3/haml_syntax" }, config.values)
147 125 end
148 126
149 127 end
1  test/concrete_test.rb
@@ -3,4 +3,5 @@
3 3 require 'metamodel_test'
4 4 require 'working_set_test'
5 5 require 'concrete_syntax_provider_test'
  6 +require 'haml_eval_context_test'
6 7
46 test/haml_eval_context_test.rb
... ... @@ -0,0 +1,46 @@
  1 +$:.unshift File.join(File.dirname(__FILE__),"..","lib")
  2 +
  3 +require 'test/unit'
  4 +require 'rgen/environment'
  5 +require 'rgen/array_extensions'
  6 +require 'rgen/ecore/ecore'
  7 +require 'concrete/haml_eval_context'
  8 +begin
  9 + require 'haml'
  10 +rescue LoadError
  11 +end
  12 +
  13 +class HamlEvalContextTest < Test::Unit::TestCase
  14 +
  15 + def test_simple
  16 + return unless haveHaml?
  17 + context = Concrete::HamlEvalContext.new(RGen::ECore.ecore)
  18 + engine = Haml::Engine.new("= metaclass('EClass').name")
  19 + assert_equal "EClass\n", engine.render(context)
  20 + engine = Haml::Engine.new("= metaclasses.size")
  21 + assert_equal "18\n", engine.render(context)
  22 + engine = Haml::Engine.new("= metaclassFeatures('EEnum').size")
  23 + assert_equal "8\n", engine.render(context)
  24 + engine = Haml::Engine.new("= metaclassContainments('EEnum').size")
  25 + assert_equal "2\n", engine.render(context)
  26 + engine = Haml::Engine.new("= metaclassReferences('EEnum').size")
  27 + assert_equal "1\n", engine.render(context)
  28 + engine = Haml::Engine.new("= metaclassAttributes('EEnum').size")
  29 + assert_equal "5\n", engine.render(context)
  30 + engine = Haml::Engine.new("= metaclassAttributes('EEnum', :except => ['name']).size")
  31 + assert_equal "4\n", engine.render(context)
  32 + engine = Haml::Engine.new("= metaclassAttributes('EEnum', :except => 'name').size")
  33 + assert_equal "4\n", engine.render(context)
  34 + engine = Haml::Engine.new("= metaclassAttributes('EEnum', :except => 'namex').size")
  35 + assert_equal "5\n", engine.render(context)
  36 + end
  37 +
  38 + def haveHaml?
  39 + begin
  40 + Haml
  41 + rescue NameError
  42 + end
  43 + end
  44 +
  45 +end
  46 +

0 comments on commit 0ade31a

Please sign in to comment.
Something went wrong with that request. Please try again.