duby 0.0.2-java → 0.0.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/History.txt +7 -0
  2. data/README.txt +18 -7
  3. data/Rakefile +72 -0
  4. data/examples/ant/example-build.xml +7 -0
  5. data/examples/appengine/Rakefile +8 -67
  6. data/examples/appengine/Readme +4 -3
  7. data/examples/appengine/lib/duby/appengine_tasks.rb +173 -0
  8. data/examples/appengine/lib/duby/plugin/datastore.rb +92 -31
  9. data/examples/appengine/lib/duby_task.rb +61 -0
  10. data/examples/appengine/src/com/ribrdb/DubyApp.duby +32 -6
  11. data/examples/appengine/src/com/ribrdb/list.dhtml +2 -2
  12. data/examples/appengine/{config.ru → src/config.ru} +0 -0
  13. data/examples/bintrees.duby +66 -0
  14. data/examples/dynamic.duby +17 -0
  15. data/examples/fib.duby +3 -11
  16. data/examples/fields.duby +3 -3
  17. data/examples/fractal.duby +1 -3
  18. data/examples/sort_closure.duby +7 -0
  19. data/examples/swing.duby +11 -11
  20. data/javalib/duby-bootstrap.jar +0 -0
  21. data/javalib/dynalang-invoke-0.1.jar +0 -0
  22. data/lib/duby.rb +168 -35
  23. data/lib/duby/ast.rb +224 -27
  24. data/lib/duby/ast/call.rb +85 -25
  25. data/lib/duby/ast/class.rb +112 -28
  26. data/lib/duby/ast/flow.rb +65 -44
  27. data/lib/duby/ast/intrinsics.rb +223 -21
  28. data/lib/duby/ast/literal.rb +67 -16
  29. data/lib/duby/ast/local.rb +36 -40
  30. data/lib/duby/ast/method.rb +83 -67
  31. data/lib/duby/ast/structure.rb +105 -23
  32. data/lib/duby/compiler.rb +83 -28
  33. data/lib/duby/env.rb +33 -0
  34. data/lib/duby/jvm/base.rb +210 -0
  35. data/lib/duby/jvm/compiler.rb +293 -219
  36. data/lib/duby/jvm/method_lookup.rb +77 -67
  37. data/lib/duby/jvm/source_compiler.rb +250 -157
  38. data/lib/duby/jvm/source_generator/builder.rb +53 -49
  39. data/lib/duby/jvm/source_generator/loops.rb +9 -9
  40. data/lib/duby/jvm/source_generator/precompile.rb +35 -25
  41. data/lib/duby/jvm/typer.rb +19 -10
  42. data/lib/duby/jvm/types.rb +127 -68
  43. data/lib/duby/jvm/types/basic_types.rb +26 -13
  44. data/lib/duby/jvm/types/enumerable.rb +6 -4
  45. data/lib/duby/jvm/types/factory.rb +49 -13
  46. data/lib/duby/jvm/types/floats.rb +16 -0
  47. data/lib/duby/jvm/types/integers.rb +63 -2
  48. data/lib/duby/jvm/types/intrinsics.rb +43 -21
  49. data/lib/duby/jvm/types/methods.rb +326 -86
  50. data/lib/duby/jvm/types/number.rb +3 -0
  51. data/lib/duby/nbcompiler.rb +1 -1
  52. data/lib/duby/plugin/edb.rb +1 -1
  53. data/lib/duby/plugin/java.rb +10 -1
  54. data/lib/duby/transform.rb +134 -46
  55. data/lib/duby/typer.rb +75 -50
  56. data/test/test_ast.rb +106 -106
  57. data/test/test_compilation.rb +46 -32
  58. data/test/test_env.rb +42 -0
  59. data/test/test_java_typer.rb +35 -51
  60. data/test/test_javac_compiler.rb +4 -1
  61. data/test/test_jvm_compiler.rb +564 -133
  62. data/test/test_typer.rb +68 -92
  63. metadata +37 -21
  64. data/examples/README +0 -16
  65. data/lib/duby/c/compiler.rb +0 -134
  66. data/lib/duby/old/compiler_old.rb +0 -845
  67. data/lib/duby/old/declaration.rb +0 -72
  68. data/lib/duby/old/mapper.rb +0 -72
  69. data/lib/duby/old/signature.rb +0 -52
  70. data/lib/duby/old/typer_old.rb +0 -163
  71. data/lib/duby/plugin/math.rb +0 -84
  72. data/test/test_math_plugin.rb +0 -87
