cheri 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -49,11 +49,13 @@ class Generator
49
49
  end #self
50
50
  end #Template
51
51
 
52
- class TypeBuilder < Cheri::Builder::AbstractBuilder
53
- def initialize(mod,clazz,*r,&k)
52
+ class TypeBuilder
53
+ include Cheri::Builder::Builder
54
+ def initialize(mod,type,*r,&k)
54
55
  super(*r,&k)
55
56
  @mod = mod
56
- @clazz = clazz
57
+ @type = type
58
+ @clazz = type.clazz
57
59
  end
58
60
  # returns the module to which this builder belongs
59
61
  def mod
@@ -64,6 +66,15 @@ class Generator
64
66
  @ctx.call(self,&@blk) if @blk
65
67
  @obj
66
68
  end
69
+ def parent?
70
+ @type.parent?
71
+ end
72
+ def child?
73
+ @type.child?
74
+ end
75
+ def any?
76
+ @type.any?
77
+ end
67
78
  end #TypeBuilder
68
79
 
69
80
  class CherifyBuilder < Cheri::Builder::AbstractBuilder
@@ -90,14 +101,14 @@ class Generator
90
101
 
91
102
  class TypeFactory
92
103
  def initialize(mod,types)
93
- raise Cheri.type_error(types,Hash) unless Hash === types
104
+ raise Cheri.type_error(types,BuildTypes) unless BuildTypes === types
94
105
  @mod = mod
95
106
  @types = types
96
107
  @inv = types.invert
97
108
  end
98
109
  def builder(ctx,sym,*r,&k)
99
- if (clazz = @types[sym])
100
- TypeBuilder.new(@mod,clazz,ctx,sym,*r,&k)
110
+ if (type = @types[sym])
111
+ TypeBuilder.new(@mod,type,ctx,sym,*r,&k)
101
112
  elsif @inv[r.first.class]
102
113
  if sym == :cherify
103
114
  CherifyBuilder.new(@mod,ctx,sym,*r,&k)
@@ -112,16 +123,17 @@ class Generator
112
123
  end
113
124
  end #TypeFactory
114
125
 
126
+ # Factory for self-building types (e.g., Cheri markup types)
115
127
  class AutoFactory
116
128
  def initialize(mod,types)
117
- raise Cheri.type_error(types,Hash) unless Hash === types
129
+ raise Cheri.type_error(types,BuildTypes) unless BuildTypes === types
118
130
  @mod = mod
119
131
  @types = types
120
132
  @inv = types.invert
121
133
  end
122
134
  def builder(ctx,sym,*r,&k)
123
- if (clazz = @types[sym])
124
- bld = clazz.new(ctx,sym,*r,&k)
135
+ if (type = @types[sym])
136
+ bld = type.clazz.new(ctx,sym,*r,&k)
125
137
  elsif @inv[r.first.class]
126
138
  if sym == :cherify
127
139
  CherifyBuilder.new(@mod,ctx,sym,*r,&k)
@@ -152,7 +164,7 @@ class Generator
152
164
  create_mod(eb)
153
165
  else
154
166
  # _really_ simple builder
155
- @types = {}
167
+ @types = BuildTypes.new
156
168
  @ctr = TypeConnecter.new
157
169
  if Hash === @args.last
158
170
  @args.pop.each_pair do |clazz,adder|
@@ -173,7 +185,7 @@ class Generator
173
185
  sym ||= make_sym(clazz)
174
186
  adder = adder.to_sym
175
187
  warn "warning: redefining type #{sym} => #{clazz}" if @types[sym]
176
- @types[sym] = clazz
188
+ @types[sym] = BuildType.new(clazz,sym)
177
189
  @ctr.add_type(clazz).connect(Object,adder)
178
190
  end
179
191
  private :add_type
@@ -214,8 +226,8 @@ class Generator
214
226
  unless unmapped.empty?
215
227
  iv = mapped.invert
216
228
  unmapped.each do |unm|
217
- unless iv[unm]
218
- sym = make_sym(unm)
229
+ unless iv[unm.clazz]
230
+ sym = make_sym(unm.clazz)
219
231
  if (dupl = mapped[sym])
