エラー表示でた!!
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 というかパクった