duby 0.0.2-java → 0.0.3-java

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