@@ -1,3 +1,10 @@
1
+ === 0.0.3 / 2010-06-09
2
+
3
+ * Even more language features!
4
+ * invokedynamic support (sort of)
5
+ * Using mirror types to avoid class initialization
6
+ * Other stuff
7
+
1
8
  === 0.0.2 / 2010-02-15
2
9
 
3
10
  * More language features added
data/README.txt CHANGED
@@ -20,20 +20,31 @@ output either JVM bytecode or Java source files.
20
20
  duby <script.duby>
21
21
  duby -e "inline script"
22
22
  dubyc <script.duby>
23
- dubyc -e "inline script" # produces dash_e.class
23
+ dubyc -e "inline script" # produces DashE.class
24
24
  dubyc -java <script.duby>
25
- dubyc -java -e "inline script" # produces dash_e.java
25
+ dubyc -java -e "inline script" # produces DashE.java
26
26
 
27
27
  == REQUIREMENTS:
28
28
 
29
- * JRuby 1.4RC2 or higher.
29
+ * JRuby 1.5.0 or higher.
30
30
  * BiteScript 0.0.5 or higher
31
31
 
32
32
  == INSTALL:
33
33
 
34
+ If your "gem" command is the one from JRuby:
35
+
34
36
  * gem install duby
35
37
 
36
- To build from source you should have a checkout of both jruby and
37
- bitescript in Duby's parent directory. Run "ant jar-complete" in
38
- jruby, then in the duby directory "ant bootstrap" followed by "ant"
39
- should build it.
38
+ Otherwise:
39
+
40
+ * jruby -S gem install duby
41
+
42
+ Only JRuby is supported at this time.
43
+
44
+ == For Java tools:
45
+
46
+ To build the Duby jars from source you should have a checkout of both jruby and
47
+ bitescript in Duby's parent directory. Run "ant jar-complete" in jruby, then in
48
+ the duby directory "../jruby/bin/jruby -S rake jar" to build the Duby jar. Use
49
+ "jar:complete" instead to produce a free-standing jar file with JRuby and the
50
+ JRubyParser libraries included.
data/Rakefile CHANGED
@@ -1,6 +1,10 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'java'
4
+ $: << './lib'
5
+ require 'duby'
6
+ require 'jruby/compiler'
7
+ require 'ant'
4
8
 
5
9
  task :default => :test
6
10
 
@@ -11,3 +15,71 @@ Rake::TestTask.new :test do |t|
11
15
  t.test_files = FileList["test/**/*.rb"]
12
16
  java.lang.System.set_property("jruby.duby.enabled", "true")
13
17
  end