220
232
  warn "warning: derived symbol #{sym} (#{unm}) conflicts with symbol for #{dupl} -- ignoring"
221
233
  else
@@ -228,9 +240,9 @@ class Generator
228
240
  # separate the Java classes (if any)
229
241
  jmapped = nil
230
242
  if !mapped.empty? && defined?Cheri::Java::Builder
231
- jmapped = {}
232
- mapped.each_pair do |sym,clazz|
233
- jmapped[sym] = clazz if clazz.respond_to?(:java_class)
243
+ jmapped = BuildTypes.new
244
+ mapped.each_pair do |sym,type|
245
+ jmapped[sym] = type if type.clazz.respond_to?(:java_class)
234
246
  end
235
247
  if jmapped.empty?
236
248
  jmapped = nil
@@ -245,11 +257,9 @@ class Generator
245
257
  # doesn't (currently) apply to Java classes (and likely never will)
246
258
  amapped = nil
247
259
  unless mapped.empty?
248
- amapped = {}
249
- mapped.each_pair do |sym,clazz|
250
- if clazz < Cheri::Builder::Frame
251
- amapped[sym] = clazz
252
- end
260
+ amapped = BuildTypes.new
261
+ mapped.each_pair do |sym,type|
262
+ amapped[sym] = type if type.clazz < Cheri::Builder::Frame
253
263
  end
254
264
  if amapped.empty?
255
265
  amapped = nil
@@ -332,13 +342,9 @@ class Generator
332
342
  private :create_mod
333
343
 
334
344
  class EvalBuilder
335
- # keep = /^(__|=|<|>|class|to_s|eql\?|equal\?|inspect|require|warn|instance_|respond_to\?)/
336
- # instance_methods.each do |m|
337
- # undef_method m unless m =~ keep
338
- # end
339
345
 
340
346
  def initialize(*r,&k)
341
- @__mapped = {}
347
+ @__mapped = BuildTypes.new
342
348
  @__unmapped = []
343
349
  instance_eval(&k)
344
350
  end
@@ -373,27 +379,27 @@ class Generator
373
379
  @__ext = bld
374
380
  end
375
381
 
376
- def build(type,sym=nil)
382
+ def build(type,sym=nil,&k)
377
383
  raise Cheri.type_error(type,Class) unless Class === type
378
384
  if sym
379
385
  if Symbol === sym
380
- add_mapped(type,sym)
386
+ add_mapped(type,sym,&k)
381
387
  elsif Array === sym
382
388
  sym.each do |s|
383
389
  raise Cheri.type_error(s,Symbol) unless Symbol === s
384
- add_mapped(type,s)
390
+ add_mapped(type,s,&k)
385
391
  end
386
392
  else
387
393
  raise Cheri.type_error(sym,Symbol,Array)
388
394
  end
389
395
  else
390
- @__unmapped << type unless @__unmapped.include?(type)
396
+ @__unmapped << BuildType.new(type,nil,&k) unless @__unmapped.include?(type)
391
397
  end
392
398
  end
393
399
 
394
- def add_mapped(type,sym)
400
+ def add_mapped(type,sym,&k)
395
401
  warn "warning: redefining :#{sym} from #{@__mapped[sym]} to #{type}" if @__mapped[sym]
396
- @__mapped[sym] = type
402
+ @__mapped[sym] = BuildType.new(type,sym,&k)
397
403
  end
398
404
  private :add_mapped
399
405
 
@@ -34,7 +34,7 @@ end
34
34
  # Included by all Cheri::Html builders
35
35
  module HtmlBuilder
36
36
  def esc(inp,out=nil)
37
- if @ctx[:html_esc] && (cs = Charsets.charset(inp))
37
+ if @opt[:esc] && (cs = Charsets.charset(inp))
38
38
  cs.xlat(inp,(out ||= ''))
39
39
  out
40
40
  elsif out
@@ -43,16 +43,32 @@ module HtmlBuilder
43
43
  inp
44
44
  end
45
45
  end
46
- end #XmlBuilder
46
+ end #HtmlBuilder
47
47
 
48
48
  class Elem
49
49
  include Cheri::Builder::MarkupBuilder
