dubious 0.0.0-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 (120) hide show
  1. data/LICENSE +202 -0
  2. data/README.rdoc +160 -0
  3. data/ROADMAP.rdoc +28 -0
  4. data/Rakefile +130 -0
  5. data/bin/dubious +4 -0
  6. data/examples/contacts/Rakefile +169 -0
  7. data/examples/contacts/WEB-INF/app.yaml +9 -0
  8. data/examples/contacts/WEB-INF/appengine-web.xml +14 -0
  9. data/examples/contacts/WEB-INF/classes/controllers/ApplicationController.class +0 -0
  10. data/examples/contacts/WEB-INF/classes/controllers/ContactsController.class +0 -0
  11. data/examples/contacts/WEB-INF/classes/controllers/InfoPropertiesController.class +0 -0
  12. data/examples/contacts/WEB-INF/classes/controllers/SourceController.class +0 -0
  13. data/examples/contacts/WEB-INF/classes/models/Contact$Query.class +0 -0
  14. data/examples/contacts/WEB-INF/classes/models/Contact.class +0 -0
  15. data/examples/contacts/WEB-INF/lib/appengine-api.jar +0 -0
  16. data/examples/contacts/WEB-INF/lib/dubious.jar +0 -0
  17. data/examples/contacts/WEB-INF/lib/dubydatastore.jar +0 -0
  18. data/examples/contacts/WEB-INF/web.xml +3 -0
  19. data/examples/contacts/app.yaml +44 -0
  20. data/examples/contacts/app/controllers/application_controller.mirah +5 -0
  21. data/examples/contacts/app/controllers/contacts_controller.mirah +62 -0
  22. data/examples/contacts/app/controllers/info_properties_controller.mirah +55 -0
  23. data/examples/contacts/app/controllers/source_controller.mirah +30 -0
  24. data/examples/contacts/app/models/contact.mirah +28 -0
  25. data/examples/contacts/app/views/contacts/edit.html.erb +50 -0
  26. data/examples/contacts/app/views/contacts/index.html.erb +38 -0
  27. data/examples/contacts/app/views/contacts/new.html.erb +49 -0
  28. data/examples/contacts/app/views/contacts/show.html.erb +47 -0
  29. data/examples/contacts/app/views/layouts/application.html.erb +10 -0
  30. data/examples/contacts/app/views/layouts/contacts.html.erb +20 -0
  31. data/examples/contacts/app/views/source/list.html.erb +27 -0
  32. data/examples/contacts/app/views/source/show.html.erb +15 -0
  33. data/examples/contacts/config/application.properties +4 -0
  34. data/examples/contacts/config/asset.properties +32 -0
  35. data/examples/contacts/config/build.properties +10 -0
  36. data/examples/contacts/config/routes.properties +1 -0
  37. data/examples/contacts/public/404.html +30 -0
  38. data/examples/contacts/public/422.html +30 -0
  39. data/examples/contacts/public/500.html +30 -0
  40. data/examples/contacts/public/favicon.ico +0 -0
  41. data/examples/contacts/public/images/appengine_duby.png +0 -0
  42. data/examples/contacts/public/images/back.gif +0 -0
  43. data/examples/contacts/public/images/dir.gif +0 -0
  44. data/examples/contacts/public/images/dubious.png +0 -0
  45. data/examples/contacts/public/images/file.gif +0 -0
  46. data/examples/contacts/public/images/silver-120x30.gif +0 -0
  47. data/examples/contacts/public/index.html +144 -0
  48. data/examples/contacts/public/javascripts/effects.js +1128 -0
  49. data/examples/contacts/public/javascripts/jquery.rails.min.js +117 -0
  50. data/examples/contacts/public/javascripts/lang/sh_css.min.js +1 -0
  51. data/examples/contacts/public/javascripts/lang/sh_duby.min.js +1 -0
  52. data/examples/contacts/public/javascripts/lang/sh_erb.min.js +1 -0
  53. data/examples/contacts/public/javascripts/lang/sh_html.min.js +1 -0
  54. data/examples/contacts/public/javascripts/lang/sh_java.min.js +1 -0
  55. data/examples/contacts/public/javascripts/lang/sh_js.min.js +1 -0
  56. data/examples/contacts/public/javascripts/lang/sh_mirah.min.js +1 -0
  57. data/examples/contacts/public/javascripts/lang/sh_properties.min.js +1 -0
  58. data/examples/contacts/public/javascripts/lang/sh_rb.min.js +1 -0
  59. data/examples/contacts/public/javascripts/lang/sh_sh.min.js +1 -0
  60. data/examples/contacts/public/javascripts/lang/sh_xml.min.js +1 -0
  61. data/examples/contacts/public/javascripts/lang/sh_yaml.min.js +1 -0
  62. data/examples/contacts/public/javascripts/prototype.js +4320 -0
  63. data/examples/contacts/public/javascripts/sh_main.min.js +4 -0
  64. data/examples/contacts/public/robots.txt +0 -0
  65. data/examples/contacts/public/stylesheets/main.css +188 -0
  66. data/examples/contacts/public/stylesheets/scaffold.css +57 -0
  67. data/examples/contacts/public/stylesheets/sh_style.css +66 -0
  68. data/examples/contacts/public/stylesheets/source.css +27 -0
  69. data/examples/shout/Rakefile +138 -0
  70. data/examples/shout/WEB-INF/appengine-web.xml +14 -0
  71. data/examples/shout/WEB-INF/web.xml +11 -0
  72. data/examples/shout/app.mirah +35 -0
  73. data/examples/shout/app.yaml +11 -0
  74. data/examples/shout/public/404.html +30 -0
  75. data/examples/shout/public/422.html +30 -0
  76. data/examples/shout/public/500.html +30 -0
  77. data/examples/shout/public/favicon.ico +0 -0
  78. data/examples/shout/public/images/appengine_duby.png +0 -0
  79. data/examples/shout/public/images/back.gif +0 -0
  80. data/examples/shout/public/images/dir.gif +0 -0
  81. data/examples/shout/public/images/file.gif +0 -0
  82. data/examples/shout/public/images/silver-120x30.gif +0 -0
  83. data/examples/shout/public/index.html +116 -0
  84. data/examples/shout/public/javascripts/effects.js +1128 -0
  85. data/examples/shout/public/javascripts/jquery.rails.min.js +117 -0
  86. data/examples/shout/public/javascripts/prototype.js +4320 -0
  87. data/examples/shout/public/robots.txt +0 -0
  88. data/examples/shout/public/stylesheets/main.css +185 -0
  89. data/examples/shout/public/stylesheets/scaffold.css +57 -0
  90. data/examples/shout/views/application.html.erb +10 -0
  91. data/examples/shout/views/list.html.erb +14 -0
  92. data/javalib/dubydatastore.jar +0 -0
  93. data/lib/dubious.jar +0 -0
  94. data/lib/dubious.rb +2 -0
  95. data/lib/dubious/cli.rb +65 -0
  96. data/lib/dubious/templates/base/Rakefile +169 -0
  97. data/lib/dubious/templates/base/WEB-INF/app.yaml.tt +9 -0
  98. data/lib/dubious/templates/base/app/controllers/application_controller.mirah +5 -0
  99. data/lib/dubious/templates/base/config/application.properties.tt +6 -0
  100. data/lib/dubious/templates/base/config/asset.properties +0 -0
  101. data/lib/dubious/templates/base/config/build.properties +0 -0
  102. data/lib/dubious/templates/base/config/routes.properties +1 -0
  103. data/lib/dubious/templates/base/public/404.html +30 -0
  104. data/lib/dubious/templates/base/public/422.html +30 -0
  105. data/lib/dubious/templates/base/public/500.html +30 -0
  106. data/lib/dubious/templates/base/public/favicon.ico +0 -0
  107. data/lib/dubious/templates/base/public/images/appengine_duby.png +0 -0
  108. data/lib/dubious/templates/base/public/images/back.gif +0 -0
  109. data/lib/dubious/templates/base/public/images/dir.gif +0 -0
  110. data/lib/dubious/templates/base/public/images/dubious.png +0 -0
  111. data/lib/dubious/templates/base/public/images/file.gif +0 -0
  112. data/lib/dubious/templates/base/public/images/silver-120x30.gif +0 -0
  113. data/lib/dubious/templates/base/public/index.html +144 -0
  114. data/lib/dubious/templates/base/public/javascripts/jquery.rails.min.js +117 -0
  115. data/lib/dubious/templates/base/public/robots.txt +0 -0
  116. data/lib/dubious/templates/base/public/stylesheets/main.css +188 -0
  117. data/lib/dubious/templates/base/public/stylesheets/scaffold.css +57 -0
  118. data/lib/dubious/templates/generator/controller.mirah.tt +5 -0
  119. data/lib/dubious/templates/generator/model.mirah.tt +6 -0
  120. metadata +224 -0
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require File.dirname(__FILE__) + '/../lib/dubious/cli'
4
+ Dubious::CLI.start
@@ -0,0 +1,169 @@
1
+ begin
2
+ require 'ant'
3
+ rescue LoadError
4
+ puts 'This Rakefile requires JRuby. Please use jruby -S rake.'
5
+ exit 1
6
+ end
7
+
8
+ if ENV['MIRAH_HOME'] && File.exist?(ENV['MIRAH_HOME'] +'/lib/mirah.rb')
9
+ $: << File.expand_path(ENV['MIRAH_HOME'] +'/lib')
10
+ end
11
+
12
+ if File.exist?('../bitescript/lib/bitescript.rb')
13
+ $: << File.expand_path('../bitescript/lib/')
14
+ end
15
+
16
+ MIRAH_HOME = ENV['MIRAH_HOME'] ? ENV['MIRAH_HOME'] : Gem.find_files('mirah').first.sub(/lib\/mirah.rb/,'')
17
+
18
+ MODEL_SRC_JAR = File.join(MIRAH_HOME, 'examples', 'appengine', 'war',
19
+ 'WEB-INF', 'lib', 'dubydatastore.jar')
20
+
21
+
22
+ require 'rake/clean'
23
+ require 'mirah/appengine_tasks'
24
+
25
+ def mirahc *files
26
+ if files[-1].kind_of?(Hash)
27
+ options = files.pop
28
+ else
29
+ options = {}
30
+ end
31
+ source_dir = options.fetch(:dir, Duby.source_path)
32
+ dest = File.expand_path(options.fetch(:dest, Duby.dest_path))
33
+ files = files.map {|f| f.sub(/^#{source_dir}\//, '')}
34
+ flags = options.fetch(:options, Duby.compiler_options)
35
+ args = ['-d', dest, *flags] + files
36
+ chdir(source_dir) do
37
+ cmd = "mirahc #{args.join ' '}"
38
+ puts cmd
39
+ if files.any? {|f|f.include? 'controllers'}
40
+ system cmd
41
+ else
42
+ Duby.compile(*args)
43
+ Duby.reset
44
+ end
45
+ end
46
+ end
47
+
48
+
49
+ OUTDIR = 'WEB-INF/classes'
50
+
51
+ def class_files_for files
52
+ files.map do |f|
53
+ explode = f.split('/')[1..-1]
54
+ explode.last.gsub!(/(^[a-z]|_[a-z])/) {|m|m.sub('_','').upcase}
55
+ explode.last.sub! /\.(duby|java|mirah)$/, '.class'
56
+ OUTDIR + '/' + explode.join('/')
57
+ end
58
+ end
59
+
60
+ MODEL_JAR = "WEB-INF/lib/dubydatastore.jar"
61
+
62
+ LIB_MIRAH_SRC = Dir["lib/**/*.duby"]
63
+ LIB_JAVA_SRC = Dir["lib/**/*.java"]
64
+ LIB_SRC = LIB_MIRAH_SRC + LIB_JAVA_SRC
65
+ LIB_CLASSES = class_files_for LIB_SRC
66
+
67
+ STDLIB_CLASSES= LIB_CLASSES.select{|l|l.include? 'stdlib'}
68
+
69
+
70
+ CLASSPATH = [AppEngine::Rake::SERVLET, AppEngine::SDK::API_JAR].join(":")
71
+
72
+
73
+
74
+ Duby.dest_paths << OUTDIR
75
+ Duby.source_paths << 'lib'
76
+ Duby.source_paths << 'app'
77
+ Duby.compiler_options << '--classpath' << [File.expand_path(OUTDIR), *FileList["WEB-INF/lib/*.jar"].map{|f|File.expand_path(f)}].join(':') + ':' + CLASSPATH
78
+
79
+ CLEAN.include(OUTDIR)
80
+ CLOBBER.include("WEB-INF/lib/dubious.jar", 'WEB-INF/appengine-generated')
81
+
82
+ APP_SRC = Dir["app/**/{*.duby,*.mirah}"]
83
+ APP_CLASSES = class_files_for APP_SRC
84
+ APP_MODEL_CLASSES = APP_CLASSES.select {|app| app.include? '/models' }
85
+ APP_CONTROLLER_CLASSES = APP_CLASSES.select {|app| app.include? '/controllers' }
86
+ APP_APPLICATION_CONTROLLER_CLASS = APP_CONTROLLER_CLASSES.find {|controller| controller.include? 'ApplicationController' }
87
+ TEMPLATES = Dir["app/views/**/*.erb"]
88
+
89
+ directory OUTDIR
90
+
91
+ (APP_CLASSES+LIB_CLASSES).zip(APP_SRC+LIB_SRC).each do |klass,src|
92
+ file klass => src
93
+ end
94
+
95
+ APP_CONTROLLER_CLASSES.reject {|k|k == APP_APPLICATION_CONTROLLER_CLASS}.each do |klass|
96
+ file klass => APP_APPLICATION_CONTROLLER_CLASS
97
+ end
98
+
99
+ APP_CONTROLLER_CLASSES.each do |f|
100
+ file f => APP_MODEL_CLASSES + TEMPLATES
101
+ end
102
+
103
+ APP_CLASSES.each do |f|
104
+ file f => LIB_CLASSES
105
+ end
106
+
107
+ file MODEL_JAR => MODEL_SRC_JAR do |t|
108
+ cp MODEL_SRC_JAR, MODEL_JAR
109
+ end
110
+
111
+ appengine_app :app, 'app', '' => APP_CLASSES+LIB_CLASSES
112
+
113
+ #there is an upload task in appengine_tasks, but I couldn't get it to work
114
+ desc "publish to appengine"
115
+ task :publish => 'compile:app' do
116
+ sh "appcfg.sh update ."
117
+ end
118
+
119
+ namespace :compile do
120
+ task :app => APP_CLASSES
121
+
122
+ task :java => OUTDIR do
123
+ ant.javac :srcdir => 'lib', :destdir => OUTDIR, :classpath => CLASSPATH
124
+ end
125
+
126
+ end
127
+
128
+ desc "compile app"
129
+ task :compile => 'compile:app'
130
+
131
+ desc "run development server"
132
+ task :server
133
+
134
+ task :default => :server
135
+
136
+ task :generate_build_properties do
137
+ def git_data(dir, file='')
138
+ returning = nil
139
+ chdir dir do
140
+ # ["commit abc....123", "2010-06-23 12:58:06 -0700"]
141
+ IO.popen("git rev-list --pretty=format:%ci -1 HEAD #{file}") do |f|
142
+ returning = [f.gets.chomp, f.gets.chomp]
143
+ end
144
+ end
145
+ returning
146
+ end
147
+
148
+ dubious_data = git_data(".")
149
+ mirah_data = git_data(MIRAH_HOME)
150
+ bite_data = git_data(MIRAH_HOME + '/../bitescript')
151
+ model_data = git_data(File.dirname(MODEL_SRC_JAR),File.basename(MODEL_SRC_JAR))
152
+
153
+ prop_file = "config/build.properties"
154
+ File.open(prop_file, 'w') do |f|
155
+ f.write <<-EOF
156
+ # the current build environment
157
+ application.build.time=#{Time.now.xmlschema}
158
+ dubious.version.commit=#{dubious_data[0][7..-1]}
159
+ dubious.version.time=#{Time.parse(dubious_data[1]).xmlschema}
160
+ mirah.version.commit=#{mirah_data[0][7..-1]}
161
+ mirah.version.time=#{Time.parse(mirah_data[1]).xmlschema}
162
+ bitescript.version.commit=#{bite_data[0][7..-1]}
163
+ bitescript.version.time=#{Time.parse(bite_data[1]).xmlschema}
164
+ model.version.commit=#{model_data[0][7..-1]}
165
+ model.version.time=#{Time.parse(model_data[1]).xmlschema}
166
+ EOF
167
+ end
168
+
169
+ end
@@ -0,0 +1,9 @@
1
+ application: contacts
2
+ version: duby
3
+ runtime: java
4
+ public_root: /public
5
+
6
+ resource_files:
7
+ - exclude: /build/**
8
+
9
+ handlers:
@@ -0,0 +1,14 @@
1
+ <!-- Generated from app.yaml. Do not edit. -->
2
+ <appengine-web-app xmlns='http://appengine.google.com/ns/1.0'>
3
+ <application>contacts</application>
4
+ <version>duby</version>
5
+ <public-root>/public</public-root>
6
+ <static-files>
7
+ </static-files>
8
+ <resource-files>
9
+ <exclude path='/build/**'/>
10
+ </resource-files>
11
+ <ssl-enabled>true</ssl-enabled>
12
+ <precompilation-enabled>true</precompilation-enabled>
13
+ <sessions-enabled>false</sessions-enabled>
14
+ </appengine-web-app>
@@ -0,0 +1,3 @@
1
+ <!-- Generated from app.yaml. Do not edit. -->
2
+ <web-app version='2.5' xmlns='http://java.sun.com/xml/ns/javaee'>
3
+ </web-app>
@@ -0,0 +1,44 @@
1
+ application: dubious-demo # duby-primer
2
+ version: duby
3
+ runtime: java
4
+ public_root: /public
5
+
6
+ resource_files:
7
+ - exclude: /build/**
8
+
9
+ static_files:
10
+ - include: /**/*.ico
11
+ expiration: 365d
12
+ - include: /**/*.gif
13
+ expiration: 365d
14
+ - include: /**/*.jpeg
15
+ expiration: 365d
16
+ - include: /**/*.jpg
17
+ expiration: 365d
18
+ - include: /**/*.png
19
+ expiration: 365d
20
+ - include: /**/*.js
21
+ expiration: 365d
22
+ - include: /**/*.css
23
+ expiration: 365d
24
+
25
+ handlers:
26
+ - url: /info/properties
27
+ servlet: controllers.InfoPropertiesController
28
+ name: info_properties
29
+
30
+ - url: /contacts/*
31
+ servlet: controllers.ContactsController
32
+ name: contacts
33
+
34
+ - url: /source/*
35
+ servlet: controllers.SourceController
36
+ name: source
37
+
38
+ - url: /simple_duby/*
39
+ servlet: testing.SimpleDuby
40
+ name: dimple_duby
41
+
42
+ - url: /simple_java/*
43
+ servlet: testing.SimpleJava
44
+ name: simple_java
@@ -0,0 +1,5 @@
1
+ import dubious.*
2
+
3
+ class ApplicationController < ActionController
4
+
5
+ end
@@ -0,0 +1,62 @@
1
+ import dubious.*
2
+ import models.*
3
+
4
+ class ContactsController < ApplicationController
5
+
6
+ # GET /contacts
7
+ def index
8
+ @contacts = Contact.all.run
9
+ render index_erb, main_erb
10
+ end
11
+
12
+ # GET /contacts/1
13
+ def show
14
+ @contact = Contact.get(params.id)
15
+ render show_erb, main_erb
16
+ end
17
+
18
+ # GET /contacts/new
19
+ def new
20
+ @contact = Contact.new
21
+ render new_erb, main_erb
22
+ end
23
+
24
+ # GET /contacts/1/edit
25
+ def edit
26
+ @contact = Contact.get(params.id)
27
+ render edit_erb, main_erb
28
+ end
29
+
30
+ # DELETE /contacts/1
31
+ def delete
32
+ Contact.delete(Contact.get(params.id).key)
33
+ redirect_to params.index
34
+ end
35
+
36
+ # POST /contacts
37
+ def create
38
+ Contact.new.update(params.for('contact')).save
39
+ redirect_to params.index
40
+ end
41
+
42
+ # PUT /contacts/1
43
+ def update
44
+ Contact.get(params.id).update(params.for('contact')).save
45
+ redirect_to params.show
46
+ end
47
+
48
+ def doGet(request, response)
49
+ action_response(response, action_request(request, 'get'))
50
+ end
51
+
52
+ def doPost(request, response)
53
+ action_response(response, action_request(request, 'post'))
54
+ end
55
+
56
+ # render templates
57
+ def_edb(index_erb, 'views/contacts/index.html.erb')
58
+ def_edb(show_erb, 'views/contacts/show.html.erb')
59
+ def_edb(new_erb, 'views/contacts/new.html.erb')
60
+ def_edb(edit_erb, 'views/contacts/edit.html.erb')
61
+ def_edb(main_erb, 'views/layouts/contacts.html.erb')
62
+ end
@@ -0,0 +1,55 @@
1
+ import com.google.apphosting.api.ApiProxy;
2
+ import java.io.FileInputStream
3
+ import java.util.Properties
4
+ import java.util.ArrayList
5
+ import java.lang.System
6
+
7
+ class InfoPropertiesController < ApplicationController
8
+
9
+ # GET /info/properties
10
+ def doGet(request, response)
11
+ rows = tr 'Mirah version', build_info('mirah')
12
+ rows += tr 'Bitescript version', build_info('bitescript')
13
+ rows += tr 'Dubious version', build_info('dubious')
14
+ rows += tr 'MirahModel version', build_info('model')
15
+ rows += tr 'Java version', prop('java.specification.version'),
16
+ prop('java.vm.version')
17
+ rows += tr 'Java vm name', prop('java.vm.name')
18
+ rows += tr 'Environment', prop('runtime.environment').toLowerCase
19
+ rows += tr 'Runtime version', prop('runtime.version')
20
+ rows += tr 'Auth domain', ApiProxy.getCurrentEnvironment.getAuthDomain
21
+ rows += tr 'App id', prop('application.id')
22
+ rows += tr 'App version', prop('application.version')
23
+ response.getWriter.write("<table><tbody>#{rows}</tbody></table>")
24
+ end
25
+
26
+ def build_info(tool:String)
27
+ out = String[2]
28
+ props = Properties.new
29
+ begin
30
+ props.load(FileInputStream.new("config/build.properties"))
31
+ out[0] = props.getProperty("#{tool}.version.commit").substring(0,7)
32
+ out[1] = props.getProperty("#{tool}.version.time").substring(0,10)
33
+ rescue
34
+ out[0] = 'unknown'; out[1] = ""
35
+ end
36
+ out
37
+ end
38
+
39
+ def prop(s:String)
40
+ s = "com.google.appengine.#{s}" unless s.startsWith('java.')
41
+ System.getProperty(s) || ""
42
+ end
43
+
44
+ def tr(key:String, val:String)
45
+ "<tr><td>#{key}</td><td colspan='2'>#{val}</td></tr>"
46
+ end
47
+
48
+ def tr(key:String, val:String, ext:String)
49
+ "<tr><td>#{key}</td><td class='mono'>#{val}</td><td>#{ext}</td></tr>"
50
+ end
51
+
52
+ def tr(key:String, val:String[])
53
+ tr(key, val[0], val[1])
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ import java.lang.System
2
+ import java.io.File
3
+ import stdlib.Array
4
+ import stdlib.Io
5
+
6
+ class SourceController < ApplicationController
7
+
8
+ # GET /source/*
9
+ def doGet(request, response)
10
+ @base = request.getRequestURI
11
+ @base += '/' unless @base.endsWith('/')
12
+ # @path = request.getPathInfo || "" # (index).html added
13
+ @path = @base.substring(7, @base.length - 1)
14
+ node = File.new(System.getProperty('user.dir') + @path)
15
+ if node.isDirectory
16
+ @entries = Array.sort(node.listFiles)
17
+ response.getWriter.write(list_erb)
18
+ else
19
+ if @path.matches "^\/public\/.+\.(ico|gif|jpe?g|png)$"
20
+ response.sendRedirect @path.substring(7, @path.length); nil
21
+ else
22
+ @content = node.isFile ? Io.read(node) : 'Sorry, no file'
23
+ response.getWriter.write(show_erb)
24
+ end
25
+ end
26
+ end
27
+
28
+ def_edb(show_erb, 'views/source/show.html.erb')
29
+ def_edb(list_erb, 'views/source/list.html.erb')
30
+ end
@@ -0,0 +1,28 @@
1
+ import com.google.appengine.ext.duby.db.Model
2
+ import com.google.appengine.api.datastore.*
3
+ import dubious.TimeConversion
4
+ import java.util.Date
5
+
6
+ class Contact < Model
7
+ property :title, String
8
+ property :birthday, Date
9
+ property :url, Link
10
+ property :platform, String
11
+ property :editor, String
12
+ property :summary, Text
13
+ property :address, PostalAddress
14
+ property :phone, PhoneNumber
15
+ property :private, Boolean
16
+
17
+ # timestamps
18
+ property :created_at, Date
19
+ property :updated_at, Date
20
+ def before_save
21
+ @updated_at = Date.new
22
+ @created_at = updated_at if @created_at.nil?
23
+ end
24
+
25
+ def coerce_date(o:Object)
26
+ TimeConversion.new('jsdate').parse(String(o))
27
+ end
28
+ end