aurita-gui 0.3.3 → 0.3.4

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/History.txt CHANGED
@@ -1,4 +1,44 @@
1
- === 0.3.3 / 2009-01-24
1
+
2
+ === 0.3.4 / 2009-01-26
3
+
4
+ * Added retreiving elements from object tree
5
+ via DOM id:
6
+
7
+ x = HTML.build {
8
+ div {
9
+ p {
10
+ span(:id => :nested) { 'content before' }
11
+ }
12
+ }
13
+ }
14
+
15
+ x[:nested].content = 'content after'
16
+
17
+ Also added replacing elements in tree:
18
+
19
+ x[:nested] = HTML.div { 'other element' }
20
+
21
+ * Added marshalling of element hierarchies:
22
+
23
+ e = HTML.build { div.outer(:onclick => js.alert('message')) { p.inner 'nested content' } }
24
+ e.to_s == Element.marshal_load(e.marshal_dump)
25
+
26
+ and thus
27
+
28
+ Element.marshal_load(e.marshal_dump)[0].onclick
29
+ --> "alert('message');"
30
+ Element.marshal_load(e.marshal_dump)[0][0].class
31
+ --> 'inner'
32
+
33
+ This is useful for caching object hierarchies.
34
+
35
+
36
+ * Extended API by convenience shortcuts:
37
+ Content for elements can be set markaby-like now:
38
+
39
+ HTML.div 'content here' :class => :highlighted
40
+
41
+ === 0.3.3 / 2009-01-25
2
42
 
3
43
  * Added form helpers for template rendering.
4
44
  * Extended API by convenience shortcuts:
data/aurita-gui.gemspec CHANGED
@@ -14,7 +14,7 @@ as stand-alone library in any context (such as rails).
14
14
  As there seems to be a lack of ruby form generators, i decided to release this
15
15
  part of Aurita in a single gem with no dependencies.
16
16
  EOF
17
- s.version = '0.3.3'
17
+ s.version = '0.3.4'
18
18
  s.author = 'Tobias Fuchs'
19
19
  s.email = 'fuchs@atomnode.net'
20
20
  s.date = Time.now
@@ -42,6 +42,59 @@ module GUI
42
42
  # by any derived class like Aurita::GUI::Form or
43
43
  # Aurita::GUI::Table.
44
44
  #
45
+ # == Markaby style
46
+ #
47
+ # A syntax similar to markaby is also provided:
48
+ #
49
+ # HTML.build {
50
+ # div.outer {
51
+ # p.inner 'click me'
52
+ # } +
53
+ # div.footer 'text at the bottom'
54
+ # }.to_s
55
+ #
56
+ # -->
57
+ #
58
+ # <div class="outer">
59
+ # <p class="inner">paragraph</p>
60
+ # </div>
61
+ # <div class="footer">text at the bottom</div>
62
+ #
63
+ # == Javascript convenience
64
+ #
65
+ # When including the Javascript helper (aurita-gui/javascript),
66
+ # class HTML is extended by method .js, which provides
67
+ # building Javascript snippets in ruby:
68
+ #
69
+ # e = HTML.build {
70
+ # div.outer(:onclick => js.Wombat.alert('message')) {
71
+ # p.inner 'click me'
72
+ # }
73
+ # }
74
+ # e.to_s
75
+ # -->
76
+ # <div class="outer" onclick="Wombat.alert(\'message\'); ">
77
+ # <p class="inner">click me</p>
78
+ # </div>
79
+ #
80
+ # But watch out for operator precedence! This won't work, as
81
+ # .js() catches the block first:
82
+ #
83
+ # HTML.build {
84
+ # div :header, :onclick => js.funcall { 'i will not be passed to div' }
85
+ # }
86
+ # -->
87
+ # <div class="header" onclick="funcall();"></div>
88
+ #
89
+ #
90
+ # So be explicit, use parentheses:
91
+ #
92
+ # HTML.build {
93
+ # div(:header, :onclick => js.funcall) { 'aaah, much better' }
94
+ # }
95
+ # -->
96
+ # <div class="header" onclick="funcall();">aaah, much better</div>
97
+ #
45
98
  # == Notes