50
50
  include HtmlBuilder
51
51
  include HtmlElement
52
52
 
53
+ def initialize(ctx,*r,&k)
54
+ opt = @opt = ctx[:html_opts] || {}
55
+ @fmt = true if opt[:format]
56
+ @amap = opt[:attr] if opt[:attr]
57
+ super
58
+ end
59
+
53
60
  def mod
54
61
  Cheri::Html
55
62
  end
63
+
64
+ def margin
65
+ @mrg || @opt[:margin] || @ctx[:margin] || 0
66
+ end
67
+
68
+ def indent
69
+ @idt || @opt[:indent] || @ctx[:indent] || 0
70
+ end
71
+
56
72
  end
57
73
 
58
74
  class EmptyElem
@@ -60,39 +76,54 @@ class EmptyElem
60
76
  include HtmlBuilder
61
77
  include HtmlElement
62
78
 
79
+ def initialize(ctx,*r,&k)
80
+ opt = @opt = ctx[:html_opts] || {}
81
+ @fmt = true if opt[:format]
82
+ @amap = opt[:attr] if opt[:attr]
83
+ super
84
+ end
85
+
63
86
  def mod
64
87
  Cheri::Html
65
88
  end
66
89
 
67
- def content?(value)
90
+ def add?(value)
68
91
  raise HtmlException,"content not allowed for empty element #{@sym}: #{value}"
69
92
  end
93
+
94
+ def margin
95
+ @mrg || @opt[:margin] || @ctx[:margin] || 0
96
+ end
97
+
98
+ def indent
99
+ @idt || @opt[:indent] || @ctx[:indent] || 0
100
+ end
101
+
70
102
  end
71
103
 
72
104
 
73
105
  class TextElem
74
- include Cheri::Builder::Builder
75
- include Cheri::Builder::Content
76
- include Cheri::Builder::ContentArgs
106
+ include Cheri::Builder::MarkupLikeBuilder
77
107
  include HtmlBuilder
78
108
 
79
- def mod
80
- Cheri::Html
109
+ def initialize(ctx,*r,&k)
110
+ @opt = ctx[:html_opts] || {}
111
+ super
81
112
  end
82
-
83
- def object
84
- self
113
+
114
+ def set?(n,v)
115
+ raise HtmlException,"attribute not allowed for text element #{@sym}: #{n}=#{v}"
85
116
  end
117
+ private :set?
86
118
 
87
- def run
88
- value = @ctx.call(self,&@blk) if @blk
89
- add(value) if value && !(Appendable === value)
90
- self
119
+ def mod
120
+ Cheri::Html
91
121
  end
92
122
 
93
123
  # Appends content to +str+, or to an empty string if +str+ is omitted.
94
124
  # Returns the result.
95
125
  def to_s(str='')
126
+ return to_io(str) unless String === str
96
127
  @cont.each do |c|
97
128
  if String === c
98
129
  esc(c,str)
@@ -132,10 +163,69 @@ class EscElem < TextElem
132
163
 
133
164
  end
134
165
 
166
+ class CommentElem
167
+ include Cheri::Builder::CommentBuilder
168
+ include HtmlBuilder
169
+ include HtmlElement
170
+
171
+ def initialize(ctx,*r,&k)
172
+ opt = @opt = ctx[:html_opts] || {}
173
+ @fmt = true if opt[:format]
174
+ super
175
+ end
176
+
177
+ def set?(n,v)
178
+ raise HtmlException,"attribute not allowed for comment #{@sym}: #{n}=#{v}"
179
+ end
180
+ private :set?
181
+
182
+ def mod
183
+ Cheri::Html
184
+ end
185
+
186
+ def margin
187
+ @mrg || @opt[:margin] || @ctx[:margin] || 0
188
+ end
189
+
190
+ def indent
191
+ @idt || @opt[:indent] || @ctx[:indent] || 0
192
+ end
193
+
194
+ end #CommentElem
195
+
135
196
  # TODO: special handling for these:
136
197
 
137
198
  class HtmlElem < Elem
