dubious 0.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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