RUIC 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,7 +4,8 @@ module UIC; end
4
4
 
5
5
  require 'nokogiri'
6
6
  require_relative 'ruic/version'
7
- require_relative 'ruic/asset_classes'
7
+ require_relative 'ruic/attributes'
8
+ require_relative 'ruic/assets'
8
9
  require_relative 'ruic/interfaces'
9
10
  require_relative 'ruic/application'
10
11
  require_relative 'ruic/behaviors'
@@ -13,27 +14,57 @@ require_relative 'ruic/presentation'
13
14
 
14
15
  class RUIC
15
16
  DEFAULTMETADATA = 'C:/Program Files (x86)/NVIDIA Corporation/UI Composer 8.0/res/DataModelMetadata/en-us/MetaData.xml'
16
- def self.run(ruic_path)
17
- script = File.read(ruic_path,encoding:'utf-8')
18
- Dir.chdir(File.dirname(ruic_path)) do
19
- self.new.instance_eval(script,ruic_path)
17
+ def self.run(opts={})
18
+ ruic = nil
19
+ if opts[:script]
20
+ script = File.read(opts[:script],encoding:'utf-8')
21
+ Dir.chdir(File.dirname(opts[:script])) do
22
+ ruic = self.new
23
+ ruic.uia(opts[:uia]) if opts[:uia]
24
+ ruic.instance_eval(script,opts[:script])
25
+ end
26
+ end
27
+
28
+ if opts[:repl]
29
+ ruic ||= self.new.tap{ |r| r.uia(opts[:uia]) if opts[:uia] }
30
+
31
+ require 'ripl'
32
+ require 'ripl/multi_line'
33
+ require 'ripl/multi_line/live_error.rb'
34
+ Ripl::MultiLine.engine = Ripl::MultiLine::LiveError
35
+ Ripl::Shell.include Ripl::MultiLine.engine
36
+ Ripl.config.merge! prompt:' ', result_prompt:'#=> ', multi_line_prompt:' '
37
+ ARGV.clear # So that RIPL doesn't try to interpret the options
38
+ puts "(starting interactive RUIC session; 'quit' or ctrl-d to end)"
39
+ Ripl.start binding:ruic.instance_eval{ binding }
20
40
  end
21
41
  end
42
+
22
43
  def initialize( metadata=DEFAULTMETADATA )
23
44
  @metadata = metadata
24
45
  @apps = {}
25
46
  end
47
+
26
48
  def metadata(path)
27
49
  @metadata = path
28
50
  end
51
+
29
52
  def uia(path)
30
53
  meta = UIC.Meta @metadata
31
54
  name = @apps.empty? ? :app : :"app#{@apps.length+1}"
32
55
  @apps[name] = UIC.App(meta,path)
33
56
  end
57
+
58
+ module SelfInspecting
59
+ def inspect
60
+ to_s
61
+ end
62
+ end
63
+
34
64
  def method_missing(name,*a)
35
- @apps[name] || super
65
+ @apps[name] || (name=~/^app\d*/ ? "(no #{name} loaded)".extend(SelfInspecting) : super)
36
66
  end
67
+
37
68
  def assert(condition=:CONDITIONNOTSUPPLIED,msg=nil,&block)
38
69
  if block && condition==:CONDITIONNOTSUPPLIED || condition.is_a?(String)
39
70
  msg = condition.is_a?(String) ? condition : yield
@@ -45,15 +76,20 @@ class RUIC
45
76
  exit 1
46
77
  end
47
78
  end
79
+
48
80
  def show(*a); puts *a.map(&:to_s); end
81
+
82
+ def inspect
83
+ "<RUIC #{@apps.empty? ? "(no app loaded)" : Hash[ @apps.map{ |id,app| [id,File.basename(app.file)] } ]}>"
84
+ end
49
85
  end
50
86
 
51
- def RUIC(file_path=nil,&block)
87
+ def RUIC(opts={},&block)
52
88
  if block
53
89
  Dir.chdir(File.dirname($0)) do
54
- RUIC.new.instance_eval(&block)
90
+ RUIC.new.tap{ |r| r.uia(opts[:uia]) if opts[:uia] }.instance_eval(&block)
55
91
  end