138
- end
199
+ LOOSE1 = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
200
+ LOOSE2 = " \"http://www.w3.org/TR/html4/loose.dtd\">\n"
201
+ STRICT1 = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n"
202
+ STRICT2 = " \"http://www.w3.org/TR/html4/strict.dtd\">\n"
203
+ Markup = Cheri::Builder::Markup
204
+
205
+ def to_s(str='')
206
+ return to_io(str) unless String === str
207
+ if @opt[:doctype]
208
+ strict = @opt[:strict]
209
+ str << Markup.sp(margin) if @fmt
210
+ str << (strict ? STRICT1 : LOOSE1)
211
+ str << Markup.sp(margin) if @fmt
212
+ str << (strict ? STRICT2 : LOOSE2)
213
+ end
214
+ super(str)
215
+ end
216
+
217
+ def to_io(ios)
218
+ if @opt[:doctype]
219
+ strict = @opt[:strict]
220
+ ios << Markup.sp(margin) if @fmt
221
+ ios << (strict ? STRICT1 : LOOSE1)
222
+ ios << Markup.sp(margin) if @fmt
223
+ ios << (strict ? STRICT2 : LOOSE2)
224
+ end
225
+ super(ios)
226
+ end
227
+
228
+ end #HtmlElem
139
229
 
140
230
  class BodyElem < Elem
141
231
  end
@@ -51,18 +51,31 @@ end #self
51
51
  # html([*args] [, &block]) -> HtmlProxy if no block given, else result of block
52
52
  #
53
53
  def html(*r,&k)
54
- if (ctx = __cheri_ctx)
54
+ if ctx = __cheri_ctx
55
+ unless copts = ctx[:html_opts]
56
+ iopts = ctx.ictx[:html_opts] ||= HtmlOptions.new
57
+ copts = ctx[:html_opts] = HtmlOptions.new(iopts)
58
+ end
55
59
  if k
56
-
57
- # TODO: search stack for Html?
58
- # new HtmlElement if stack empty, maybe other cases?
59
60
  if ctx.tos(HtmlElement)
60
61
  HtmlFrame.new(ctx,*r,&k).run
61
62
  else
62
- ctx.msend(Cheri::Html,:html,*r,&k)
63
+ if r.empty?
64
+ ctx.msend(Cheri::Html,:html,&k)
65
+ else
66
+ copts.ingest_args(*r)
67
+ if args = copts.delete(:args)
68
+ ctx.msend(Cheri::Html,:html,args,&k)
69
+ else
70
+ ctx.msend(Cheri::Html,:html,&k)
71
+ end
72
+ end
63
73
  end
64
-
65
74
  else
75
+ unless r.empty?
76
+ copts.ingest_args(*r)
77
+ copts.delete(:args)
78
+ end
66
79
  ctx[:html_proxy] ||= HtmlProxy.new(ctx,*r)
67
80
  end
68
81
  end
@@ -72,11 +85,91 @@ end #self
72
85
 
73
86
  module HtmlFactory
74
87
  def self.builder(ctx,sym,*r,&k)
88
+ opts = ctx[:html_opts] || {}
89
+ if ((als = opts[:alias]) && (asym = als[sym])) || (asym = Aliases[sym])
90
+ sym = asym
91
+ end
75
92
  clazz = Types[sym]
76
93
  clazz ? clazz.new(ctx,sym,*r,&k) : nil
77
94
  end
78
95
  end #HtmlFactory
79
96
 
