kumaryu日記
2009-07-30 できた!
_ [Ruby] IronRubyいじりのつづき
エラー表示でた!!
DLRのプロジェクトページから最新のソースを拾ってきて自前でビルドしたらちゃんとエラー表示されるようになったわー。
IronRubyの最新ソースはビルド方法がよくわからなかったのでパス。
最新じゃないといけないのかは確認してないけど、たぶん、たぶんだけど最新であることよりは自前ビルドが効いたのかもしれない。というか公式ビルドはどんな環境でビルドしてんだよー。
そんなわけでDLRの最新ソースを自分でビルドするのがおすすめです。Silverlight2で動かなくなっちゃったけど。まあ3で動くからいいや。
きっかけはIronRubyのDaily野良ビルド(ただし古い)を使ったらエラー表示が出たこと。あとIronRubyのMLで「なんかエラー出ないんですけど」→「出てるけど何言ってんの?」という感じのやりとりを発見したこと。
そりゃ開発してるんだったら最新を自前ビルドしてますわな。
で、ソース整理もできました。
バインディングをなんとかするのに参考にした*1のはこちら。How to Bind Silverlight DataGrid From IEnumerable of IDictionary by Transforming Each Dictionary Key Into a Property of Anonymous Typed Object
で、できたのがこちら。
include System include System::Collections include System::Reflection include System::Reflection::Emit module DataSource PropertName = /^[A-Za-z]+[A-Za-z0-9_]*$/ @@types = {} class CLRClassBuilder def initialize(name) asm_name = AssemblyName.new("TempAsm#{name}") asm = AppDomain.current_domain.define_dynamic_assembly( asm_name, AssemblyBuilderAccess.Run) mod = asm.define_dynamic_module('TempModule') @builder = mod.define_type( "TempType#{name}", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout, Object.to_clr_type) @builder.define_default_constructor( MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName) define_property(:__source__, Object) end def type @builder.create_type end def define_property(name, type) if type==TrueClass or type==FalseClass then clr_type = System::Boolean.to_clr_type else clr_type = type.to_clr_type end field_builder = @builder.define_field( "_#{name}", clr_type, FieldAttributes.Private) getmethod_builder = @builder.define_method( "get_#{name}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, clr_type, Type.EmptyTypes) il_gen = getmethod_builder.GetILGenerator il_gen.emit(OpCodes.Ldarg_0) il_gen.emit(OpCodes.Ldfld, field_builder) il_gen.emit(OpCodes.Ret) setmethod_builder = @builder.define_method( "set_#{name}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, nil, System::Array[Type].new(1) { clr_type }) il_gen = setmethod_builder.GetILGenerator il_gen.Emit(OpCodes.Ldarg_0) il_gen.Emit(OpCodes.Ldarg_1) il_gen.Emit(OpCodes.Stfld, field_builder) il_gen.Emit(OpCodes.Ret) property_builder = @builder.define_property( name, PropertyAttributes.HasDefault, clr_type, nil) property_builder.set_get_method(getmethod_builder) property_builder.set_set_method(setmethod_builder) end end module_function def to_data_source(list) return [] if list.empty? d = list[0] sig = '' d.each_pair do |key, val| sig << "_#{key}_#{val.class}".gsub(/\-/, '_') end unless @@types.include?(sig) then builder = CLRClassBuilder.new(sig) d.each_pair do |key, value| if key.to_s=~PropertName then builder.define_property(key.to_s, value.class) end end @@types[sig] = builder.type end to_typed_list(@@types[sig], list) end def to_typed_list(type, list) list.collect {|d| row = Activator.create_instance(type) row.__source__ = d d.each_pair do |key, value| row.__send__("#{key}=".to_sym, value) end row } end end class Array def to_data_source DataSource.to_data_source(self) end end
これで
data_grid.items_source = [ { 'name' => 'あああ" }, { 'name' => 'いいい" }, ].to_data_source
のようにするとそれっぽくなります。この例だとnameという文字列型のプロパティを持ったオブジェクトの配列ができます。でもなんかプロパティの書き込みが上手くいってないかも。
配列の中身はHashじゃなくてStructもいけます。生成されたオブジェクトには__source__プロパティがついてて、元のオブジェクトが入ってます。
UIはあとはさくっと書けたので、次はロジック部分。サーバと通信したいんですけど、まずサーバ書かないと。
サーバはめんどいからRack単体で使えばいいかと思ったんですが、Rack単体もそれなりに面倒で、Sinatraが楽そうだったのでSinatraを使うことに。Rack単体で使っても便利なようにすれば結局Sinatraっぽくなっちゃうもんね。
*1 というかパクった