56
92
  else
57
- RUIC.run(file_path)
93
+ RUIC.run(opts)
58
94
  end
59
95
  end
@@ -1,6 +1,10 @@
1
1
  class UIC::Application
2
2
  include UIC::FileBacked
3
3
 
4
+ def inspect
5
+ "<UIC::Application '#{File.basename(file)}'>"
6
+ end
7
+
4
8
  attr_reader :metadata
5
9
  def initialize(metadata,uia_path)
6
10
  @metadata = metadata
@@ -1,448 +1,282 @@
1
- #encoding: utf-8
2
- require 'set'
3
- $depth = 0
4
- class UIC::Asset
5
- class Root
6
- @properties = {}
7
- class << self
8
- attr_reader :name
9
- def properties
10
- (ancestors[1].respond_to?(:properties) ? ancestors[1].properties : {}).merge(@properties)
11
- end
12
-
13
- def each
14
- (@by_name.values - [self]).each{ |klass| yield klass }
15
- end
16
- include Enumerable
17
-
18
- def inspect
19
- "<#{@name}>"
20
- end
21
- end
22
-
23
- def properties
24
- self.class.properties
25
- end
26
-
27
- def at(sub_path)
28
- presentation.at(sub_path,@el)
29
- end
30
- alias_method :/, :at
31
-
32
- attr_accessor :presentation, :el
33
- def initialize( presentation, element )
34
- @presentation = presentation
35
- @el = element
36
- end
37
-
38
- def type
39
- self.class.name
40
- # self.class.name.split('::').last
41
- end
42
-
43
- def parent
44
- presentation.parent_asset(@el)
45
- end
46
-
47
- def children
48
- presentation.child_assets(@el)
49
- end
50
-
51
- def find(criteria={},&block)
52
- criteria[:under] ||= self
53
- presentation.find(criteria,&block)
54
- end
55
-
56
- # Find the owning component (even if you are a component)
57
- def component
58
- presentation.owning_component(@el)
59
- end
60
-
61
- def component?
62
- @el.name == 'Component'
63
- end
64
-
65
- def master?
66
- presentation.master?(@el)
67
- end
68
-
69
- def slide?
70
- false
71
- end
72
-
73
- def has_slide?(slide_name_or_index)
74
- presentation.has_slide?(@el,slide_name_or_index)
75
- end
76
-
77
- def slides
78
- presentation.slides_for(@el)
79
- end
80
-
81
- def on_slide(slide_name_or_index)
82
- if has_slide?(slide_name_or_index)
83
- UIC::SlideValues.new( self, slide_name_or_index )
84
- end
85
- end
86
-
87
- def path
88
- @path ||= @presentation.path_to(@el)
89
- end
90
-
91
- def name
92
- properties['name'].get( self, presentation.slide_index(@el) )
93
- end
94
-
95
- def name=( new_name )
96
- properties['name'].set( self, new_name, presentation.slide_index(@el) )
97
- end
98
-
99
- # Get the value(s) of an attribute
100
- def [](attribute_name, slide_name_or_index=nil)
101
- if property = properties[attribute_name]
102
- if slide_name_or_index
103
- property.get( self, slide_name_or_index ) if has_slide?(slide_name_or_index)
104
- else
105
- UIC::ValuesPerSlide.new(@presentation,self,property)
106
- end
107
- end
108
- end
109
-
110
- # Set the value of an attribute, either across all slides, or on a particular slide
111
- # el['foo'] = 42
112
- # el['foo',0] = 42
113
- def []=( attribute_name, slide_name_or_index=nil, new_value )
114
- if property = properties[attribute_name] then
115
- property.set(self,new_value,slide_name_or_index)
116
- end
117
- end
118
-
119
- def to_xml
120
- @el.to_xml
121
- end
122
- def inspect
123
- "<asset #{@el.name}##{@el['id']}>"
124
- end
125
-
126
- def to_s
127
- "<#{type} #{path}>"
128
- end
129
-
130
- def ==(other)
131
- (self.class==other.class) && (el==other.el)
132
- end
133
- alias_method :eql?, :==
134
- end
135
-
136
- attr_reader :by_name
137
-
138
- HIER = {}
139
- %w[Asset Slide Scene].each{ |s| HIER[s] = 'Root' }
140
- %w[Node Behavior Effect Image Layer MaterialBase RenderPlugin].each{ |s| HIER[s]='Asset' }
141
- %w[Camera Component Group Light Model Text].each{ |s| HIER[s]='Node' }
142
- %w[Material ReferencedMaterial].each{ |s| HIER[s]='MaterialBase' }
143
-
144
- def initialize(xml)
145
- @by_name = {'Root'=>Root}
146
-
147
- doc = Nokogiri.XML(xml)
148
- hack_in_slide_names!(doc)
149
-
150
- HIER.each do |class_name,parent_class_name|
151
- parent_class = @by_name[parent_class_name]
152
- el = doc.root.at(class_name)
153
- @by_name[class_name] = create_class(el,parent_class,el.name)
154
- UIC::Asset.const_set( el.name, @by_name[class_name] ) # give the class instance a name by pointing a constant to it :/
155
- end
156
-
157
- # Extend well-known classes with script interfaces after they are created
158
- @by_name['State'] = @by_name['Slide']
159
- @by_name['Slide'].instance_eval do
160
- attr_accessor :index, :name
161
- define_method :inspect do
162
- "<slide ##{index} of #{@el['component'] || @el.parent['component']}>"
163
- end
164
- define_method(:slide?){ true }
165
- end
166
-
167
- refmat = @by_name['ReferencedMaterial']
168
- @by_name['MaterialBase'].instance_eval do
169
- define_method :replace_with_referenced_material do
170
- type=='ReferencedMaterial' ? self : presentation.replace_asset( self, 'ReferencedMaterial', name:name )
171
- end
172
- end
173
- end
174
-
175
- # Creates a class from MetaData.xml with accessors for the <Property> listed.
176
- # Instances of the class are associated with a presentation and know how to
177
- # get/set values in that XML based on value types, slides, defaults.
178
- # Also used to create classes from effects, materials, and behavior preambles.
179
- def create_class(el,parent_class,name='CustomAsset')
180
- Class.new(parent_class) do
181
- @name = name
182
- @properties = Hash[ el.css("Property").map do |e|
183
- type = e['type'] || (e['list'] ? 'String' : 'Float')
184
- type = "Float" if type=="float"
185
- property = UIC::Property.const_get(type).new(e)
186
- [ property.name, UIC::Property.const_get(type).new(e) ]
187
- end ]
188
- end
189
- end
190
-
191
- def new_instance(presentation,el)
192
- @by_name[el.name].new(presentation,el)
193
- end
194
-
195
- def hack_in_slide_names!(doc)
196
- doc.at('Slide') << '<Property name="name" formalName="Name" type="String" default="Slide" hidden="True" />'
197
- end
198
- end
199
-
200
- def UIC.Meta(metadata_path)
201
- UIC::Asset.new(File.read(metadata_path,encoding:'utf-8'))
202
- end
203
-
204
- class UIC::Property
205
- class << self; attr_accessor :default; end
206
- def initialize(el); @el = el; end
207
- def name; @name||=@el['name']; end
208
- def type; @type||=@el['type']; end
209
- def formal; @formal||=@el['formalName'] || @el['name']; end
210
- def min; @el['min']; end
211
- def max; @el['max']; end
212
- def description; @desc||=@el['description']; end
213
- def default; @def ||= (@el['default'] || self.class.default); end
214
- def get(asset,slide)
215
- if asset.slide? || asset.has_slide?(slide)
216
- asset.presentation.get_attribute(asset.el,name,slide) || default
217
- end
218
- end
219
- def set(asset,new_value,slide_name_or_index)
220
- asset.presentation.set_attribute(asset.el,name,slide_name_or_index,new_value)
221
- end
222
- def inspect
223
- "<#{type} '#{name}'>"
224
- end
225
-
226
- class String < self
227
- self.default = ''
228
- end
229
- MultiLineString = String
230
-
231
- class Float < self
232
- self.default = 0.0
233
- def get(asset,slide); super.to_f; end
234
- end
235
- class Long < self
236
- self.default = 0
237
- def get(asset,slide); super.to_i; end
238
- end
239
- class Boolean < self
240
- self.default = false
241
- def get(asset,slide); super=='True'; end
242
- def set(asset,new_value,slide_name_or_index)
243
- super( asset, new_value ? 'True' : 'False', slide_name_or_index )
244
- end
245
- end
246
- class Vector < self
247
- self.default = '0 0 0'
248
- def get(asset,slide)
249
- VectorValue.new(asset,self,slide,super)
250
- end
251
- def set(asset,new_value,slide_name_or_index)
252
- new_value = new_value.join(' ') if new_value.is_a?(Array)
253
- super( asset, new_value, slide_name_or_index )
254
- end
255
- end
256
- Rotation = Vector
257
- Color = Vector
258
- class Image < self
259
- self.default = nil
260
- def get(asset,slide)
261
- if idref = super
262
- result = asset.presentation.asset_by_id( idref[1..-1] )
263
- slide ? result.on_slide( slide ) : result
264
- end
265
- end
266
- def set(asset,new_value,slide)
267
- raise "Setting image attributes not yet supported"
268
- end
269
- end
270
- class Texture < String
271
- def get(asset,slide)
272
- if path=super
273
- path.empty? ? nil : path
274
- end
275
- end
276
- end
277
-
278
- class ObjectRef < self
279
- self.default = nil
280
- def get(asset,slide)
281
- ref = super
282
- type = :absolute
283
- obj = nil
284
- unless ref=='' || ref.nil?
285
- type = ref[0]=='#' ? :absolute : :path
286
- ref = type==:absolute ? asset.presentation.asset_by_id( ref[1..-1] ) : asset.presentation.at( ref, asset.el )
287
- end
288
- ObjectReference.new(asset,self,slide,ref,type)
289
- end
290
- def set(asset,new_object,slide)
291
- get(asset,slide).object = new_object
292
- end
293
- end
294
-
295
- class ObjectReference
296
- attr_reader :object, :type
297
- def initialize(asset,property,slide,object=nil,type=nil)
298
- @asset = asset
299
- @name = property.name
300
- @slide = slide
301
- @object = object
302
- @type = type
303
- end
304
- def object=(new_object)
305
- raise "ObjectRef must be set to an asset (not a #{new_object.class.name})" unless new_object.is_a?(UIC::Asset::Root)
306
- @object = new_object
307
- write_value!
308
- end
309
- def type=(new_type)
310
- raise "ObjectRef types must be either :absolute or :path (not #{new_type.inspect})" unless [:absolute,:path].include?(new_type)
311
- @type = new_type
312
- write_value!
313
- end
314
- private
315
- def write_value!
316
- path = case @object
317
- when NilClass then ""
318
- else case @type
319
- when :absolute then "##{@object.el['id']}"
320
- when :path then @asset.presentation.path_to( @object.el, @asset.el ).sub(/^[^:.]+:/,'')
321
- # when :root then @asset.presentation.path_to( @object.el ).sub(/^[^:.]+:/,'')
322
- end
323
- end
324
- @asset.presentation.set_attribute( @asset.el, @name, @slide, path )
325
- end
326
- end
327
-
328
- Import = String #TODO: a real class
329
- Mesh = String #TODO: a real class
330
- Renderable = String #TODO: a real class
331
- Font = String #TODO: a real class
332
- FontSize = Long
333
-
334
- StringListOrInt = String #TODO: a real class
335
-
336
- class VectorValue
337
- attr_reader :x, :y, :z
338
- def initialize(asset,property,slide,str)
339
- @asset = asset
340
- @property = property
341
- @slide = slide
342
- @x, @y, @z = str.split(/\s+/).map(&:to_f)
343
- end
344
- def setall
345
- @property.set( @asset, to_s, @slide )
346
- end
347
- def x=(n); @x=n; setall end
348
- def y=(n); @y=n; setall end
349
- def z=(n); @z=n; setall end
350
- alias_method :r, :x
351
- alias_method :g, :y
352
- alias_method :b, :z
353
- alias_method :r=, :x=
354
- alias_method :g=, :y=
355
- alias_method :b=, :z=
356
- def inspect
357
- "<#{@asset.path}.#{@property.name}: #{self}>"
358
- end
359
- def to_s
360
- to_a.join(' ')
361
- end
362
- def to_a
363
- [x,y,z]
364
- end
365
- end
366
- end
367
-
368
- class UIC::SlideCollection
369
- include Enumerable
370
- attr_reader :length
371
- def initialize(slides)
372
- @length = slides.length-1
373
- @slides = slides
374
- @lookup = {}
375
- slides.each do |s|
376
- @lookup[s.index] = s
377
- @lookup[s.name] = s
378
- end
379
- end
380
- def each
381
- @slides.each{ |s| yield(s) }
382
- end
383
- def [](index_or_name)
384
- @lookup[ index_or_name ]
385
- end
386
- def inspect
387
- "[ #{@slides.map(&:inspect).join ', '} ]"
388
- end
389
- def to_ary
390
- @slides
391
- end
392
- end
393
-
394
- class UIC::ValuesPerSlide
395
- def initialize(presentation,asset,property)
396
- raise unless presentation.is_a?(UIC::Presentation)
397
- raise unless asset.is_a?(UIC::Asset::Root)
398
- raise unless property.is_a?(UIC::Property)
399
- @preso = presentation
400
- @asset = asset
401
- @el = asset.el
402
- @property = property
403
- end
404
- def value
405
- values.first
406
- end
407
- def [](slide_name_or_index)
408
- @property.get( @asset, slide_name_or_index )
409
- end
410
- def []=(slide_name_or_index,new_value)
411
- @property.set( @asset, new_value, slide_name_or_index )
412
- end
413
- def linked?
414
- @preso.attribute_linked?(@el,@property.name)
415
- end
416
- def unlink
417
- @preso.unlink_attribute( @el, @property.name )
418
- end
419
- def link
420
- @preso.link_attribute( @el, @property.name )
421
- end
422
- def values
423
- @asset.slides.map{ |s| self[s.name] }
424
- end
425
- def inspect
426
- "<Values of '#{@asset.name}.#{@property.name}' across slides>"
427
- end
428
- alias_method :to_s, :inspect
429
- end
430
-
431
- class UIC::SlideValues
432
- def initialize( asset, slide )
433
- @asset = asset
434
- @slide = slide
435
- end
436
- def [](attribute_name)
437
- @asset[attribute_name,@slide]
438
- end
439
- def []=( attribute_name, new_value )
440
- @asset[attribute_name,@slide] = new_value
441
- end
442
- def method_missing( name, *args, &blk )
443
- asset.send(name,*args,&blk)
444
- end
445
- def inspect
446
- "<#{@asset.inspect} on slide #{@slide.inspect}>"
447
- end
1
+ #encoding: utf-8
2
+ class UIC::Asset
3
+ class Root
4
+ @properties = {}
5
+ class << self
6
+ attr_reader :name
7
+ def properties
8
+ (ancestors[1].respond_to?(:properties) ? ancestors[1].properties : {}).merge(@properties)
9
+ end
10
+
11
+ def each
12
+ (@by_name.values - [self]).each{ |klass| yield klass }
13
+ end
14
+ include Enumerable
15
+
16
+ def inspect
17
+ "<#{@name}>"
18
+ end
19
+ end
20
+
21
+ def properties
22
+ self.class.properties
23
+ end
24
+
25
+ def at(sub_path)
26
+ presentation.at(sub_path,@el)
27
+ end
28
+ alias_method :/, :at
29
+
30
+ attr_accessor :presentation, :el
31
+ def initialize( presentation, element )
32
+ @presentation = presentation
33
+ @el = element
34
+ end
35
+
36
+ def type
37
+ self.class.name
38
+ # self.class.name.split('::').last
39
+ end
40
+
41
+ def parent
42
+ presentation.parent_asset(@el)
43
+ end
44
+
45
+ def children
46
+ presentation.child_assets(@el)
47
+ end
48
+
49
+ def find(criteria={},&block)
50
+ criteria[:under] ||= self
51
+ presentation.find(criteria,&block)
52
+ end
53
+
54
+ # Find the owning component (even if you are a component)
55
+ def component
56
+ presentation.owning_component(@el)
57
+ end
58
+
59
+ def component?
60
+ @el.name == 'Component'
61
+ end
62
+
63
+ def master?
64
+ presentation.master?(@el)
65
+ end
66
+
67
+ def slide?
68
+ false
69
+ end
70
+
71
+ def has_slide?(slide_name_or_index)
72
+ presentation.has_slide?(@el,slide_name_or_index)
73
+ end
74
+
75
+ def slides
76
+ presentation.slides_for(@el)
77
+ end
78
+
79
+ def on_slide(slide_name_or_index)
80
+ if has_slide?(slide_name_or_index)
81
+ UIC::SlideValues.new( self, slide_name_or_index )
82
+ end
83
+ end
84
+
85
+ def path
86
+ @path ||= @presentation.path_to(@el)
87
+ end
88
+
89
+ def name
90
+ properties['name'].get( self, presentation.slide_index(@el) )
91
+ end
92
+
93
+ def name=( new_name )
94
+ properties['name'].set( self, new_name, presentation.slide_index(@el) )
95
+ end
96
+
97
+ # Get the value(s) of an attribute
98
+ def [](attribute_name, slide_name_or_index=nil)
99
+ if property = properties[attribute_name]
100
+ if slide_name_or_index
101
+ property.get( self, slide_name_or_index ) if has_slide?(slide_name_or_index)
102
+ else
103
+ UIC::ValuesPerSlide.new(@presentation,self,property)
104
+ end
105
+ end
106
+ end
107
+
108
+ # Set the value of an attribute, either across all slides, or on a particular slide
109
+ # el['foo'] = 42
110
+ # el['foo',0] = 42
111
+ def []=( attribute_name, slide_name_or_index=nil, new_value )
112
+ if property = properties[attribute_name] then
113
+ property.set(self,new_value,slide_name_or_index)
114
+ end
115
+ end
116
+
117
+ def to_xml
118
+ @el.to_xml
119
+ end
120
+ def inspect
121
+ "<asset #{@el.name}##{@el['id']}>"
122
+ end
123
+
124
+ def to_s
125
+ "<#{type} #{path}>"
126
+ end
127
+
128
+ def ==(other)
129
+ (self.class==other.class) && (el==other.el)
130
+ end
131
+ alias_method :eql?, :==
132
+ end
133
+
134
+ attr_reader :by_name
135
+
136
+ HIER = {}
137
+ %w[Asset Slide Scene].each{ |s| HIER[s] = 'Root' }
138
+ %w[Node Behavior Effect Image Layer MaterialBase RenderPlugin].each{ |s| HIER[s]='Asset' }
139
+ %w[Camera Component Group Light Model Text].each{ |s| HIER[s]='Node' }
140
+ %w[Material ReferencedMaterial].each{ |s| HIER[s]='MaterialBase' }
141
+
142
+ def initialize(xml)
143
+ @by_name = {'Root'=>Root}
144
+
145
+ doc = Nokogiri.XML(xml)
146
+ hack_in_slide_names!(doc)
147
+
148
+ HIER.each do |class_name,parent_class_name|
149
+ parent_class = @by_name[parent_class_name]
150
+ el = doc.root.at(class_name)
151
+ @by_name[class_name] = create_class(el,parent_class,el.name)
152
+ UIC::Asset.const_set( el.name, @by_name[class_name] ) # give the class instance a name by pointing a constant to it :/
153
+ end
154
+
155
+ # Extend well-known classes with script interfaces after they are created
156
+ @by_name['State'] = @by_name['Slide']
157
+ @by_name['Slide'].instance_eval do
158
+ attr_accessor :index, :name
159
+ define_method :inspect do
160
+ "<slide ##{index} of #{@el['component'] || @el.parent['component']}>"
161
+ end
162
+ define_method(:slide?){ true }
163
+ end
164
+
165
+ refmat = @by_name['ReferencedMaterial']
166
+ @by_name['MaterialBase'].instance_eval do
167
+ define_method :replace_with_referenced_material do
168
+ type=='ReferencedMaterial' ? self : presentation.replace_asset( self, 'ReferencedMaterial', name:name )
169
+ end
170
+ end
171
+ end
172
+
173
+ # Creates a class from MetaData.xml with accessors for the <Property> listed.
174
+ # Instances of the class are associated with a presentation and know how to
175
+ # get/set values in that XML based on value types, slides, defaults.
176
+ # Also used to create classes from effects, materials, and behavior preambles.
177
+ def create_class(el,parent_class,name='CustomAsset')
178
+ Class.new(parent_class) do
179
+ @name = name
180
+ @properties = Hash[ el.css("Property").map do |e|
181
+ type = e['type'] || (e['list'] ? 'String' : 'Float')
182
+ type = "Float" if type=="float"
183
+ property = UIC::Property.const_get(type).new(e)
184
+ [ property.name, UIC::Property.const_get(type).new(e) ]
185
+ end ]
186
+ end
187
+ end
188
+
189
+ def new_instance(presentation,el)
190
+ @by_name[el.name].new(presentation,el)
191
+ end
192
+
193
+ def hack_in_slide_names!(doc)
194
+ doc.at('Slide') << '<Property name="name" formalName="Name" type="String" default="Slide" hidden="True" />'
195
+ end
196
+ end
197
+
198
+ def UIC.Meta(metadata_path)
199
+ UIC::Asset.new(File.read(metadata_path,encoding:'utf-8'))
200
+ end
201
+
202
+ class UIC::SlideCollection
203
+ include Enumerable
204
+ attr_reader :length
205
+ def initialize(slides)
206
+ @length = slides.length-1
207
+ @slides = slides
208
+ @lookup = {}
209
+ slides.each do |s|
210
+ @lookup[s.index] = s
211
+ @lookup[s.name] = s
212
+ end
213
+ end
214
+ def each
215
+ @slides.each{ |s| yield(s) }
216
+ end
217
+ def [](index_or_name)
218
+ @lookup[ index_or_name ]
219
+ end
220
+ def inspect
221
+ "[ #{@slides.map(&:inspect).join ', '} ]"
222
+ end
223
+ def to_ary
224
+ @slides
225
+ end
226
+ end
227
+
228
+ class UIC::ValuesPerSlide
229
+ def initialize(presentation,asset,property)
230
+ raise unless presentation.is_a?(UIC::Presentation)
231
+ raise unless asset.is_a?(UIC::Asset::Root)
232
+ raise unless property.is_a?(UIC::Property)
233
+ @preso = presentation
234
+ @asset = asset
235
+ @el = asset.el
236
+ @property = property
237
+ end
238
+ def value
239
+ values.first
240
+ end
241
+ def [](slide_name_or_index)
242
+ @property.get( @asset, slide_name_or_index )
243
+ end
244
+ def []=(slide_name_or_index,new_value)
245
+ @property.set( @asset, new_value, slide_name_or_index )
246
+ end
247
+ def linked?
248
+ @preso.attribute_linked?(@el,@property.name)
249
+ end
250
+ def unlink
251
+ @preso.unlink_attribute( @el, @property.name )
252
+ end
253
+ def link
254
+ @preso.link_attribute( @el, @property.name )
255
+ end
256
+ def values
257
+ @asset.slides.map{ |s| self[s.name] }
258
+ end
259
+ def inspect
260
+ "<Values of '#{@asset.name}.#{@property.name}' across slides>"
261
+ end
262
+ alias_method :to_s, :inspect
263
+ end
264
+
265
+ class UIC::SlideValues
266
+ def initialize( asset, slide )
267
+ @asset = asset
268
+ @slide = slide
269
+ end
270
+ def [](attribute_name)
271
+ @asset[attribute_name,@slide]
272
+ end
273
+ def []=( attribute_name, new_value )
274
+ @asset[attribute_name,@slide] = new_value
275
+ end
276
+ def method_missing( name, *args, &blk )
277
+ asset.send(name,*args,&blk)
278
+ end
279
+ def inspect
280
+ "<#{@asset.inspect} on slide #{@slide.inspect}>"
281
+ end
448
282
  end