97
+ class HtmlOptions < Cheri::Builder::BaseOptions
98
+ def validate(key,value)
99
+ raise Cheri.type_error(key,Symbol) unless Symbol === key
100
+ case key
101
+ when :esc : validate_boolean_nil(value) || nil
102
+ when :doctype : validate_boolean_nil(value) || nil
103
+ when :strict : validate_boolean_nil(value) || nil
104
+ when :validate : validate_boolean_nil(value) || nil
105
+ when :warn : validate_boolean_nil(value) || nil
106
+ when :raise : validate_boolean_nil(value) || nil
107
+ when :format : validate_boolean_nil(value) || nil
108
+ when :indent
109
+ unless Fixnum === value
110
+ if true == value
111
+ value = 2
112
+ elsif false == value || nil == value
113
+ value = nil
114
+ else
115
+ raise Cheri.type_error(value,Fixnum,TrueClass,FalseClass,NilClass)
116
+ end
117
+ end
118
+ store(:format,true) if value
119
+ value
120
+ when :margin
121
+ unless Fixnum === value
122
+ if false == value || nil == value
123
+ value = nil
124
+ else
125
+ raise Cheri.type_error(value,Fixnum,FalseClass,NilClass)
126
+ end
127
+ end
128
+ store(:format,true) if value
129
+ value
130
+ when :alias
131
+ if Array === value
132
+ raise ArgumentError,"odd number of values for :alias" if ((len = value.length) & 1) == 1
133
+ als = self[:alias] || {}
134
+ (len>>1).times do |i|
135
+ als[value[i*2].to_sym] = value[i*2+1].to_sym
136
+ end
137
+ als
138
+ elsif Hash === value
139
+ value.each_pair do |k,v|
140
+ validate_symbol(k)
141
+ validate_symbol(v)
142
+ end
143
+ value
144
+ else
145
+ raise Cheri.type_error(value,Array,Hash)
146
+ end
147
+ when :attr
148
+ if Array === value
149
+ raise ArgumentError,"odd number of values for :attr" if ((len = value.length) & 1) == 1
150
+ als = self[:attr] || {}
151
+ (len>>1).times do |i|
152
+ als[value[i*2].to_sym] = value[i*2+1].to_sym
153
+ end
154
+ als
155
+ elsif Hash === value
156
+ value.each_pair do |k,v|
157
+ validate_symbol(k)
158
+ validate_symbol(v)
159
+ end
160
+ value
161
+ else
162
+ raise Cheri.type_error(value,Array,Hash)
163
+ end
164
+ else
165
+ # anything else gets passed through as args to html
166
+ args = self[:args] ||= {}
167
+ args[key] = value
168
+ nil
169
+ end
170
+ end
171
+ end #HtmlOptions
172
+
80
173
  class HtmlProxy < Cheri::Builder::BaseProxy
81
174
 
82
175
  impl(Types.keys)
@@ -86,17 +179,14 @@ class HtmlProxy < Cheri::Builder::BaseProxy
86
179
  end
87
180
  private :mod
88
181
 
89
- def [](opts)
90
- if Symbol === opts
91
- case opts
92
- when :esc : @ctx[:html_esc] = true
93
- else raise ArgumentError,"invalid simple html option: :#{opts}"
94
- end
95
- elsif Hash === opts
96
- @ctx[:html_esc] = opts[:esc] != false if opts[:esc] != nil
97
- else
98
- raise Cheri.type_error(opts,Hash,Symbol)
99
- end
182
+ def [](*args)
183
+ ictx = (ctx = @ctx).ictx
184
+ iopts = ictx[:html_opts] ||= HtmlOptions.new
185
+ iopts.ingest_args(*args)
186
+ iopts.delete(:args)
187
+ copts = ctx[:html_opts] ||= HtmlOptions.new
188
+ copts.ingest_args(*args)
189
+ copts.delete(:args)
100
190
  nil
101
191
  end
102
192
  end #HtmlProxy
@@ -104,12 +194,28 @@ end #HtmlProxy
104
194
  class HtmlFrame
105
195
  include Cheri::Builder::Frame
106
196
  def initialize(ctx,*r,&k)
107
- super
197
+ super(ctx,&k)
108
198
  @obj = ctx[:html_proxy] ||= HtmlProxy.new(ctx,*r)
109
- end
199
+ @args = r unless r.empty?
200
+ end
110
201
  def mod
111
202
  Cheri::Html
112
203
  end
204
+ def run
205
+ if blk = @blk
206
+ if args = @args
207
+ opts = (ctx = @ctx)[:html_opts]
208
+ temp = HtmlOptions.new(opts)
209
+ temp.ingest_args(*args)
210
+ ctx[:html_opts] = temp
211
+ result = ctx.call(self,&blk)
212
+ ctx[:html_opts] = opts
213
+ result
214
+ else
215
+ @ctx.call(self,&blk)
216
+ end
217
+ end
218
+ end
113
219
  end #HtmlFrame
114
220
 
115
221
  end #Html