46
99
  #
47
100
  # Double-quotes in tag parameters will be escaped
@@ -139,11 +192,54 @@ module GUI
139
192
 
140
193
  # Redirect methods to setting or retreiving tag
141
194
  # attributes.
142
- def method_missing(meth, value=nil)
143
- return @attrib[meth] unless value or meth.to_s.include? '='
144
- @attrib[meth.to_s.gsub('=','').intern] = value
195
+ # There are several possible routings for method_missing:
196
+ #
197
+ # 1. Setting an attribute (no block, method ends in '=') Example:
198
+ # my_div = HTML.div 'content'
199
+ # my_div.onlick = "alert('foo');"
200
+ # puts my_div.to_s
201
+ # -->
202
+ # <div onclick="alert('foo');">content</div>
203
+ #
204
+ # 2. Retreiving an attribute (no block, method does not end in '='). Example:
205
+ #
206
+ # puts my_div.onlick
207
+ # -->
208
+ # 'alert(\'foo\');'
209
+ #
210
+ # 3. Setting the css class (block or value passed, method does not end in '='). Example:
211
+ #
212
+ # my_div.highlighted { 'content' }
213
+ # or
214
+ # my_div.highlighted 'content'
215
+ #
216
+ # -->
217
+ # <div class="highlighted">content</div>
218
+ #
219
+ def method_missing(meth, value=nil, &block)
220
+ if block_given? then
221
+ @attrib[:class] = meth
222
+ @attrib.update(value) if value.is_a? Hash
223
+ @content = yield
224
+ return self
225
+ elsif !value.nil? && !meth.to_s.include?('=') then
226
+ @attrib[:class] = meth
227
+ case value
228
+ when Hash then
229
+ @attrib.update(value)
230
+ @content = value[:content]
231
+ when String then
232
+ @content = value
233
+ end
234
+ return self
235
+ else
236
+ return @attrib[meth] unless value or meth.to_s.include? '='
237
+ @attrib[meth.to_s.gsub('=','').intern] = value
238
+ end
145
239
  end
146
240
 
241
+ # Set enclosed content of this element.
242
+ # Will be automatically wrapped in an array.
147
243
  def content=(obj)
148
244
  @content = [ obj ]
149
245
  end
@@ -156,13 +252,29 @@ module GUI
156
252
 
157
253
  # Do not redirect random access operators.
158
254
  def [](index)
159
- return @content[index]
160
- # raise ::Exception.new('Undefined method [] for ' << self.class.to_s)
255
+ return @content[index] if (index.is_a? Numeric)
256
+ return find_by_dom_id(index)
161
257
  end
258
+
259
+ # Retreive an element from object tree by
260
+ # its dom_id
261
+ def find_by_dom_id(dom_id)
262
+ dom_id = dom_id.to_sym
263
+ @content.each { |c|
264
+ if c.is_a? Element then
265
+ return c if (c.dom_id == dom_id)
266
+ sub = c.find_by_dom_id(dom_id)
267
+ return sub if sub
268
+ end
269
+ }
270
+ return nil
271
+ end
272
+
162
273
  # Do not redirect random access operators.
163
274
  def []=(index,element)
164
- @content[index] = element
165
- # raise ::Exception.new('Undefined method []= for ' << self.class.to_s)
275
+ @content[index] = element if (index.is_a? Numeric)
276
+ e = find_by_dom_id(index)
277
+ e.swap(element)
166
278
  end
167
279
  def length
168
280
  @content.length
@@ -171,6 +283,17 @@ module GUI
171
283
  @content.length == 0
172
284
  end
173
285
 
286
+ # Copy constructor. Replace self with
287
+ # other element.
288
+ def swap(other)
289
+ save_own_id = dom_id()
290
+ @tag = other.tag
291
+ @attrib = other.attrib
292
+ @attrib[:id] = save_own_id
293
+ @content = other.content
294
+ end
295
+ alias copy swap
296
+
174
297
  # Static helper definition for clearing
175
298
  # CSS floats.
176
299
  def clear_floating
