concrete 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -0
- data/concrete/editor.js +6 -4
- data/concrete/inline_editor.js +19 -18
- data/concrete/ui/abstract_dialog.js +3 -3
- data/concrete/ui/layout_manager.js +4 -4
- data/concrete/ui/toolbar.js +1 -1
- data/doc/concrete_developers_guide.html +7 -4
- data/doc/concrete_developers_guide.txt +6 -2
- data/doc/concrete_users_guide.html +3 -2
- data/doc/concrete_users_guide.txt +2 -1
- data/lib/concrete/concrete_syntax_provider.rb +5 -11
- data/lib/concrete/haml_eval_context.rb +57 -0
- data/lib/concrete/server.rb +40 -5
- data/rakefile +1 -1
- data/test/concrete_syntax_provider_test.rb +127 -0
- data/test/concrete_syntax_provider_test/syntaxDir2/first_syntax/style.css +0 -0
- data/test/concrete_syntax_provider_test/syntaxDir3/haml_syntax/templates.haml +1 -0
- data/test/concrete_syntax_provider_test/syntaxDir3/html_syntax/templates.html +2 -0
- data/test/concrete_test.rb +2 -0
- data/test/haml_eval_context_test.rb +46 -0
- data/test/index_builder_test/ecore_index.js +34 -34
- metadata +40 -13
data/CHANGELOG
CHANGED
@@ -30,3 +30,10 @@
|
|
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.2.1 (September 21st, 2010)
|
34
|
+
|
35
|
+
* Added support for HAML style HTML templates and stylesheets based on SASS
|
36
|
+
* Fixed layout/positioning problems when a doctype is provided (added missing 'px' unit)
|
37
|
+
* Fixed bug not allowing to finish editing of a text attribute if an integer attribute has been edited before
|
38
|
+
* Fixed bug not allowing to position the cursor with the mouse in a text field in edit mode
|
39
|
+
|
data/concrete/editor.js
CHANGED
@@ -134,12 +134,14 @@ Concrete.Editor = Class.create({
|
|
134
134
|
this._handleInfoPopups(event);
|
135
135
|
}
|
136
136
|
this._handleRefHighlight(event);
|
137
|
-
this.popup.setStyle({left: event.clientX+20, top: event.clientY+20});
|
137
|
+
this.popup.setStyle({left: event.clientX+20+'px', top: event.clientY+20+'px'});
|
138
138
|
}
|
139
139
|
if (this.inlineEditor.isActive) {
|
140
140
|
if (event.type == "click" && event.isLeftClick()) {
|
141
|
-
|
142
|
-
|
141
|
+
if (!Event.element(event).up().hasClassName("ct_inline_editor")) {
|
142
|
+
this.inlineEditor.cancel();
|
143
|
+
this.selector.selectDirect(Event.element(event));
|
144
|
+
}
|
143
145
|
}
|
144
146
|
else if (event.keyCode == 9) { // tab
|
145
147
|
this.inlineEditor.finish();
|
@@ -512,7 +514,7 @@ Concrete.Editor = Class.create({
|
|
512
514
|
adjustMarker: function() {
|
513
515
|
var cur = this.selector.getCursorPosition();
|
514
516
|
var poff = this.marker.getOffsetParent().cumulativeOffset();
|
515
|
-
this.marker.setStyle({left: cur.x-poff.left, top: cur.y-poff.top});
|
517
|
+
this.marker.setStyle({left: cur.x-poff.left+'px', top: cur.y-poff.top+'px'});
|
516
518
|
},
|
517
519
|
|
518
520
|
getModel: function() {
|
data/concrete/inline_editor.js
CHANGED
@@ -11,43 +11,44 @@ Concrete.InlineEditor = Class.create(Concrete.BasicInlineEditor, {
|
|
11
11
|
},
|
12
12
|
|
13
13
|
edit: function(element, opt) {
|
14
|
-
var init = opt.init || ""
|
15
|
-
var partial = opt.partial || false
|
14
|
+
var init = opt.init || "";
|
15
|
+
var partial = opt.partial || false;
|
16
16
|
if (opt.options instanceof Array) {
|
17
|
-
this._options = opt.options
|
18
|
-
this._regexp = undefined
|
17
|
+
this._options = opt.options;
|
18
|
+
this._regexp = undefined;
|
19
19
|
}
|
20
20
|
else if (opt.options instanceof RegExp) {
|
21
|
-
this._options = []
|
22
|
-
this._regexp = opt.options
|
21
|
+
this._options = [];
|
22
|
+
this._regexp = opt.options;
|
23
23
|
}
|
24
24
|
else {
|
25
|
-
this._options = []
|
25
|
+
this._options = [];
|
26
|
+
this._regexp = undefined;
|
26
27
|
}
|
27
|
-
this._successHandler = opt.onSuccess
|
28
|
-
this._failureHandler = opt.onFailure
|
29
|
-
this.isActive = true
|
28
|
+
this._successHandler = opt.onSuccess;
|
29
|
+
this._failureHandler = opt.onFailure;
|
30
|
+
this.isActive = true;
|
30
31
|
this.stateChangeFunc(this.isActive);
|
31
|
-
this.show(element, init, partial, this._options)
|
32
|
+
this.show(element, init, partial, this._options);
|
32
33
|
},
|
33
34
|
|
34
35
|
finish: function() {
|
35
36
|
if ((this._options.size() == 0 || this._options.include(this.getText())) && (this._regexp == undefined || this._regexp.test(this.getText()))) {
|
36
|
-
this.isActive = false
|
37
|
+
this.isActive = false;
|
37
38
|
this.stateChangeFunc(this.isActive);
|
38
|
-
this.hide()
|
39
|
-
if (this._successHandler) this._successHandler(this.getText())
|
39
|
+
this.hide();
|
40
|
+
if (this._successHandler) this._successHandler(this.getText());
|
40
41
|
}
|
41
42
|
else {
|
42
|
-
this.setError()
|
43
|
+
this.setError();
|
43
44
|
}
|
44
45
|
},
|
45
46
|
|
46
47
|
cancel: function() {
|
47
|
-
this.isActive = false
|
48
|
+
this.isActive = false;
|
48
49
|
this.stateChangeFunc(this.isActive);
|
49
|
-
this.hide()
|
50
|
-
if (this._failureHandler) this._failureHandler()
|
50
|
+
this.hide();
|
51
|
+
if (this._failureHandler) this._failureHandler();
|
51
52
|
}
|
52
53
|
|
53
54
|
})
|
@@ -20,7 +20,7 @@ Concrete.UI.AbstractDialog = Class.create({
|
|
20
20
|
}.bind(this));
|
21
21
|
Event.observe(window, 'mousemove', function(event) {
|
22
22
|
if (this.moving) {
|
23
|
-
this.dialogElement.setStyle({left: event.clientX-this.moveOffset.left, top: event.clientY-this.moveOffset.top});
|
23
|
+
this.dialogElement.setStyle({left: event.clientX-this.moveOffset.left+'px', top: event.clientY-this.moveOffset.top+'px'});
|
24
24
|
}
|
25
25
|
}.bind(this));
|
26
26
|
Event.observe(window, 'click', function(event) {
|
@@ -62,8 +62,8 @@ Concrete.UI.AbstractDialog = Class.create({
|
|
62
62
|
},
|
63
63
|
|
64
64
|
open: function() {
|
65
|
-
this.dialogElement.setStyle({left: (window.innerWidth-this.dialogElement.getWidth())/2, top: (window.innerHeight-this.dialogElement.getHeight())/2});
|
66
|
-
this.dialogElement.down(".shadow").setStyle({width: this.dialogElement.getWidth()+10, height: this.dialogElement.getHeight()+10});
|
65
|
+
this.dialogElement.setStyle({left: (window.innerWidth-this.dialogElement.getWidth())/2+'px', top: (window.innerHeight-this.dialogElement.getHeight())/2+'px'});
|
66
|
+
this.dialogElement.down(".shadow").setStyle({width: this.dialogElement.getWidth()+10+'px', height: this.dialogElement.getHeight()+10+'px'});
|
67
67
|
this.dialogElement.show();
|
68
68
|
this.dialogElement.down("input").select();
|
69
69
|
this.active = true;
|
@@ -45,10 +45,10 @@ Concrete.UI.LayoutManager = Class.create({
|
|
45
45
|
var sb = this.sidebar;
|
46
46
|
var sbd = this._sidebarDrag;
|
47
47
|
var main = this.main;
|
48
|
-
tb.setStyle({left: 0, top: 0, width: window.innerWidth});
|
49
|
-
sb.setStyle({left: 0, top: tb.getHeight(), width: this._sidebarWidth, height: window.innerHeight-tb.getHeight()});
|
50
|
-
sbd.setStyle({left: sb.getWidth(), top: tb.getHeight(), height: window.innerHeight-tb.getHeight()});
|
51
|
-
main.setStyle({left: sb.getWidth()+sbd.getWidth(), top: tb.getHeight(), width: window.innerWidth-sb.getWidth()-sbd.getWidth(), height: window.innerHeight-tb.getHeight()});
|
48
|
+
tb.setStyle({left: 0, top: 0, width: window.innerWidth+'px'});
|
49
|
+
sb.setStyle({left: 0, top: tb.getHeight()+'px', width: this._sidebarWidth+'px', height: window.innerHeight-tb.getHeight()+'px'});
|
50
|
+
sbd.setStyle({left: sb.getWidth()+'px', top: tb.getHeight()+'px', height: window.innerHeight-tb.getHeight()+'px'});
|
51
|
+
main.setStyle({left: sb.getWidth()+sbd.getWidth()+'px', top: tb.getHeight()+'px', width: window.innerWidth-sb.getWidth()-sbd.getWidth()+'px', height: window.innerHeight-tb.getHeight()+'px'});
|
52
52
|
}
|
53
53
|
|
54
54
|
});
|
data/concrete/ui/toolbar.js
CHANGED
@@ -39,7 +39,7 @@ Concrete.UI.Toolbar = Class.create({
|
|
39
39
|
if (left > document.viewport.getDimensions().width - this.popup.getWidth()) {
|
40
40
|
left = document.viewport.getDimensions().width - this.popup.getWidth();
|
41
41
|
}
|
42
|
-
this.popup.setStyle({left: left, top: event.clientY+20});
|
42
|
+
this.popup.setStyle({left: left+'px', top: event.clientY+20+'px'});
|
43
43
|
this.popup.show();
|
44
44
|
}
|
45
45
|
}
|
@@ -753,12 +753,15 @@ cellspacing="0" cellpadding="4">
|
|
753
753
|
templates.html
|
754
754
|
<syntax 2>
|
755
755
|
style.css
|
756
|
-
templates.html
|
756
|
+
templates.html
|
757
|
+
...
|
758
|
+
<syntax root dir n></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’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" in case the syntax contains a HTML part. It should also contain a CSS file
|
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’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>
|
759
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>
|
760
763
|
<h3 id="_setting_up_the_server">Setting up the Server</h3><div style="clear:left"></div>
|
761
|
-
<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
|
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>
|
762
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’s home directory.</p></div>
|
763
766
|
<div class="listingblock">
|
764
767
|
<div class="content">
|
@@ -1047,7 +1050,7 @@ cellspacing="0" cellpadding="4">
|
|
1047
1050
|
</div>
|
1048
1051
|
<div id="footer">
|
1049
1052
|
<div id="footer-text">
|
1050
|
-
Last updated 2010-06-
|
1053
|
+
Last updated 2010-06-30 08:10:12 WEDT
|
1051
1054
|
</div>
|
1052
1055
|
</div>
|
1053
1056
|
</body>
|
@@ -353,16 +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" in case the syntax contains a HTML part. It should also contain a CSS file
|
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.
|
361
|
+
|
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.
|
359
363
|
|
360
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.
|
361
365
|
|
362
366
|
|
363
367
|
=== Setting up the Server ===
|
364
368
|
|
365
|
-
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_.
|
366
370
|
|
367
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.
|
368
372
|
|
@@ -498,7 +498,8 @@ If a constraint is violated for an element or feature, the element/feature will
|
|
498
498
|
Move the mouse over such an element in order to show the error message in a popup window.</p></div>
|
499
499
|
<h3 id="_model_information">Model Information</h3><div style="clear:left"></div>
|
500
500
|
<div class="paragraph"><p>Depending on the concrete syntax, the feature names might be hidden. When moving the mouse over a model value, the name of the
|
501
|
-
feature that value belongs to will be shown. Also for references, the full identifier
|
501
|
+
feature that value belongs to will be shown. Also for references, the full identifier, and in case of external references to other models,
|
502
|
+
the module identifier will be shown.</p></div>
|
502
503
|
</div>
|
503
504
|
<h2 id="_workbench">Workbench</h2>
|
504
505
|
<div class="sectionbody">
|
@@ -687,7 +688,7 @@ cellspacing="0" cellpadding="4">
|
|
687
688
|
</div>
|
688
689
|
<div id="footer">
|
689
690
|
<div id="footer-text">
|
690
|
-
Last updated 2010-06-
|
691
|
+
Last updated 2010-06-25 07:58:46 WEDT
|
691
692
|
</div>
|
692
693
|
</div>
|
693
694
|
</body>
|
@@ -159,7 +159,8 @@ Move the mouse over such an element in order to show the error message in a popu
|
|
159
159
|
=== Model Information ===
|
160
160
|
|
161
161
|
Depending on the concrete syntax, the feature names might be hidden. When moving the mouse over a model value, the name of the
|
162
|
-
feature that value belongs to will be shown. Also for references, the full identifier
|
162
|
+
feature that value belongs to will be shown. Also for references, the full identifier, and in case of external references to other models,
|
163
|
+
the module identifier will be shown.
|
163
164
|
|
164
165
|
|
165
166
|
== Workbench ==
|
@@ -1,9 +1,11 @@
|
|
1
|
+
require 'andand'
|
2
|
+
|
1
3
|
module Concrete
|
2
4
|
|
3
5
|
class ConcreteSyntaxProvider
|
4
6
|
|
5
7
|
class ConcreteSyntax
|
6
|
-
attr_accessor :ident, :dir, :name, :desc
|
8
|
+
attr_accessor :ident, :dir, :name, :desc
|
7
9
|
end
|
8
10
|
|
9
11
|
def initialize(configDirs, logger, config=nil)
|
@@ -22,7 +24,7 @@ class ConcreteSyntaxProvider
|
|
22
24
|
|
23
25
|
def selectSyntax(ident)
|
24
26
|
@selectedSyntax = syntaxes.find{|s| s.ident == ident}
|
25
|
-
@config.andand.storeValue("concrete_syntax", ident.to_s)
|
27
|
+
@config.andand.storeValue("concrete_syntax", ident.to_s) if @selectedSyntax
|
26
28
|
end
|
27
29
|
|
28
30
|
def syntaxesAsJson
|
@@ -38,25 +40,17 @@ class ConcreteSyntaxProvider
|
|
38
40
|
Dir.entries(cd).sort.each do |sd|
|
39
41
|
next if sd == "." || sd == ".."
|
40
42
|
syntaxDir = cd+"/"+sd
|
41
|
-
templatesFile = syntaxDir + "/templates.html"
|
42
|
-
styleFile = syntaxDir + "/style.css"
|
43
|
-
unless File.exist?(templatesFile) || File.exist?(styleFile)
|
44
|
-
@logger.warn("Concrete syntax dir without a templates.html or a style.css: #{syntaxDir}")
|
45
|
-
next
|
46
|
-
end
|
47
43
|
s = ConcreteSyntax.new
|
48
44
|
s.ident = syntaxDir.gsub("\\","/")
|
49
45
|
s.dir = syntaxDir
|
50
46
|
s.name = sd.split(/[_\W]/).collect{|w| w.capitalize}.join(" ")
|
51
47
|
s.desc = ""
|
52
|
-
s.cssStyleFile = styleFile if File.exist?(styleFile)
|
53
|
-
s.htmlTemplates = File.read(templatesFile) if File.exist?(templatesFile)
|
54
48
|
result << s
|
55
49
|
end
|
56
50
|
end
|
57
51
|
result
|
58
52
|
end
|
59
|
-
|
53
|
+
|
60
54
|
end
|
61
55
|
|
62
56
|
end
|
@@ -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
|
+
|
data/lib/concrete/server.rb
CHANGED
@@ -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 @@ class Server
|
|
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 @@ class Server
|
|
27
34
|
def handleRequest(req, res)
|
28
35
|
if req.path == "/"
|
29
36
|
editorHtml = File.read(@htmlRoot+"/editor.html")
|
30
|
-
|
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
|
-
|
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 @@ class Server
|
|
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
|
data/rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rake/gempackagetask'
|
|
2
2
|
|
3
3
|
GemSpec = Gem::Specification.new do |s|
|
4
4
|
s.name = "concrete"
|
5
|
-
s.version = "0.2.
|
5
|
+
s.version = "0.2.1"
|
6
6
|
s.date = Time.now.strftime("%Y-%m-%d")
|
7
7
|
s.summary = %q{Concrete is a lightweight, web-based model editor which can be configured for different DSLs}
|
8
8
|
s.authors = ["Martin Thiede"]
|
@@ -0,0 +1,127 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)+"/../lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'concrete/concrete_syntax_provider'
|
6
|
+
begin
|
7
|
+
require 'haml'
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
|
11
|
+
class ConcreteSyntaxProviderTest < Test::Unit::TestCase
|
12
|
+
|
13
|
+
TestDir = File.dirname(__FILE__)+"/concrete_syntax_provider_test"
|
14
|
+
|
15
|
+
class LoggerMock
|
16
|
+
attr_reader :messages
|
17
|
+
def initialize; @messages = []; end
|
18
|
+
def info(msg)
|
19
|
+
@messages << "INFO: #{msg}"
|
20
|
+
end
|
21
|
+
def warn(msg)
|
22
|
+
@messages << "WARN: #{msg}"
|
23
|
+
end
|
24
|
+
def error(msg)
|
25
|
+
@messages << "ERROR: #{msg}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ConfigMock
|
30
|
+
attr_reader :values
|
31
|
+
def initialize; @values = {}; end
|
32
|
+
def loadValue(ident)
|
33
|
+
@values[ident]
|
34
|
+
end
|
35
|
+
def storeValue(ident, value)
|
36
|
+
@values[ident] = value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_syntaxes_no_dirs
|
41
|
+
logger = LoggerMock.new
|
42
|
+
sp = Concrete::ConcreteSyntaxProvider.new([], logger)
|
43
|
+
assert_equal [], sp.syntaxes
|
44
|
+
assert_equal '{ "syntaxes": [], "selected": "" }', sp.syntaxesAsJson
|
45
|
+
assert_equal [], logger.messages
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_syntaxes_non_existing_dir
|
49
|
+
logger = LoggerMock.new
|
50
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/doesNotExist"], logger)
|
51
|
+
assert_equal [], sp.syntaxes
|
52
|
+
assert_equal '{ "syntaxes": [], "selected": "" }', sp.syntaxesAsJson
|
53
|
+
assert_equal [], logger.messages
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_syntaxes_no_syntaxes
|
57
|
+
logger = LoggerMock.new
|
58
|
+
raise "bad preconditions" unless File.exist?(TestDir+"/syntaxDir0")
|
59
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir0"], logger)
|
60
|
+
assert_equal [], sp.syntaxes
|
61
|
+
assert_equal '{ "syntaxes": [], "selected": "" }', sp.syntaxesAsJson
|
62
|
+
assert_equal [], logger.messages
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_syntaxes_empty_syntax
|
66
|
+
logger = LoggerMock.new
|
67
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir1"], logger)
|
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
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_syntaxes_common
|
77
|
+
logger = LoggerMock.new
|
78
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir2"], logger)
|
79
|
+
syntaxes = sp.syntaxes
|
80
|
+
assert_equal 1, syntaxes.size
|
81
|
+
assert_equal "First Syntax", syntaxes.first.name
|
82
|
+
assert_equal TestDir+"/syntaxDir2/first_syntax", syntaxes.first.ident
|
83
|
+
assert_equal '{ "syntaxes": [' +
|
84
|
+
'{ "ident": "'+TestDir+'/syntaxDir2/first_syntax", "name": "First Syntax" }' +
|
85
|
+
'], "selected": "" }', sp.syntaxesAsJson
|
86
|
+
assert_equal [], logger.messages
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_selectSyntax_no_syntax
|
90
|
+
logger = LoggerMock.new
|
91
|
+
sp = Concrete::ConcreteSyntaxProvider.new([], logger)
|
92
|
+
assert_nothing_raised do
|
93
|
+
sp.selectSyntax("dummy")
|
94
|
+
end
|
95
|
+
assert_nil sp.selectedSyntax
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_selectSyntax_no_config
|
99
|
+
logger = LoggerMock.new
|
100
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir2", TestDir+"/syntaxDir3"], logger)
|
101
|
+
assert_nothing_raised do
|
102
|
+
sp.selectSyntax("dummy")
|
103
|
+
end
|
104
|
+
assert_equal "First Syntax", sp.selectedSyntax.name
|
105
|
+
sp.selectSyntax(TestDir+"/syntaxDir3/haml_syntax")
|
106
|
+
assert_equal "Haml Syntax", sp.selectedSyntax.name
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_selectSyntax_load_config
|
110
|
+
logger = LoggerMock.new
|
111
|
+
config = ConfigMock.new
|
112
|
+
config.values["concrete_syntax"] = TestDir+"/syntaxDir3/haml_syntax"
|
113
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir2", TestDir+"/syntaxDir3"], logger, config)
|
114
|
+
assert_equal "Haml Syntax", sp.selectedSyntax.name
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_selectSyntax_store_config
|
118
|
+
logger = LoggerMock.new
|
119
|
+
config = ConfigMock.new
|
120
|
+
sp = Concrete::ConcreteSyntaxProvider.new([TestDir+"/syntaxDir2", TestDir+"/syntaxDir3"], logger, config)
|
121
|
+
sp.selectSyntax("dummy")
|
122
|
+
assert_equal({}, config.values)
|
123
|
+
sp.selectSyntax(TestDir+"/syntaxDir3/haml_syntax" )
|
124
|
+
assert_equal({"concrete_syntax" => TestDir+"/syntaxDir3/haml_syntax" }, config.values)
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
%p Haml code!
|
data/test/concrete_test.rb
CHANGED
@@ -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
|
+
|
@@ -1,4 +1,37 @@
|
|
1
1
|
{ "_class": "EPackage", "name": "ECore", "elements": [
|
2
|
+
{ "_class": "EClass", "name": "EAnnotation", "elements": [
|
3
|
+
{ "_class": "EAttribute", "name": "source" },
|
4
|
+
{ "_class": "EReference", "name": "eModelElement" },
|
5
|
+
{ "_class": "EReference", "name": "details" },
|
6
|
+
{ "_class": "EReference", "name": "contents" },
|
7
|
+
{ "_class": "EReference", "name": "references" }] },
|
8
|
+
{ "_class": "EClass", "name": "EDataType", "elements": [
|
9
|
+
{ "_class": "EAttribute", "name": "serializable" }] },
|
10
|
+
{ "_class": "EClass", "name": "EClassifier", "elements": [
|
11
|
+
{ "_class": "EAttribute", "name": "defaultValue" },
|
12
|
+
{ "_class": "EAttribute", "name": "instanceClass" },
|
13
|
+
{ "_class": "EAttribute", "name": "instanceClassName" },
|
14
|
+
{ "_class": "EReference", "name": "ePackage" }] },
|
15
|
+
{ "_class": "EClass", "name": "EOperation", "elements": [
|
16
|
+
{ "_class": "EReference", "name": "eContainingClass" },
|
17
|
+
{ "_class": "EReference", "name": "eParameters" },
|
18
|
+
{ "_class": "EReference", "name": "eExceptions" }] },
|
19
|
+
{ "_class": "EClass", "name": "EClass", "elements": [
|
20
|
+
{ "_class": "EAttribute", "name": "abstract" },
|
21
|
+
{ "_class": "EAttribute", "name": "interface" },
|
22
|
+
{ "_class": "EReference", "name": "eIDAttribute" },
|
23
|
+
{ "_class": "EReference", "name": "eAllAttributes" },
|
24
|
+
{ "_class": "EReference", "name": "eAllContainments" },
|
25
|
+
{ "_class": "EReference", "name": "eAllOperations" },
|
26
|
+
{ "_class": "EReference", "name": "eAllReferences" },
|
27
|
+
{ "_class": "EReference", "name": "eAllStructuralFeatures" },
|
28
|
+
{ "_class": "EReference", "name": "eAllSuperTypes" },
|
29
|
+
{ "_class": "EReference", "name": "eAttributes" },
|
30
|
+
{ "_class": "EReference", "name": "eReferences" },
|
31
|
+
{ "_class": "EReference", "name": "eOperations" },
|
32
|
+
{ "_class": "EReference", "name": "eStructuralFeatures" },
|
33
|
+
{ "_class": "EReference", "name": "eSuperTypes" },
|
34
|
+
{ "_class": "EReference", "name": "eSubTypes" }] },
|
2
35
|
{ "_class": "EClass", "name": "EModelElement", "elements": [
|
3
36
|
{ "_class": "EReference", "name": "eAnnotations" }] },
|
4
37
|
{ "_class": "EClass", "name": "EReference", "elements": [
|
@@ -49,37 +82,4 @@
|
|
49
82
|
{ "_class": "EClass", "name": "EEnum", "elements": [
|
50
83
|
{ "_class": "EReference", "name": "eLiterals" }] },
|
51
84
|
{ "_class": "EClass", "name": "EParameter", "elements": [
|
52
|
-
{ "_class": "EReference", "name": "eOperation" }] }
|
53
|
-
{ "_class": "EClass", "name": "EAnnotation", "elements": [
|
54
|
-
{ "_class": "EAttribute", "name": "source" },
|
55
|
-
{ "_class": "EReference", "name": "eModelElement" },
|
56
|
-
{ "_class": "EReference", "name": "details" },
|
57
|
-
{ "_class": "EReference", "name": "contents" },
|
58
|
-
{ "_class": "EReference", "name": "references" }] },
|
59
|
-
{ "_class": "EClass", "name": "EDataType", "elements": [
|
60
|
-
{ "_class": "EAttribute", "name": "serializable" }] },
|
61
|
-
{ "_class": "EClass", "name": "EClassifier", "elements": [
|
62
|
-
{ "_class": "EAttribute", "name": "defaultValue" },
|
63
|
-
{ "_class": "EAttribute", "name": "instanceClass" },
|
64
|
-
{ "_class": "EAttribute", "name": "instanceClassName" },
|
65
|
-
{ "_class": "EReference", "name": "ePackage" }] },
|
66
|
-
{ "_class": "EClass", "name": "EOperation", "elements": [
|
67
|
-
{ "_class": "EReference", "name": "eContainingClass" },
|
68
|
-
{ "_class": "EReference", "name": "eParameters" },
|
69
|
-
{ "_class": "EReference", "name": "eExceptions" }] },
|
70
|
-
{ "_class": "EClass", "name": "EClass", "elements": [
|
71
|
-
{ "_class": "EAttribute", "name": "abstract" },
|
72
|
-
{ "_class": "EAttribute", "name": "interface" },
|
73
|
-
{ "_class": "EReference", "name": "eIDAttribute" },
|
74
|
-
{ "_class": "EReference", "name": "eAllAttributes" },
|
75
|
-
{ "_class": "EReference", "name": "eAllContainments" },
|
76
|
-
{ "_class": "EReference", "name": "eAllOperations" },
|
77
|
-
{ "_class": "EReference", "name": "eAllReferences" },
|
78
|
-
{ "_class": "EReference", "name": "eAllStructuralFeatures" },
|
79
|
-
{ "_class": "EReference", "name": "eAllSuperTypes" },
|
80
|
-
{ "_class": "EReference", "name": "eAttributes" },
|
81
|
-
{ "_class": "EReference", "name": "eReferences" },
|
82
|
-
{ "_class": "EReference", "name": "eOperations" },
|
83
|
-
{ "_class": "EReference", "name": "eStructuralFeatures" },
|
84
|
-
{ "_class": "EReference", "name": "eSuperTypes" },
|
85
|
-
{ "_class": "EReference", "name": "eSubTypes" }] }] }
|
85
|
+
{ "_class": "EReference", "name": "eOperation" }] }] }
|
metadata
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concrete
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 1
|
9
|
+
version: 0.2.1
|
5
10
|
platform: ruby
|
6
11
|
authors:
|
7
12
|
- Martin Thiede
|
@@ -9,29 +14,39 @@ autorequire:
|
|
9
14
|
bindir: bin
|
10
15
|
cert_chain: []
|
11
16
|
|
12
|
-
date: 2010-
|
17
|
+
date: 2010-09-21 00:00:00 +02:00
|
13
18
|
default_executable:
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: rgen
|
17
|
-
|
18
|
-
|
19
|
-
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
20
25
|
requirements:
|
21
26
|
- - ">="
|
22
27
|
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 5
|
31
|
+
- 2
|
23
32
|
version: 0.5.2
|
24
|
-
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
25
35
|
- !ruby/object:Gem::Dependency
|
26
36
|
name: andand
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
30
40
|
requirements:
|
31
41
|
- - ">="
|
32
42
|
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 3
|
46
|
+
- 1
|
33
47
|
version: 1.3.1
|
34
|
-
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
35
50
|
description:
|
36
51
|
email:
|
37
52
|
executables: []
|
@@ -127,6 +142,7 @@ files:
|
|
127
142
|
- lib/concrete/concrete_syntax_provider.rb
|
128
143
|
- lib/concrete/config.rb
|
129
144
|
- lib/concrete/file_cache_map.rb
|
145
|
+
- lib/concrete/haml_eval_context.rb
|
130
146
|
- lib/concrete/index_builder.rb
|
131
147
|
- lib/concrete/metamodel/concrete_mmm.rb
|
132
148
|
- lib/concrete/metamodel/ecore_to_concrete.rb
|
@@ -143,9 +159,14 @@ files:
|
|
143
159
|
- redist/scriptaculous/slider.js
|
144
160
|
- redist/scriptaculous/sound.js
|
145
161
|
- redist/scriptaculous/unittest.js
|
162
|
+
- test/concrete_syntax_provider_test/syntaxDir2/first_syntax/style.css
|
163
|
+
- test/concrete_syntax_provider_test/syntaxDir3/haml_syntax/templates.haml
|
164
|
+
- test/concrete_syntax_provider_test/syntaxDir3/html_syntax/templates.html
|
165
|
+
- test/concrete_syntax_provider_test.rb
|
146
166
|
- test/concrete_test.rb
|
147
167
|
- test/file_cache_map_test/testdir/fileA
|
148
168
|
- test/file_cache_map_test.rb
|
169
|
+
- test/haml_eval_context_test.rb
|
149
170
|
- test/index_builder_test/ecore_index.js
|
150
171
|
- test/index_builder_test/ecore_index_expected.js
|
151
172
|
- test/index_builder_test.rb
|
@@ -179,21 +200,27 @@ require_paths:
|
|
179
200
|
- lib
|
180
201
|
- lib
|
181
202
|
required_ruby_version: !ruby/object:Gem::Requirement
|
203
|
+
none: false
|
182
204
|
requirements:
|
183
205
|
- - ">="
|
184
206
|
- !ruby/object:Gem::Version
|
207
|
+
segments:
|
208
|
+
- 1
|
209
|
+
- 8
|
210
|
+
- 6
|
185
211
|
version: 1.8.6
|
186
|
-
version:
|
187
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
213
|
+
none: false
|
188
214
|
requirements:
|
189
215
|
- - ">="
|
190
216
|
- !ruby/object:Gem::Version
|
217
|
+
segments:
|
218
|
+
- 0
|
191
219
|
version: "0"
|
192
|
-
version:
|
193
220
|
requirements: []
|
194
221
|
|
195
222
|
rubyforge_project:
|
196
|
-
rubygems_version: 1.3.
|
223
|
+
rubygems_version: 1.3.7
|
197
224
|
signing_key:
|
198
225
|
specification_version: 3
|
199
226
|
summary: Concrete is a lightweight, web-based model editor which can be configured for different DSLs
|