18
+
19
+ task :init do
20
+ mkdir_p 'dist'
21
+ mkdir_p 'build'
22
+ end
23
+
24
+ task :clean do
25
+ ant.delete :quiet => true, :dir => 'build'
26
+ ant.delete :quiet => true, :dir => 'dist'
27
+ end
28
+
29
+ task :compile => :init do
30
+ # build the Ruby sources
31
+ puts "Compiling Ruby sources"
32
+ JRuby::Compiler.compile_argv([
33
+ '-t', 'build',
34
+ '--javac',
35
+ 'src/org/jruby/duby/duby_command.rb'
36
+ ])
37
+
38
+ # build the Duby sources
39
+ puts "Compiling Duby sources"
40
+ Dir.chdir 'src' do
41
+ classpath = Duby::Env.encode_paths([
42
+ 'javalib/jruby-complete.jar',
43
+ 'javalib/JRubyParser.jar',
44
+ 'dist/duby.jar',
45
+ 'build',
46
+ '/usr/share/ant/lib/ant.jar'
47
+ ])
48
+ Duby.compile(
49
+ '-c', classpath,
50
+ '-d', '../build',
51
+ 'org/jruby/duby',
52
+ 'duby/lang')
53
+ end
54
+ end
55
+
56
+ task :jar => :compile do
57
+ ant.jar :jarfile => 'dist/duby.jar' do
58
+ fileset :dir => 'lib'
59
+ fileset :dir => 'build'
60
+ fileset :dir => '.', :includes => 'bin/*'
61
+ fileset :dir => '../bitescript/lib'
62
+ manifest do
63
+ attribute :name => 'Main-Class', :value => 'org.jruby.duby.DubyCommand'
64
+ end
65
+ end
66
+ end
67
+
68
+ namespace :jar do
69
+ task :complete => :jar do
70
+ ant.jar :jarfile => 'dist/duby-complete.jar' do
71
+ zipfileset :src => 'dist/duby.jar'
72
+ zipfileset :src => 'javalib/jruby-complete.jar'
73
+ zipfileset :src => 'javalib/JRubyParser.jar'
74
+ manifest do
75
+ attribute :name => 'Main-Class', :value => 'org.jruby.duby.DubyCommand'
76
+ end
77
+ end
78
+ end
79
+
80
+ task :bootstrap => :compile do
81
+ ant.jar :jarfile => 'javalib/duby-bootstrap.jar' do
82
+ fileset :dir => 'build'
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,7 @@
1
+ <project name="test">
2
+ <taskdef name="dubyc" classname="org.jruby.duby.ant.Compile"/>
3
+
4
+ <target name="blah">
5
+ <dubyc src="examples/fib.duby" target="build"/>
6
+ </target>
7
+ </project>
@@ -1,72 +1,13 @@
1
- require 'appengine-sdk'
2
- require 'appengine-tools/web-xml'
3
- require 'appengine-tools/xml-formatter'
1
+ $: << File.expand_path(File.dirname(__FILE__) + '/lib')
2
+ require 'duby/appengine_tasks'
4
3
 
5
- SRC = File.expand_path(File.dirname(__FILE__) + '/src')
6
- LIB = File.expand_path(File.dirname(__FILE__) + '/lib')
7
- WAR = File.expand_path(File.dirname(__FILE__) + '/war')
8
- CONFIG_RU = File.expand_path(File.dirname(__FILE__) + '/config.ru')
9
- SERVLET = AppEngine::SDK::SDK_ROOT +
10
- '/lib/shared/geronimo-servlet_2.5_spec-1.2.jar'
11
- APIS = AppEngine::SDK::API_JAR
4
+ appengine_app :app
12
5
 
13
- WEB_INF_CLASSES = WAR + '/WEB-INF/classes'
14
- WEB_XML = WAR + '/WEB-INF/web.xml'
15
- AEWEB_XML = WAR + '/WEB-INF/appengine-web.xml'
6
+ DUBY_APP = "#{Duby.dest_path}/com/ribrdb/DubyApp.class"
7
+ MODEL = "#{Duby.dest_path}/com/google/appengine/ext/duby/db/Model.class"
8
+ LIST = "#{Duby.source_path}/com/ribrdb/list.dhtml"
16
9
 
