cheri 0.0.5 → 0.0.6

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.
@@ -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