proteus 0.9.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.
- data/COPYING +674 -0
- data/README +88 -0
- data/Rakefile +33 -0
- data/bin/pro +189 -0
- data/lib/ClassParser.rb +84 -0
- data/lib/ComponentClass.rb +51 -0
- data/lib/ComponentInstance.rb +76 -0
- data/lib/DefinitionHelper.rb +157 -0
- data/lib/DefinitionParser.rb +107 -0
- data/lib/Facade.rb +116 -0
- data/lib/FileHelper.rb +80 -0
- data/lib/InputParser.rb +241 -0
- data/lib/InstanceParser.rb +65 -0
- data/lib/InstanceProxy.rb +100 -0
- data/lib/PropertyHash.rb +64 -0
- data/lib/TemplateRenderer.rb +128 -0
- data/lib/exceptions.rb +50 -0
- data/test/spec/ClassParser_spec.rb +314 -0
- data/test/spec/ComponentClass_spec.rb +44 -0
- data/test/spec/ComponentInstance_spec.rb +50 -0
- data/test/spec/DefinitionHelper_spec.rb +134 -0
- data/test/spec/DefinitionParser_spec.rb +236 -0
- data/test/spec/Facade_spec.rb +44 -0
- data/test/spec/FileHelper_spec.rb +113 -0
- data/test/spec/InputParser_spec.rb +424 -0
- data/test/spec/InstanceParser_spec.rb +125 -0
- data/test/spec/TemplateRenderer_spec.rb +54 -0
- data/test/spec/lib/NS1/TestComponent10.def +4 -0
- data/test/spec/lib/NS1/TestComponent11.def +4 -0
- data/test/spec/lib/NS1/TestComponent4.def +4 -0
- data/test/spec/lib/NS1/TestComponent5.def +4 -0
- data/test/spec/lib/NS1/TestComponent8.def +4 -0
- data/test/spec/lib/NS1/TestComponent9.def +4 -0
- data/test/spec/lib/NS2/TestComponent6.def +4 -0
- data/test/spec/lib/TestComponent1.def +4 -0
- data/test/spec/lib/TestComponent3.def +4 -0
- data/test/spec/test_lib_dir1/File1.def +0 -0
- data/test/spec/test_lib_dir1/File3.def +0 -0
- data/test/spec/test_lib_dir1/NS1/File4.def +0 -0
- data/test/spec/test_lib_dir1/NS1/NS2/File5.def +0 -0
- data/test/spec/test_lib_dir2/File1.def +0 -0
- data/test/spec/test_lib_dir2/File2.def +0 -0
- metadata +113 -0
data/README
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
Welcome to Proteus
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
Proteus is an abstraction of general document definition and display markup that
|
9
|
+
allows you to create and reuse complex patterns of structured content.
|
10
|
+
|
11
|
+
The purpose of Proteus is to allow you to describe documents in terms of their
|
12
|
+
content, abstracting away from the often cumbersome markup or syntax of its
|
13
|
+
native form (for example in HTML).
|
14
|
+
|
15
|
+
|
16
|
+
Project Goals:
|
17
|
+
|
18
|
+
1. Content-orientation: we should be describing content, not the structure
|
19
|
+
used to display it.
|
20
|
+
2. Human-readability: it should be easy for a human to write and maintain
|
21
|
+
documents.
|
22
|
+
3. Flexibility: it should be easy to adapt the solution to solve a wide
|
23
|
+
variety of problems.
|
24
|
+
4. Extensibility: it should be easy to change the behaviour of the system as
|
25
|
+
it is required.
|
26
|
+
|
27
|
+
|
28
|
+
For Users:
|
29
|
+
|
30
|
+
Proteus works by interpreting document definitions in YAML, for example:
|
31
|
+
|
32
|
+
Form:
|
33
|
+
- Control:
|
34
|
+
name: name
|
35
|
+
- Control:
|
36
|
+
name: age
|
37
|
+
- Control:
|
38
|
+
name: gender
|
39
|
+
type: select
|
40
|
+
options:
|
41
|
+
- male
|
42
|
+
- female
|
43
|
+
|
44
|
+
Each camelcase node indicates a "component" - a piece of content which displays
|
45
|
+
in a unique way. In the above example, the "Form" and "Control" nodes are
|
46
|
+
components and the rest of the information describes those components.
|
47
|
+
|
48
|
+
|
49
|
+
For Developers:
|
50
|
+
|
51
|
+
Proteus provides a simple system for describing new component types that
|
52
|
+
permits inheritance and composition of existing types, again in human readable
|
53
|
+
syntax. This allows users to create custom languages from existing components
|
54
|
+
quickly and easily.
|
55
|
+
|
56
|
+
|
57
|
+
The Name:
|
58
|
+
|
59
|
+
In Greek mythology, Proteus is an early sea-god who can foretell the future
|
60
|
+
and change his shape. From this feature of Proteus comes the adjective protean,
|
61
|
+
with the general meaning of "versatile", "mutable", "capable of assuming many
|
62
|
+
forms". -- Wikipedia
|
63
|
+
|
64
|
+
|
65
|
+
Homepage:
|
66
|
+
|
67
|
+
More information is available on the Proteus website,
|
68
|
+
|
69
|
+
proteus.rubyforge.org
|
70
|
+
|
71
|
+
or on Github,
|
72
|
+
|
73
|
+
github.com/wmadden/proteus
|
74
|
+
|
75
|
+
|
76
|
+
Licensing:
|
77
|
+
Proteus is free software: you can redistribute it and/or modify it under the
|
78
|
+
terms of the GNU General Public License as published by the Free Software
|
79
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
80
|
+
version.
|
81
|
+
|
82
|
+
Proteus is distributed in the hope that it will be useful, but WITHOUT ANY
|
83
|
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
84
|
+
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
85
|
+
|
86
|
+
You should have received a copy of the GNU General Public License along with
|
87
|
+
Proteus. If not, see <http://www.gnu.org/licenses/>.
|
88
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'cucumber/rake/task'
|
4
|
+
|
5
|
+
require 'rake'
|
6
|
+
require 'spec/rake/spectask'
|
7
|
+
|
8
|
+
desc "Run all examples with RCov"
|
9
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
10
|
+
t.spec_files = FileList['test/spec/**/*.rb']
|
11
|
+
t.rcov = true
|
12
|
+
t.rcov_opts = ['--exclude', 'spec']
|
13
|
+
t.rcov_dir = 'coverage/spec/'
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'spec/rake/verify_rcov'
|
17
|
+
|
18
|
+
RCov::VerifyTask.new(:verify_rcov => :spec) do |t|
|
19
|
+
t.threshold = 100.0
|
20
|
+
t.index_html = 'coverage/spec/verify/index.html'
|
21
|
+
end
|
22
|
+
|
23
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
24
|
+
t.cucumber_opts = "--format pretty"
|
25
|
+
t.rcov = true
|
26
|
+
t.rcov_opts << %[-o "test/features/cucumber"]
|
27
|
+
end
|
28
|
+
|
29
|
+
task :doc do |t|
|
30
|
+
`rdoc -aUd src/ -o doc/api`
|
31
|
+
end
|
32
|
+
|
33
|
+
task :default => [:spec, :features, :verify_rcov]
|
data/bin/pro
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
################################################################################
|
3
|
+
# proteus
|
4
|
+
#
|
5
|
+
# Executable interface to the system.
|
6
|
+
# -----------------------------------------------------------------------------
|
7
|
+
# (C) Copyright 2009 William Madden
|
8
|
+
#
|
9
|
+
# This file is part of Proteus.
|
10
|
+
#
|
11
|
+
# Proteus is free software: you can redistribute it and/or modify it under the
|
12
|
+
# terms of the GNU General Public License as published by the Free Software
|
13
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
14
|
+
# version.
|
15
|
+
#
|
16
|
+
# Proteus is distributed in the hope that it will be useful, but WITHOUT ANY
|
17
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
18
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
19
|
+
#
|
20
|
+
# You should have received a copy of the GNU General Public License along with
|
21
|
+
# Proteus. If not, see <http://www.gnu.org/licenses/>.
|
22
|
+
################################################################################
|
23
|
+
|
24
|
+
require 'getoptlong'
|
25
|
+
require 'rdoc/usage'
|
26
|
+
|
27
|
+
require 'Facade.rb'
|
28
|
+
require 'TemplateRenderer.rb'
|
29
|
+
require 'exceptions.rb'
|
30
|
+
|
31
|
+
include Proteus
|
32
|
+
|
33
|
+
PROTEUS_VERSION = "v0.9.0"
|
34
|
+
|
35
|
+
USAGE =
|
36
|
+
"""
|
37
|
+
Usage:
|
38
|
+
proteus [--path PATH] FILE
|
39
|
+
proteus --inline COMPONENT [--props PROPERTIES] [CHILDREN]
|
40
|
+
proteus --help
|
41
|
+
|
42
|
+
Options:
|
43
|
+
--help, -h Display this message.
|
44
|
+
|
45
|
+
--inline, -i Parse inline. Takes component information on the
|
46
|
+
command line and outputs the rendered component
|
47
|
+
instance.
|
48
|
+
|
49
|
+
--path, -p Sets the path to search for component definitions.
|
50
|
+
Defaults to /usr/lib/proteus.
|
51
|
+
|
52
|
+
--props, -P Sets the properties for an inline component.
|
53
|
+
|
54
|
+
--no-current, -C Don't add current directory to path.
|
55
|
+
|
56
|
+
--version, -V Output version.
|
57
|
+
|
58
|
+
"""
|
59
|
+
|
60
|
+
PROTEUS_PATH = 'PROTEUS_PATH'
|
61
|
+
|
62
|
+
def exit_error( error )
|
63
|
+
puts error
|
64
|
+
puts USAGE
|
65
|
+
exit( 1 )
|
66
|
+
end
|
67
|
+
|
68
|
+
def parse_props( props )
|
69
|
+
result = {}
|
70
|
+
|
71
|
+
props.split( ',' ).each do |prop|
|
72
|
+
pair = prop.split(':')
|
73
|
+
result[ pair[0] ] = pair[1]
|
74
|
+
end
|
75
|
+
|
76
|
+
return result
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def parse_command_line
|
81
|
+
|
82
|
+
@opts.each do |opt, arg|
|
83
|
+
case opt
|
84
|
+
when '--help', '-h'
|
85
|
+
puts( USAGE )
|
86
|
+
exit( 0 )
|
87
|
+
when '--inline', '-i'
|
88
|
+
@inline = true
|
89
|
+
when '--props', '-P'
|
90
|
+
@props = parse_props( arg )
|
91
|
+
when '--path', '-p'
|
92
|
+
@path = arg
|
93
|
+
when '--namespace', '-n'
|
94
|
+
@current_ns = arg
|
95
|
+
when '--no-current', '-C'
|
96
|
+
@exclude_current = true
|
97
|
+
when '--version', '-V'
|
98
|
+
puts( PROTEUS_VERSION )
|
99
|
+
exit( 0 )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
#------------------------------
|
106
|
+
# ENTRY POINT
|
107
|
+
#------------------------------
|
108
|
+
|
109
|
+
# Option variables
|
110
|
+
@inline = false
|
111
|
+
@path = ENV[ PROTEUS_PATH ] || FileHelper::DEFAULT_PATH
|
112
|
+
@props = {}
|
113
|
+
@current_ns = []
|
114
|
+
@exclude_current = false
|
115
|
+
|
116
|
+
# Valid options
|
117
|
+
@opts = GetoptLong.new(
|
118
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
119
|
+
[ '--path', '-p', GetoptLong::REQUIRED_ARGUMENT ],
|
120
|
+
[ '--inline', '-i', GetoptLong::NO_ARGUMENT ],
|
121
|
+
[ '--props', '-P', GetoptLong::REQUIRED_ARGUMENT ],
|
122
|
+
[ '--namespace', '-n', GetoptLong::REQUIRED_ARGUMENT ],
|
123
|
+
[ '--no-current', '-C', GetoptLong::NO_ARGUMENT ],
|
124
|
+
[ '--version', '-V', GetoptLong::NO_ARGUMENT ]
|
125
|
+
)
|
126
|
+
|
127
|
+
# Parse command line
|
128
|
+
parse_command_line
|
129
|
+
|
130
|
+
# Set up system
|
131
|
+
facade = Facade.new
|
132
|
+
|
133
|
+
facade.path = @path + ':.'
|
134
|
+
|
135
|
+
# Initialize renderer
|
136
|
+
renderer = TemplateRenderer.new
|
137
|
+
|
138
|
+
# Parse input
|
139
|
+
if not @inline
|
140
|
+
|
141
|
+
# from file
|
142
|
+
|
143
|
+
if ARGV.length < 1
|
144
|
+
exit_error( "Error: no target given." )
|
145
|
+
elsif ARGV.length > 1
|
146
|
+
exit_error( "Error: multiple targets given." )
|
147
|
+
end
|
148
|
+
|
149
|
+
file = ARGV[0]
|
150
|
+
|
151
|
+
tree = facade.parse_file( file, @current_ns )
|
152
|
+
|
153
|
+
puts renderer.render( tree )
|
154
|
+
|
155
|
+
else
|
156
|
+
|
157
|
+
# from command line
|
158
|
+
|
159
|
+
if ARGV.length < 1
|
160
|
+
exit_error( "Error: no component identifier given." )
|
161
|
+
end
|
162
|
+
|
163
|
+
component_id = ARGV[0]
|
164
|
+
|
165
|
+
ARGV.shift
|
166
|
+
component_children = ARGV
|
167
|
+
|
168
|
+
component_properties = @props
|
169
|
+
|
170
|
+
class_path = facade.input_parser.parse_component_id( component_id )
|
171
|
+
|
172
|
+
begin
|
173
|
+
component_class = facade.definition_helper.get_class( class_path,
|
174
|
+
@current_ns )
|
175
|
+
rescue Exceptions::DefinitionUnavailable
|
176
|
+
exit_error( "Error: no definition found for '" + component_id + "'." )
|
177
|
+
end
|
178
|
+
|
179
|
+
component_children = facade.input_parser.parse_yaml( component_children,
|
180
|
+
@current_ns )
|
181
|
+
|
182
|
+
instance = ComponentInstance.new( component_class, component_properties,
|
183
|
+
component_children )
|
184
|
+
|
185
|
+
facade.input_parser.parse_instance( instance, @current_ns )
|
186
|
+
|
187
|
+
puts renderer.render( instance )
|
188
|
+
|
189
|
+
end
|
data/lib/ClassParser.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
################################################################################
|
2
|
+
# ClassParser.rb
|
3
|
+
#
|
4
|
+
# Responsible for interpreting YAML for component classes.
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# (C) Copyright 2009 William Madden
|
7
|
+
#
|
8
|
+
# This file is part of Proteus.
|
9
|
+
#
|
10
|
+
# Proteus is free software: you can redistribute it and/or modify it under the
|
11
|
+
# terms of the GNU General Public License as published by the Free Software
|
12
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
13
|
+
# version.
|
14
|
+
#
|
15
|
+
# Proteus is distributed in the hope that it will be useful, but WITHOUT ANY
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
17
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
20
|
+
# Proteus. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
################################################################################
|
22
|
+
|
23
|
+
require File.expand_path( File.join(File.dirname(__FILE__), 'exceptions.rb') )
|
24
|
+
require File.expand_path( File.join(File.dirname(__FILE__), 'ComponentClass.rb') )
|
25
|
+
|
26
|
+
|
27
|
+
module Proteus
|
28
|
+
|
29
|
+
#
|
30
|
+
# Provides functions for parsing component definitions (for component
|
31
|
+
# classes).
|
32
|
+
#
|
33
|
+
class ClassParser
|
34
|
+
|
35
|
+
# The regex for valid class names
|
36
|
+
@@CLASS_RE = /^([A-Z][a-zA-Z_0-9]*)(?:[\s]*<[\s]*([A-Z][a-zA-Z_0-9]*))?$/
|
37
|
+
@@CHILDREN = "children"
|
38
|
+
|
39
|
+
#---------------------------------------------------------------------------
|
40
|
+
#
|
41
|
+
# Methods
|
42
|
+
#
|
43
|
+
#---------------------------------------------------------------------------
|
44
|
+
|
45
|
+
public
|
46
|
+
|
47
|
+
#
|
48
|
+
# Parses YAML and returns the resultant component class.
|
49
|
+
#
|
50
|
+
# yaml: the YAML to parse
|
51
|
+
# new_class: the class instance to parse
|
52
|
+
#
|
53
|
+
def parse_yaml( yaml, new_class = nil )
|
54
|
+
result = new_class || ComponentClass.new
|
55
|
+
|
56
|
+
# Must be a hash of length one containing a hash
|
57
|
+
if not ( yaml.is_a?(Hash) and yaml.length == 1 and
|
58
|
+
(yaml.values[0].nil? or yaml.values[0].is_a?(Hash)) ) then
|
59
|
+
|
60
|
+
raise Exceptions::DefinitionMalformed, "Class definition malformed"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Parse the class name
|
64
|
+
name = yaml.keys[0]
|
65
|
+
match = @@CLASS_RE.match( name )
|
66
|
+
|
67
|
+
# If there's no match, the definition is malformed
|
68
|
+
if match.nil?
|
69
|
+
raise Exceptions::DefinitionMalformed,
|
70
|
+
"Class identifier '" + name + "' malformed."
|
71
|
+
end
|
72
|
+
|
73
|
+
# Set properties of class
|
74
|
+
result.name = match[1]
|
75
|
+
result.parent = match[2]
|
76
|
+
result.properties = yaml.values[0] || {}
|
77
|
+
result.children = result.properties.delete(@@CHILDREN) || []
|
78
|
+
|
79
|
+
return result
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
################################################################################
|
2
|
+
# ComponentClass.rb
|
3
|
+
#
|
4
|
+
# Represents an class of component.
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# (C) Copyright 2009 William Madden
|
7
|
+
#
|
8
|
+
# This file is part of Proteus.
|
9
|
+
#
|
10
|
+
# Proteus is free software: you can redistribute it and/or modify it under the
|
11
|
+
# terms of the GNU General Public License as published by the Free Software
|
12
|
+
# Foundation, either version 3 of the License, or (at your option) any later
|
13
|
+
# version.
|
14
|
+
#
|
15
|
+
# Proteus is distributed in the hope that it will be useful, but WITHOUT ANY
|
16
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
17
|
+
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
18
|
+
#
|
19
|
+
# You should have received a copy of the GNU General Public License along with
|
20
|
+
# Proteus. If not, see <http://www.gnu.org/licenses/>.
|
21
|
+
################################################################################
|
22
|
+
|
23
|
+
module Proteus
|
24
|
+
|
25
|
+
class ComponentClass
|
26
|
+
|
27
|
+
DEFAULT_CLASS = ComponentClass.new()
|
28
|
+
|
29
|
+
#---------------------------------------------------------------------------
|
30
|
+
#
|
31
|
+
# Constructor
|
32
|
+
#
|
33
|
+
#---------------------------------------------------------------------------
|
34
|
+
|
35
|
+
def initialize( )
|
36
|
+
@properties = {}
|
37
|
+
@children = []
|
38
|
+
end
|
39
|
+
|
40
|
+
#---------------------------------------------------------------------------
|
41
|
+
#
|
42
|
+
# Properties
|
43
|
+
#
|
44
|
+
#---------------------------------------------------------------------------
|
45
|
+
|
46
|
+
attr_accessor :name, :namespace, :parent, :properties, :children
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|