17
- MODEL = 'com/google/appengine/ext/duby/db/Model'
18
- DUBY_APP = 'com/ribrdb/DubyApp'
19
-
20
- MODEL_DUBY = "#{SRC}/#{MODEL}.duby"
21
- MODEL_CLASS = "#{WEB_INF_CLASSES}/#{MODEL}.class"
22
- DUBY_APP_DUBY = "#{SRC}/#{DUBY_APP}.duby"
23
- DUBY_APP_CLASS = "#{WEB_INF_CLASSES}/#{DUBY_APP}.class"
24
- LIST = "#{SRC}/com/ribrdb/list.dhtml"
25
-
26
- def dubyc(*files)
27
- chdir(File.dirname(__FILE__) + '/src') do
28
- files = files.join ' '
29
- dest = '-d ../war/WEB-INF/classes'
30
- dubyc = '../../../bin/dubyc'
31
- options = "-I #{LIB} -p datastore"
32
- orig_classpath = ENV['CLASSPATH']
33
- ENV['CLASSPATH'] ||= ''
34
- ENV['CLASSPATH'] += ":#{SERVLET}:#{APIS}:#{WEB_INF_CLASSES}"
35
- sh %{#{dubyc} #{dest} #{options} #{files}}
36
- ENV['CLASSPATH'] = orig_classpath
37
- end
38
- end
39
-
40
- directory WEB_INF_CLASSES
41
-
42
- file MODEL_CLASS => MODEL_DUBY do
43
- dubyc("#{MODEL}.duby")
44
- end
45
-
46
- file DUBY_APP_CLASS => [DUBY_APP_DUBY, LIST, MODEL_CLASS] do
47
- dubyc("#{DUBY_APP}.duby")
48
- end
49
-
50
- file WEB_XML => [WEB_INF_CLASSES, CONFIG_RU] do
51
- builder = WebXmlBuilder.new do
52
- eval IO.read(CONFIG_RU), nil, 'config.ru', 1
53
- end
54
- open(WEB_XML, 'w') do |webxml|
55
- xml = AppEngine::Rack::XmlFormatter.format(builder.to_xml)
56
- webxml.write(xml)
57
- end
58
- open(AEWEB_XML, 'w') do |aeweb|
59
- xml = AppEngine::Rack::XmlFormatter.format(AppEngine::Rack.app.to_xml)
60
- aeweb.write(xml)
61
- end
62
- end
63
-
64
- task :server => [WEB_XML, DUBY_APP_CLASS] do
65
- sh "dev_appserver.rb #{WAR}"
66
- end
67
-
68
- task :upload => [WEB_XML, DUBY_APP_CLASS] do
69
- sh "appcfg.rb update #{WAR}"
70
- end
10
+ Rake::Task[DUBY_APP].enhance([LIST, MODEL])
71
11
 
12
+ task :app => DUBY_APP
72
13
  task :default => :server
@@ -1,8 +1,9 @@
1
- Run rake to compile the application and start the development server.
1
+ Run rake to compile the application and start the development server:
2
+
3
+ $ jruby -I `pwd`/../../lib -S rake
2
4
 
3
5
  Requirements:
4
- - Install the google-appengine gem (under MRI, not JRUBY)
5
- - Have jruby on your path so dubyc can run.
6
+ - jruby -S gem install google-appengine bitescript
6
7
 
7
8
  Datastore API:
8
9
  Model.get(key)
@@ -0,0 +1,173 @@
1
+ require 'appengine-sdk'
2
+ require 'appengine-tools/appcfg'
3
+ require 'appengine-tools/dev_appserver'
4
+ require 'appengine-tools/web-xml'
5
+ require 'appengine-tools/xml-formatter'
6
+ require 'duby_task'
7
+ require 'java'
8
+ require 'open-uri'
9
+ require 'rake'
10
+ require 'yaml'
11
+
12
+ Duby.compiler_options.concat %w"-p datastore"
13
+ AppEngine::Development::JRubyDevAppserver::ARGV = []
14
+ module AppEngine::Rake
15
+ SERVLET = AppEngine::SDK::SDK_ROOT +
16
+ '/lib/shared/geronimo-servlet_2.5_spec-1.2.jar'
17
+ APIS = AppEngine::SDK::API_JAR
18
+
19
+ $CLASSPATH << SERVLET
20
+ $CLASSPATH << APIS
21
+
22
+ class AppEngineTask < Rake::Task
23
+ def initialize(*args, &block)
24
+ super
25
+ AppEngineTask.tasks << self
26
+ end
27
+
28
+ def init(src, war)
29
+ @src = src
30
+ @war = war
31
+ unless $CLASSPATH.include?(webinf_classes)
32
+ $CLASSPATH << webinf_classes
33
+ end
34
+ Duby.source_path = src
35
+ Duby.dest_path = webinf_classes
36
+ directory(webinf_classes)
37
+ directory(generated)
38
+ file_create dummy_config_ru do |t|
39
+ touch t.name
40
+ end
41
+ file_create gemfile do |t|
42
+ open(t.name, 'w') do |gems|
43
+ gems.puts('bundle_path ".gems/bundler_gems"')
44
+ end
45
+ end
46
+ file web_xml => [webinf_classes, real_config_ru] do
47
+ config_ru = IO.read(real_config_ru)
48
+ builder = WebXmlBuilder.new do
49
+ eval config_ru, nil, 'config.ru', 1
50
+ end
51
+ open(web_xml, 'w') do |webxml|
52
+ xml = AppEngine::Rack::XmlFormatter.format(builder.to_xml)
53
+ webxml.write(xml)
54
+ end
55
+ open(aeweb_xml, 'w') do |aeweb|
56
+ xml = AppEngine::Rack::XmlFormatter.format(AppEngine::Rack.app.to_xml)
57
+ aeweb.write(xml)
58
+ end
59
+ end
60
+ file build_status => [web_xml, dummy_config_ru, aeweb_xml, generated] do
61
+ open(build_status, 'w') do |status_file|
62
+ status = {
63
+ :config_ru => File.stat(dummy_config_ru).mtime,
64
+ :web_xml => File.stat(web_xml).mtime,
65
+ :aeweb_xml => File.stat(aeweb_xml).mtime,
66
+ }
67
+ status_file.write(status.to_yaml)
68
+ end
69
+ end
70
+
71
+ task :server => [name, build_status] do
72
+ # Begin horrible hacks
73
+ if AppEngine::Development::JRubyDevAppserver::ARGV.empty?
74
+ AppEngine::Development::JRubyDevAppserver::ARGV << @war
75
+ end
76
+ class << AppEngine::Development::JRubyDevAppserver
77
+ def exec(*args)
78
+ sh *args
79
+ end
80
+ end
81
+ # End horrible hacks
82
+ check_for_updates
83
+ AppEngine::Development::JRubyDevAppserver.run([@war])
84
+ @done = true
85
+ @update_thread.join
86
+ end
87
+ task :upload => [name, build_status] do
88
+ AppEngine::Admin::JRubyAppCfg.main(['update', @war])
89
+ end
90
+
91
+ enhance([web_xml])
92
+ end
93
+
94
+ def real_prerequisites
95
+ prerequisites.map {|n| application[n, scope]}
96
+ end
97
+
98
+ def check_for_updates
99
+ @update_thread = Thread.new do
100
+ # Give the server time to start
101
+ next_time = Time.now + 5
102
+ until @done
103
+ sleep_time = next_time - Time.now
104
+ sleep(sleep_time) if sleep_time > 0
105
+ next_time = Time.now + 1
106
+ update
107
+ end
108
+ end
109
+ end
110
+
111
+ def update
112
+ updated = false
113
+ real_prerequisites.each do |dep|
114
+ if dep.needed?
115
+ dep.execute
116
+ updated = true
117
+ end
118
+ end
119
+ if updated
120
+ #touch aeweb_xml
121
+ open('http://localhost:8080/_ah/reloadwebapp')
122
+ end
123
+ end
124
+
125
+ def webinf_classes
126
+ @war + '/WEB-INF/classes'
127
+ end
128
+
129
+ def aeweb_xml
130
+ @war + '/WEB-INF/appengine-web.xml'
131
+ end
132
+
133
+ def web_xml
134
+ @war + '/WEB-INF/web.xml'
135
+ end
136
+
137
+ def real_config_ru
138
+ @src + '/config.ru'
139
+ end
140
+
141
+ def dummy_config_ru
142
+ @war + '/config.ru'
143
+ end
144
+
145
+ def generated
146
+ @war + '/WEB-INF/appengine-generated'
147
+ end
148
+
149
+ def build_status
150
+ generated + '/build_status.yaml'
151
+ end
152
+
153
+ def gemfile
154
+ @war + '/Gemfile'
155
+ end
156
+ end
157
+ end
158
+
159
+ def appengine_app(*args, &block)
160
+ deps = []
161
+ if args[-1].kind_of?(Hash)
162
+ hash = args.pop
163
+ arg = hash.keys[0]
164
+ deps = hash[arg]
165
+ args << arg
166
+ end
167
+ name, src, war = args
168
+ task = AppEngine::Rake::AppEngineTask.define_task(name => deps, &block)
169
+ src = File.expand_path(src || 'src')
170
+ war = File.expand_path(war || 'war')
171
+ task.init(src, war)
172
+ task
173
+ end
@@ -1,10 +1,42 @@
1
1
  class DatastorePlugin
2
2
  @models = {}
3
-
3
+
4
+ TypeMap = {
5
+ 'Category' => 'String',
6
+ 'Email' => 'String',
7
+ 'Link' => 'String',
8
+ 'PhoneNumber' => 'String',
9
+ 'PostalAddress' => 'String',
10
+ 'Text' => 'String',
11
+ 'Blob' => 'byte[]',
12
+ 'ShortBlob' => 'byte[]',
13
+ 'Rating' => 'int',
14
+ 'Integer' => 'int',
15
+ 'Double' => 'double',
16
+ }
17
+
18
+ Defaults = {
19
+ 'Rating' => '0',
20
+ 'int' => '0',
21
+ 'double' => '0.0',
22
+ }
23
+
24
+ Conversions = {
25
+ 'Category' => 'getCategory',
26
+ 'Email' => 'getEmail',
27
+ 'Link' => 'getValue',
28
+ 'PhoneNumber' => 'getNumber',
29
+ 'PostalAddress' => 'getAddress',
30
+ 'Text' => 'getValue',
31
+ 'Blob' => 'getBytes',
32
+ 'Integer' => 'intValue',
33
+ 'Double' => 'doubleValue',
34
+ }
35
+
4
36
  class ModelState
5
37
  include Duby::AST
6
38
  attr_reader :kind, :query, :read, :save, :transformer
7
-
39
+
8
40
  def initialize(transformer, klass, parent, position, ast)
9
41
  @transformer = transformer
10
42
  @kind = klass.name.split('.')[-1]
@@ -13,13 +45,13 @@ class DatastorePlugin
13
45
  init_read(parent, position, ast)
14
46
  init_save(parent, position, ast)
15
47
  end
16
-
48
+
17
49
  def init_query(classname, parent, position, ast)
18
50
  name = "#{classname}$Query"
19
51
  @query = ClassDefinition.new(parent, position, name) do |classdef|
20
52
  queryinit = <<-EOF
21
53
  def initialize; end
22
-
54
+
23
55
  def kind
24
56
  "#{kind}"
25
57
  end
@@ -54,29 +86,29 @@ class DatastorePlugin
54
86
  [Duby::AST.type('com.google.appengine.ext.duby.db.DQuery'),
55
87
  eval(classdef, queryinit)]
56
88
  end
57
- ast.children << @query
89
+ ast << @query
58
90
  end
59
-
91
+
60
92
  def init_read(parent, position, ast)
61
93
  @read = eval(parent, <<-EOF)
62
94
  def _read_from(e:Entity)
63
95
  end
64
96
  EOF
65
- @read.body = @read.children[2] = Body.new(@read, position) {[]}
66
- ast.children << @read
97
+ @read.body = Body.new(@read, position) {[]}
98
+ ast << @read
67
99
  end
68
-
100
+
69
101
  def init_save(parent, position, ast)
70
102
  @save = eval(parent, <<-EOF)
71
103
  def _save_to(e:Entity)
72
104
  end
73
105
  EOF
74
- @save.body = @save.children[2] = Body.new(@save, position) {[]}
75
- ast.children << @save
106
+ @save.body = Body.new(@save, position) {[]}
107
+ ast << @save
76
108
  end
77
-
109
+
78
110
  def init_static(parent, ast)
79
- ast.children << eval(parent, <<-EOF)
111
+ ast << eval(parent, <<-EOF)
80
112
  import com.google.appengine.api.datastore.Entity
81
113
  import com.google.appengine.api.datastore.Blob
82
114
  import com.google.appengine.api.datastore.Category
@@ -97,7 +129,7 @@ class DatastorePlugin
97
129
  m._read_from(Model._datastore.get(key))
98
130
  m
99
131
  end
100
-
132
+
101
133
  def self.all
102
134
  #{kind}__Query__.new
103
135
  end
@@ -105,30 +137,48 @@ class DatastorePlugin
105
137
  end
106
138
 
107
139
  def eval(parent, src)
108
- ast = Duby::AST.parse_ruby(src, __FILE__)
109
- transformer.transform(ast.body_node, parent)
140
+ transformer.eval(src, __FILE__, parent)
110
141
  end
111
142
 
112
143
  def extend_query(code)
113
- query.body.children << eval(query.body, code)
144
+ query.body << eval(query.body, code)
114
145
  end
115
146
 
116
147
  def extend_read(code)
117
148
  code = 'e=nil;' + code
118
149
  read.body.children.concat eval(read.body, code).children[1..-1]
119
150
  end
120
-
151
+
121
152
  def extend_save(code)
122
153
  code = 'e=nil;' + code
123
154
  save.body.children.concat eval(save.body, code).children[1..-1]
124
155
  end
125
156
  end
126
-
157
+
127
158
  def self.find_class(node)
128
159
  node = node.parent until Duby::AST::ClassDefinition === node
129
160
  node
130
161
  end
131
-
162
+
163
+ def self.to_datastore(type, value)
164
+ if TypeMap.include?(type)
165
+ "(#{value} ? #{type}.new(#{value}) : nil)"
166
+ else
167
+ value
168
+ end
169
+ end
170
+
171
+ def self.from_datastore(type, value)
172
+ duby_type = TypeMap[type]
173
+ if duby_type
174
+ default = Defaults.fetch(type, "#{duby_type}(nil)")
175
+ conversion = Conversions[type]
176
+ "(#{value} ? #{type}(#{value}).#{conversion} : #{default})"
177
+ else
178
+ "#{type}(#{value})"
179
+ end
180
+ end
181
+
132
182
  Duby::AST.defmacro("property") do |transformer, fcall, parent|
133
183
  result = Duby::AST::Body.new(parent, fcall.position) {[]}
134
184
  klass = find_class(parent)
@@ -141,31 +191,42 @@ class DatastorePlugin
141
191
  name = fcall.args_node.get(0).name
142
192
  type = fcall.args_node.get(1).name
143
193
 
194
+ duby_type = TypeMap.fetch(type, type)
195
+
144
196
  model.extend_query(<<-EOF)
145
- def #{name}(value:#{type})
197
+ def #{name}(value:#{duby_type})
146
198
  returns :void
147
- _query.addFilter("#{name}", _eq_op, value)
199
+ _query.addFilter("#{name}", _eq_op, #{to_datastore(type, 'value')})
148
200
  end
149
201
  EOF
150
-
202
+
203
+ temp = transformer.tmp
204
+
151
205
  model.extend_read(<<-EOF)
152
- @#{name} = #{type}(e.getProperty("#{name}"))
206
+ #{temp} = e.getProperty("#{name}")
207
+ @#{name} = #{from_datastore(type, temp)}
153
208
  EOF
154
-
209
+
155
210
  model.extend_save(<<-EOF)
156
- e.setProperty("#{name}", @#{name})
211
+ e.setProperty("#{name}", #{to_datastore(type, '@' + name)})
157
212
  EOF
158
-
159
- result.children << model.eval(parent, <<-EOF)
213
+
214
+ result << model.eval(parent, <<-EOF)
160
215
  def #{name}
161
216
  @#{name}
162
217
  end
163
-
164
- def #{name}=(value:#{type})
218
+
219
+ def #{name}=(value:#{duby_type})
165
220
  @#{name} = value
166
221
  end
167
222
  EOF
168
223
 
169
224
  result
170
225
  end
171
- end
226
+
227
+ def self.reset
228
+ @models = {}
229
+ end
230
+ end
231
+
232
+ Duby.plugins << DatastorePlugin