@@ -179,6 +302,8 @@ module GUI
179
302
 
180
303
  # Render this element to a string.
181
304
  def string
305
+ return content.to_s if @tag == :pseudo
306
+
182
307
  attrib_string = ''
183
308
  @attrib.each_pair { |name,value|
184
309
  if value.instance_of?(Array) then
@@ -226,6 +351,7 @@ module GUI
226
351
  css_classes.collect { |c| c.to_sym if c }
227
352
  return css_classes
228
353
  end
354
+ alias css_class css_classes
229
355
 
230
356
  def add_class(css_class_name)
231
357
  @attrib[:class] = (css_classes << css_class_name.to_sym)
@@ -14,7 +14,55 @@ module GUI
14
14
  # is used to support design of OO-accessible GUI
15
15
  # elements.
16
16
  #
17
- # Other than other builder implementations (there
17
+ # Aurita::GUI is not intended to be used just for
18
+ # HTML rendering, that is: It is not just a builder.
19
+ # But it includes a builder syntax, extremely
20
+ # similar to Markaby, but also including Javascript:
21
+ #
22
+ # x = HTML.build {
23
+ # div.main(:onclick => js.funcall({ :foo => 23, :bar => 'batz' })) {
24
+ # h2.title 'This is what you already now from markaby'
25
+ # } +
26
+ # div.footer('Footer content' :id => :footer)
27
+ # }
28
+ # -->
29
+ # <div class="main" onclick="funcall({ foo: 23, bar: 'batz' }); ">
30
+ # <h2 class="title">
31
+ # This is what you already now from markaby
32
+ # </h2>
33
+ # <div class="footer" id="footer">Footer content</div>
34
+ # </div>
35
+ #
36
+ # But HTML.build is not just rendering text. It
37
+ # maintains an object tree. After rendering, you can
38
+ # access elements by their index, like in an multi-
39
+ # dimensional array:
40
+ #
41
+ # x[0][0].content = "But you didn't know this"
42
+ # x[0][0].onmouseover = Javascript.alert("or that")
43
+ #
44
+ # Or, more convenient, by their DOM id:
45
+ #
46
+ # x[:footer].content = 'Or finding an element by DOM id'
47
+ #
48
+ # You can even completely replace an element after
49
+ # rendering, but you won't be able to change it's DOM id
50
+ # (for obvious reasons):
51
+ #
52
+ # x[:footer] = HTML.span { 'i changed my mind' }
53
+ #
54
+ # -->
55
+ # <div class="main" onclick="funcall({ foo: 23, bar: 'batz' }); ">
56
+ # <h2 class="title" onmouseover="alert('or that'); ">
57
+ # But you didn&apos;t know this
58
+ # </h2>
59
+ # <span id="footer">I changed my mind</span>
60
+ # </div>
61
+ #
62
+ # And *this* is the reason there is an own builder
63
+ # implementation for Aurita::GUI in the first place.
64
+ #
65
+ # So, other than other builder implementations (there
18
66
  # are many), Aurita::GUI is not just a collection
19
67
  # of template helpers. It is data persistent and
20
68
  # maintains object hierarchies.
@@ -22,6 +70,10 @@ module GUI
22
70
  # rendered to a string - you can access and modify a
23
71
  # generated HTML hierarchy before rendering it.
24
72
  #
73
+ # Altogether, Aurita::GUI implements a flexible and
74
+ # convenient toolkit for GUI element (e.g. Widgets)
75
+ # libraries.
76
+ #
25
77
  # This is especially useful (and necessary) when
26
78
  # generating forms.
27
79
  #
@@ -53,6 +105,10 @@ module GUI
53
105
  # HTML.hr(:class => 'divide')
54
106
  # # --> '<hr class="divide" />'
55
107
  #
108
+ # Same as
109
+ #
110
+ # HTML.hr.divide
111
+ #
56
112
  # This is effectively a wrapper for
57
113
  #
58
114
  # Element.new(:tag => :hr, :class => 'divide')
@@ -72,11 +128,16 @@ module GUI
72
128
  #
73
129
  # So the following statements are equivalent:
74
130
  #
131
+ # e = HTML.div.highlight 'hello'
75
132
  # e = HTML.div 'hello', :class => 'highlight'
76
133
  # e = HTML.div(:class => :highlight) { 'hello' }
77
134
  # e = HTML.div(:class => :highlight, :content => 'hello')
78
135
  #
79
- # In all cases, e.to_s renders:
136
+ # In all cases, e is an Element instance:
137
+ #
138
+ # e = Element.new(:tag => :div, :class => :highlight, :content => 'hello')
139
+ #
140
+ # That again renders to a String:
80
141
  #
81
142
  # <div class="highlight">hello</div>
82
143
  #
@@ -103,9 +164,9 @@ module GUI
103
164
  # div(:class => :css_class,
104
165
  # :onmouseover => "do_something_with(this);") {
105
166
  # ul(:id => :the_list) {
106
- # li(:class => :first) { 'foo' } +
107
- # li(:class => :second) { 'bar' } +
108
- # li(:class => :third) { 'batz' }
167
+ # li.first { 'foo' } +
168
+ # li.second { 'bar' } +
169
+ # li.third { 'batz' }
109
170
  # }
110
171
  # }
111
172
  # }
@@ -124,14 +185,17 @@ module GUI
124
185
  #
125
186
  # This is due to a class_eval restriction every
126
187
  # builder struggles with at the moment.
127
- # (There is mixico, but it is not portable).
188
+ # (There is mixico, but it is not portable), as
189
+ # a price you pay for using class_eval on the
190
+ # build block, which enables the concise syntax.
128
191
  #
129
- # To come by this inconvenience, use, for
130
- # example:
192
+ # To come by this inconvenience, use puts, as you'd
193
+ # do in regular ruby code.
194
+ # In the example:
131
195
  #
132
196
  # HTML.build {
133
197
  # div {
134
- # HTML.h2 { compute_string() }
198
+ # h2 { puts compute_string() }
135
199
  # }
136
200
  # }.to_s
137
201
  #
@@ -140,18 +204,6 @@ module GUI
140
204
  # This works, as explicit calls to class methods
141
205
  # of HTML are not rendered using class_eval.
142
206
  #
143
- # The previous example effectively does the
144
- # following:
145
- #
146
- # t2 = HTML.div(:class => :css_class,
147
- # :onmouseover => "do_something_with(this);") {
148
- # HTML.ul(:id => :the_list) {
149
- # HTML.li(:class => :first) { 'foo' } +
150
- # HTML.li(:class => :second) { 'bar' } +
151
- # HTML.li(:class => :third) { 'batz' }
152
- # }
153
- # }
154
- # assert_equal(t1.to_s, t2.to_s)
155
207
  #
156
208
  # Element is not a full Enumerable implementation (yet),
157
209
  # but it offers random access operators ...
@@ -233,7 +285,6 @@ module GUI
233
285
 
234
286
  def self.render(meth_name, *args, &block)
235
287
  raise ::Exception.new('Missing attributes for HTML.' << meth_name.inspect) unless args
236
-
237
288
  e = Element.new(*args, &block)
238
289
  e.tag = meth_name
239
290
  e
@@ -241,7 +292,7 @@ module GUI
241
292
 
242
293
  def self.build(&block)
243
294
  raise ::Exception.new('Missing block for HTML.render') unless block_given?
244
- self.class_eval(&block)
295
+ Element.new(:tag => :pseudo) { self.class_eval(&block) }
245
296
  end
246
297
 
247
298
 
@@ -260,6 +311,10 @@ module GUI
260
311
  render(meth_name, *attribs, &block)
261
312
  end
262
313
 
314
+ def self.puts(arg)
315
+ arg
316
+ end
317
+
263
318
  =begin
264
319
  XHTML_TAGS = [ :html, :div, :p, :input, :select, :option, :ul, :ol, :li ]
265
320
  for t in XHTML_TAGS do
@@ -153,6 +153,18 @@ module GUI
153
153
 
154
154
  end # class
155
155
 
156
+ class HTML
157
+
158
+ def self.js(&block)
159
+ if block_given? then
160
+ Javascript.build(&block)
161
+ else
162
+ Javascript
163
+ end
164
+ end
165
+
166
+ end
167
+
156
168
  end
157
169
  end
158
170
 
@@ -0,0 +1,25 @@
1
+
2
+ module Aurita
3
+ module GUI
4
+
5
+ module Marshal_Helper
6
+ def marshal_dump
7
+ c = { :tag => @tag, :content => @content }
8
+ @attrib.update(c)
9
+ end
10
+ end
11
+
12
+ module Marshal_Helper_Class_Methods
13
+ def marshal_load(dump)
14
+ self.new(dump)
15
+ end
16
+ end
17
+
18
+ class Element
19
+ include Marshal_Helper
20
+ extend Marshal_Helper_Class_Methods
21
+ end
22
+
23
+ end
24
+ end
25
+
data/spec/html.rb CHANGED
@@ -35,18 +35,37 @@ describe Aurita::GUI::HTML, "basic rendering" do
35
35
  end
36
36
 
37
37
  it "should still provide an object tree" do
38
- @e[0].id.should == :header
39
- @e[1].id.should == :content
40
- @e[1].content.should == [ 'Content' ]
41
- @e[1].content = 'Altered'
42
- @e[1].content.should == [ 'Altered' ]
38
+ @e[0].css_class.first.should == :outer
39
+ @e[0][0].id.should == :header
40
+ @e[0][1].id.should == :content
41
+ @e[0][1].content.should == [ 'Content' ]
42
+ @e[0][1].content = 'Altered'
43
+ @e[0][1].content.should == [ 'Altered' ]
43
44
  @e.to_s.should == '<div class="outer"><h2 id="header">Header</h2><p id="content">Altered</p></div>'
44
45
  end
45
46
 
47
+ it "should be possible to retreive elements just by DOM id" do
48
+ e = @e[:header]
49
+ e.content.first.should == 'Header'
50
+ end
51
+
46
52
  it "should escape double-quotes in tag parameters" do
47
53
  e = Element.new(:onclick => 'alert("message");')
48
54
  e.onclick.should == 'alert("message");'
49
55
  e.to_s.should == '<div onclick="alert(\"message\");"></div>'
50
56
  end
51
57
 
58
+ it "should provide a shortcut for setting css classes" do
59
+
60
+ e = HTML.build {
61
+ div.wrapper {
62
+ h2.highlight {
63
+ 'content here'
64
+ } +
65
+ p.footer('footer content')
66
+ }
67
+ }
68
+ e.to_s.should == '<div class="wrapper"><h2 class="highlight">content here</h2><p class="footer">footer content</p></div>'
69
+ end
70
+
52
71
  end
data/spec/javascript.rb CHANGED
@@ -1,6 +1,7 @@
1
1
 
2
2
  require('rubygems')
3
3
  require('aurita-gui/javascript')
4
+ require('aurita-gui/html')
4
5
 
5
6
  include Aurita::GUI
6
7
 
@@ -21,5 +22,14 @@ describe Aurita::GUI::Javascript, "basic rendering" do
21
22
  Javascript.Some.Namespace.do_stuff(23, 'wombat').to_s.should == "Some.Namespace.do_stuff(23,'wombat'); "
22
23
  end
23
24
 
25
+ it "should extend class HTML by a helper method '.js'" do
26
+ e = HTML.build {
27
+ div.outer(:onclick => js.Wombat.alert('message')) {
28
+ p.inner 'click me'
29
+ }
30
+ }
31
+ e.to_s.should == '<div class="outer" onclick="Wombat.alert(\'message\'); "><p class="inner">click me</p></div>'
32
+ end
33
+
24
34
  end
25
35
 
data/spec/marshal.rb ADDED
@@ -0,0 +1,33 @@
1
+
2
+ require('rubygems')
3
+ require('aurita-gui/javascript')
4
+ require('aurita-gui/html')
5
+ require('aurita-gui/marshal')
6
+
7
+ include Aurita::GUI
8
+
9
+ describe Aurita::GUI::Marshal_Helper, "dump and load" do
10
+ before do
11
+ end
12
+
13
+ it "should provide marshal dumping of Element instances" do
14
+ e = HTML.div 'content here', :id => 'dump_test'
15
+ l = Element.marshal_load(e.marshal_dump)
16
+ e.to_s.should == l.to_s
17
+ end
18
+
19
+ it "should also work for object hierarchies" do
20
+ e = HTML.build {
21
+ div.outer(:id => :outer_div) {
22
+ p.inner(:id => :inner_p) {
23
+ 'nested content'
24
+ } +
25
+ button.confirm(:onclick => js.alert('clicked')) { 'click me' }
26
+ }
27
+ }
28
+ e.to_s.should == Element.marshal_load(e.marshal_dump).to_s
29
+ e[0][1].onclick.to_s.should == "alert('clicked'); "
30
+ end
31
+
32
+ end
33
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aurita-gui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Fuchs
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-25 00:00:00 +01:00
12
+ date: 2009-01-26 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -22,7 +22,6 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
 
24
24
  files:
25
- - fieldtest.rb
26
25
  - cheatsheet.rb
27
26
  - spec
28
27
  - History.txt
@@ -35,6 +34,7 @@ files:
35
34
  - LICENSE
36
35
  - lib/aurita-gui
37
36
  - lib/aurita-gui.rb
37
+ - lib/aurita-gui/marshal.rb
38
38
  - lib/aurita-gui/javascript.rb
39
39
  - lib/aurita-gui/button.rb
40
40
  - lib/aurita-gui/form.rb
@@ -59,6 +59,7 @@ files:
59
59
  - lib/aurita-gui/form/template_helper.rb
60
60
  - lib/aurita-gui/form/input_field.rb
61
61
  - lib/aurita-gui/form/date_field.rb
62
+ - spec/marshal.rb
62
63
  - spec/javascript.rb
63
64
  - spec/form.rb
64
65
  - spec/html.rb
data/fieldtest.rb DELETED
@@ -1,64 +0,0 @@
1
-
2
- require('rubygems')
3
- require('aurita-gui')
4
- require('test/unit/assertions')
5
-
6
- include Test::Unit::Assertions
7
-
8
- include Aurita::GUI
9
-
10
- form = Form.new(:name => :the_form,
11
- :id => :the_form_id,
12
- :action => :where_to_send)
13
- # You can either set all attributes in the
14
- # constructor call ...
15
- text = Input_Field.new(:name => :description,
16
- :class => :the_css_class,
17
- :label => 'Enter description',
18
- :onfocus => "alert('input focussed');",
19
- :value => 'some text')
20
- # Or set them afterwards:
21
- text.onblur = "alert('i lost focus :(');"
22
-
23
- puts "\nDefault ---------------------------------\n"
24
- puts text.to_s
25
- puts "\nDisabled --------------------------------\n"
26
- text.disable!
27
- puts text.to_s
28
- puts "\nEnabled ---------------------------------\n"
29
- text.enable!
30
- puts text.to_s
31
- puts "\nReadonly --------------------------------\n"
32
- text.readonly!
33
- puts text.to_s
34
- puts "\nEditable --------------------------------\n"
35
- text.editable!
36
- puts text.to_s
37
-
38
- puts "\nTo hidden -------------------------------\n"
39
- puts text.to_hidden_field.to_s
40
-
41
- # Add it to the form:
42
- form.add(text)
43
- puts "\nThe Form --------------------------------\n"
44
-
45
- # Access it again, via name:
46
- assert_equal(form[:description], text)
47
- # Or by using its index:
48
- assert_equal(form[0], text)
49
-
50
- # This is useful!
51
- form[:description].value = 'change value'
52
-
53
- checkbox = Checkbox_Field.new(:name => :enable_me,
54
- :value => :foo,
55
- :label => 'Check me',
56
- :options => [ :foo, :bar ] )
57
- form.add(checkbox)
58
- form[:description].required = true
59
- checkbox.required = true
60
- form.fields = [ :description, :enable_me ]
61
- form.delete_field(:enable_me)
62
- puts form.to_s
63
-
64
-