mirah_model 0.0.1-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.
- data/README.md +35 -0
- data/Rakefile +67 -0
- data/lib/mirah_model/datastore.rb +375 -0
- data/test/com/google/appengine/ext/duby/db/ModelTest.class +0 -0
- data/test/com/google/appengine/ext/duby/db/TestModel$Query.class +0 -0
- data/test/com/google/appengine/ext/duby/db/TestModel.class +0 -0
- data/test/model_test.mirah +115 -0
- metadata +97 -0
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            Mirah Model
         | 
| 2 | 
            +
            ================
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Mirah Model is a Mirah ORM library for using AppEngine's datastore. It is inspired by ActiveRecord and DataMapper--particularly DataMapper. It's used by Dubious, a Rails-ish web framework.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Code Examples
         | 
| 7 | 
            +
            ------
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            If you are familar with DataMapper's property methods this should look pretty familiar. You can define properties using #property, passing the property name and a type.
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                import com.google.appengine.api.datastore.* 
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                class Shout < Model
         | 
| 14 | 
            +
                  property 'title', String
         | 
| 15 | 
            +
                  property 'body',  Text
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            You can set instances by updating their attrs individually,
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                shout = Shout.new
         | 
| 21 | 
            +
                shout.title = 'foo'
         | 
| 22 | 
            +
                shout.body  = 'bar'
         | 
| 23 | 
            +
                shout.save
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            or by using the update method and passing a hash
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                shout = Shout.new.update title: 'foo', body: 'bar'
         | 
| 28 | 
            +
                shout.save
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            Development
         | 
| 31 | 
            +
            ------------
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            You can build the jar by running:
         | 
| 34 | 
            +
             | 
| 35 | 
            +
               rake jar
         | 
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            require 'rubygems'
         | 
| 2 | 
            +
            require 'rubygems/package_task'
         | 
| 3 | 
            +
            require 'ant'
         | 
| 4 | 
            +
            require 'appengine-sdk'
         | 
| 5 | 
            +
            require 'mirah_task'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
             | 
| 8 | 
            +
            Gem::PackageTask.new Gem::Specification.load('mirah_model.gemspec') do |pkg|
         | 
| 9 | 
            +
              pkg.need_zip = true
         | 
| 10 | 
            +
              pkg.need_tar = true
         | 
| 11 | 
            +
            end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            task :gem => :jar
         | 
| 14 | 
            +
            #require 'maven/junit/junit'
         | 
| 15 | 
            +
            # -- will work after maven support --JUNIT_JAR = Gem.find_files('maven/junit/junit.jar').first
         | 
| 16 | 
            +
            JUNIT_JAR = 'javalib/junit.jar'
         | 
| 17 | 
            +
            TESTING_JARS = [AppEngine::SDK::API_JAR, AppEngine::SDK::LABS_JAR, JUNIT_JAR
         | 
| 18 | 
            +
            ] +
         | 
| 19 | 
            +
              AppEngine::SDK::RUNTIME_JARS.reject {|j| j =~ /appengine-local-runtime/}
         | 
| 20 | 
            +
            TESTING_JARS.each {|jar| $CLASSPATH << jar}
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            # Mirah.compiler_options = ['-V']
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            task :default => :test
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            task :init do
         | 
| 27 | 
            +
              mkdir_p 'dist'
         | 
| 28 | 
            +
              mkdir_p 'build'
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            task :clean do
         | 
| 32 | 
            +
              ant.delete :quiet => true, :dir => 'build'
         | 
| 33 | 
            +
              ant.delete :quiet => true, :dir => 'dist'
         | 
| 34 | 
            +
            end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            task :compile => :init do
         | 
| 37 | 
            +
              # build the Duby sources
         | 
| 38 | 
            +
              puts "Compiling Duby sources"
         | 
| 39 | 
            +
              mirahc '.', :dir => 'src', :dest => 'build'
         | 
| 40 | 
            +
            end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            desc "run tests"
         | 
| 43 | 
            +
            task :compile_test => :jar do
         | 
| 44 | 
            +
              puts "Compiling Duby tests"
         | 
| 45 | 
            +
              mirahc '.', :dir => 'test', :dest => 'test',
         | 
| 46 | 
            +
                     :options => ['--classpath', Dir.pwd + "/dist/dubydatastore.jar"]
         | 
| 47 | 
            +
            end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            desc "build jar"
         | 
| 50 | 
            +
            task :jar => :compile do
         | 
| 51 | 
            +
              ant.jar :jarfile => 'dist/dubydatastore.jar' do
         | 
| 52 | 
            +
                fileset :dir => 'lib'
         | 
| 53 | 
            +
                fileset :dir => 'build'
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            task :test => :compile_test do
         | 
| 58 | 
            +
              ant.junit :haltonfailure => 'true', :fork => 'true' do
         | 
| 59 | 
            +
                classpath :path => (TESTING_JARS + ['build', 'test']).join(":")
         | 
| 60 | 
            +
                batchtest do
         | 
| 61 | 
            +
                  fileset :dir => "test" do
         | 
| 62 | 
            +
                    include :name => '**/*Test.class'
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                  formatter :type => 'plain', :usefile => 'false'
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
| @@ -0,0 +1,375 @@ | |
| 1 | 
            +
            module AppEngine
         | 
| 2 | 
            +
              class DubyDatastorePlugin
         | 
| 3 | 
            +
                @models = {}
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                TypeMap = {
         | 
| 6 | 
            +
                  'Category' => 'String',
         | 
| 7 | 
            +
                  'Email' => 'String',
         | 
| 8 | 
            +
                  'Link' => 'String',
         | 
| 9 | 
            +
                  'PhoneNumber' => 'String',
         | 
| 10 | 
            +
                  'PostalAddress' => 'String',
         | 
| 11 | 
            +
                  'Text' => 'String',
         | 
| 12 | 
            +
                  'Blob' => 'byte[]',
         | 
| 13 | 
            +
                  'ShortBlob' => 'byte[]',
         | 
| 14 | 
            +
                  'Rating' => 'int',
         | 
| 15 | 
            +
                  'Long' => 'long',
         | 
| 16 | 
            +
                  'Double' => 'double',
         | 
| 17 | 
            +
                  'Boolean' => 'boolean'
         | 
| 18 | 
            +
                }
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                Primitives = ['Long', 'Double', 'Boolean', 'Rating']
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                Boxes = {
         | 
| 23 | 
            +
                  'int' => 'Integer',
         | 
| 24 | 
            +
                  'boolean' => 'Boolean',
         | 
| 25 | 
            +
                  'long' => 'Long',
         | 
| 26 | 
            +
                  'double' => 'Double',
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                Defaults = {
         | 
| 30 | 
            +
                  'Rating' => '0',
         | 
| 31 | 
            +
                  'Long' => 'long(0)',
         | 
| 32 | 
            +
                  'Double' => '0.0',
         | 
| 33 | 
            +
                  'Boolean' => 'false',
         | 
| 34 | 
            +
                  'Blob' => 'byte[].cast(nil)',
         | 
| 35 | 
            +
                  'ShortBlob' => 'byte[].cast(nil)',
         | 
| 36 | 
            +
                }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                Conversions = {
         | 
| 39 | 
            +
                  'Category' => 'getCategory',
         | 
| 40 | 
            +
                  'Email' => 'getEmail',
         | 
| 41 | 
            +
                  'Link' => 'getValue',
         | 
| 42 | 
            +
                  'PhoneNumber' => 'getNumber',
         | 
| 43 | 
            +
                  'PostalAddress' => 'getAddress',
         | 
| 44 | 
            +
                  'Text' => 'getValue',
         | 
| 45 | 
            +
                  'Blob' => 'getBytes',
         | 
| 46 | 
            +
                  'ShortBlob' => 'getBytes',
         | 
| 47 | 
            +
                  'Long' => 'longValue',
         | 
| 48 | 
            +
                  'Double' => 'doubleValue',
         | 
| 49 | 
            +
                  'Boolean' => 'booleanValue',
         | 
| 50 | 
            +
                  'Rating' => 'getRating',
         | 
| 51 | 
            +
                }
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                class ModelState
         | 
| 54 | 
            +
                  include Duby::AST
         | 
| 55 | 
            +
                  attr_reader :kind, :query, :read, :save, :transformer
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  def initialize(transformer, klass, parent, position, ast)
         | 
| 58 | 
            +
                    @transformer = transformer
         | 
| 59 | 
            +
                    @kind = klass.name.split('.')[-1]
         | 
| 60 | 
            +
                    init_query(klass.name, parent, position, ast)
         | 
| 61 | 
            +
                    init_static(parent, ast)
         | 
| 62 | 
            +
                    init_read(parent, position, ast)
         | 
| 63 | 
            +
                    init_save(parent, position, ast)
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def init_query(classname, parent, position, ast)
         | 
| 67 | 
            +
                    name = "#{classname}$Query"
         | 
| 68 | 
            +
                    @query = ClassDefinition.new(parent, position, name) do |classdef|
         | 
| 69 | 
            +
                      queryinit = <<-EOF
         | 
| 70 | 
            +
                        def initialize; end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                        def kind
         | 
| 73 | 
            +
                          "#{kind}"
         | 
| 74 | 
            +
                        end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                        def first
         | 
| 77 | 
            +
                          it = _prepare.asIterator
         | 
| 78 | 
            +
                          if it.hasNext
         | 
| 79 | 
            +
                            e = Entity(it.next)
         | 
| 80 | 
            +
                            m = #{kind}.new
         | 
| 81 | 
            +
                            m._read_from(e)
         | 
| 82 | 
            +
                            m
         | 
| 83 | 
            +
                          else
         | 
| 84 | 
            +
                            #{kind}(nil)
         | 
| 85 | 
            +
                          end
         | 
| 86 | 
            +
                        end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                        def run
         | 
| 89 | 
            +
                          entities = _prepare.asList(_options)
         | 
| 90 | 
            +
                          models = #{kind}[entities.size]
         | 
| 91 | 
            +
                          it = entities.iterator
         | 
| 92 | 
            +
                          i = 0
         | 
| 93 | 
            +
                          while (it.hasNext)
         | 
| 94 | 
            +
                            e = Entity(it.next)
         | 
| 95 | 
            +
                            m = #{kind}.new
         | 
| 96 | 
            +
                            m._read_from(e)
         | 
| 97 | 
            +
                            models[i] = m
         | 
| 98 | 
            +
                            i += 1
         | 
| 99 | 
            +
                          end
         | 
| 100 | 
            +
                          models
         | 
| 101 | 
            +
                        end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                        def sort(name:String)
         | 
| 104 | 
            +
                          sort(name, false)
         | 
| 105 | 
            +
                        end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                        def sort(name:String, descending:boolean)
         | 
| 108 | 
            +
                          _sort(name, descending)
         | 
| 109 | 
            +
                          self
         | 
| 110 | 
            +
                        end
         | 
| 111 | 
            +
                      EOF
         | 
| 112 | 
            +
                      [Duby::AST.type(nil, 'com.google.appengine.ext.duby.db.DQuery'),
         | 
| 113 | 
            +
                       eval(classdef, queryinit)]
         | 
| 114 | 
            +
                    end
         | 
| 115 | 
            +
                    ast << @query
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def init_read(parent, position, ast)
         | 
| 119 | 
            +
                    @read = eval(parent, <<-EOF)
         | 
| 120 | 
            +
                      def _read_from(e:Entity)
         | 
| 121 | 
            +
                        self.entity = e
         | 
| 122 | 
            +
                        nil
         | 
| 123 | 
            +
                      end
         | 
| 124 | 
            +
                    EOF
         | 
| 125 | 
            +
                    ast << @read
         | 
| 126 | 
            +
                    get_properties = eval(parent, <<-EOF)
         | 
| 127 | 
            +
                      def properties
         | 
| 128 | 
            +
                        result = super()
         | 
| 129 | 
            +
                        nil
         | 
| 130 | 
            +
                        result
         | 
| 131 | 
            +
                      end
         | 
| 132 | 
            +
                    EOF
         | 
| 133 | 
            +
                    @get_properties = get_properties.body.children[1] =
         | 
| 134 | 
            +
                        Duby::AST::Body.new(get_properties.body, position)
         | 
| 135 | 
            +
                    ast << get_properties
         | 
| 136 | 
            +
                    update = eval(parent, <<-EOF)
         | 
| 137 | 
            +
                      def update(properties:Map)
         | 
| 138 | 
            +
                        nil
         | 
| 139 | 
            +
                        self
         | 
| 140 | 
            +
                      end
         | 
| 141 | 
            +
                    EOF
         | 
| 142 | 
            +
                    @update = update.body.children[0] = Body.new(update.body, position) {[]}
         | 
| 143 | 
            +
                    ast << update
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                  def init_save(parent, position, ast)
         | 
| 147 | 
            +
                    @save = eval(parent, <<-EOF)
         | 
| 148 | 
            +
                      def _save_to(e:Entity)
         | 
| 149 | 
            +
                      end
         | 
| 150 | 
            +
                    EOF
         | 
| 151 | 
            +
                    @save.body = Body.new(@save, position) {[]}
         | 
| 152 | 
            +
                    ast << @save
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                  def init_static(parent, ast)
         | 
| 156 | 
            +
                    # TODO These imports don't work any more.  Figure out how to fix that.
         | 
| 157 | 
            +
                    scope = ast.static_scope
         | 
| 158 | 
            +
                    package = "#{scope.package}." unless scope.package.empty?
         | 
| 159 | 
            +
                    scope.import('java.util.Map', 'Map')
         | 
| 160 | 
            +
                    scope.import("#{package}#{kind}$Query", "#{kind}__Query__")
         | 
| 161 | 
            +
                    %w( Entity Blob Category Email GeoPt IMHandle Key
         | 
| 162 | 
            +
                        Link PhoneNumber PostalAddress Rating ShortBlob
         | 
| 163 | 
            +
                        Text KeyFactory EntityNotFoundException ).each do |name|
         | 
| 164 | 
            +
                      scope.import("com.google.appengine.api.datastore.#{name}", name)
         | 
| 165 | 
            +
                    end
         | 
| 166 | 
            +
                    ast << eval(parent, <<-EOF)
         | 
| 167 | 
            +
                      def initialize
         | 
| 168 | 
            +
                        super
         | 
| 169 | 
            +
                      end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                      def initialize(key_name:String)
         | 
| 172 | 
            +
                        super
         | 
| 173 | 
            +
                      end
         | 
| 174 | 
            +
                      
         | 
| 175 | 
            +
                      def initialize(parent:Model)
         | 
| 176 | 
            +
                        super
         | 
| 177 | 
            +
                      end
         | 
| 178 | 
            +
                      
         | 
| 179 | 
            +
                      def initialize(parent:Key)
         | 
| 180 | 
            +
                        super
         | 
| 181 | 
            +
                      end
         | 
| 182 | 
            +
                      
         | 
| 183 | 
            +
                      def initialize(parent:Model, key_name:String)
         | 
| 184 | 
            +
                        super
         | 
| 185 | 
            +
                      end
         | 
| 186 | 
            +
                      
         | 
| 187 | 
            +
                      def initialize(parent:Key, key_name:String)
         | 
| 188 | 
            +
                        super
         | 
| 189 | 
            +
                      end
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                      def self.get(key:Key)
         | 
| 192 | 
            +
                        begin
         | 
| 193 | 
            +
                          m = #{kind}.new
         | 
| 194 | 
            +
                          m._read_from(Model._datastore.get(key))
         | 
| 195 | 
            +
                          m
         | 
| 196 | 
            +
                        rescue EntityNotFoundException
         | 
| 197 | 
            +
                          nil
         | 
| 198 | 
            +
                        end
         | 
| 199 | 
            +
                      end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                      def self.get(key_name:String)
         | 
| 202 | 
            +
                        get(KeyFactory.createKey("#{kind}", key_name))
         | 
| 203 | 
            +
                      end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                      def self.get(id:long)
         | 
| 206 | 
            +
                        get(KeyFactory.createKey("#{kind}", id))
         | 
| 207 | 
            +
                      end
         | 
| 208 | 
            +
             | 
| 209 | 
            +
                      def self.get(parent:Key, key_name:String)
         | 
| 210 | 
            +
                        get(KeyFactory.createKey(parent, "#{kind}", key_name))
         | 
| 211 | 
            +
                      end
         | 
| 212 | 
            +
             | 
| 213 | 
            +
                      def self.get(parent:Key, id:long)
         | 
| 214 | 
            +
                        get(KeyFactory.createKey(parent, "#{kind}", id))
         | 
| 215 | 
            +
                      end
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                      def self.get(parent:Model, key_name:String)
         | 
| 218 | 
            +
                        get(KeyFactory.createKey(parent.key, "#{kind}", key_name))
         | 
| 219 | 
            +
                      end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                      def self.get(parent:Model, id:long)
         | 
| 222 | 
            +
                        get(KeyFactory.createKey(parent.key, "#{kind}", id))
         | 
| 223 | 
            +
                      end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                      def self.all
         | 
| 226 | 
            +
                        #{kind}__Query__.new
         | 
| 227 | 
            +
                      end
         | 
| 228 | 
            +
                    EOF
         | 
| 229 | 
            +
                  end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                  def eval(parent, src)
         | 
| 232 | 
            +
                    transformer.eval(src, __FILE__, parent)
         | 
| 233 | 
            +
                  end
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                  def extend_query(code)
         | 
| 236 | 
            +
                    query.body << eval(query.body, code)
         | 
| 237 | 
            +
                  end
         | 
| 238 | 
            +
             | 
| 239 | 
            +
                  def extend_update(code)
         | 
| 240 | 
            +
                    @update << eval(@update, code)
         | 
| 241 | 
            +
                  end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                  def extend_get_properties(code)
         | 
| 244 | 
            +
                    @get_properties << eval(@get_properties, code)
         | 
| 245 | 
            +
                  end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                  def extend_read(code)
         | 
| 248 | 
            +
                    code = 'e=nil;' + code
         | 
| 249 | 
            +
                    eval(read.body, code).children[1..-1].each do |node|
         | 
| 250 | 
            +
                      read.body << node
         | 
| 251 | 
            +
                    end
         | 
| 252 | 
            +
                  end
         | 
| 253 | 
            +
             | 
| 254 | 
            +
                  def extend_save(code)
         | 
| 255 | 
            +
                    code = 'e=nil;' + code
         | 
| 256 | 
            +
                    eval(save.body, code).children[1..-1].each do |node|
         | 
| 257 | 
            +
                      save.body << node
         | 
| 258 | 
            +
                    end
         | 
| 259 | 
            +
                  end
         | 
| 260 | 
            +
                end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                def self.find_class(node)
         | 
| 263 | 
            +
                  node = node.parent until Duby::AST::ClassDefinition === node
         | 
| 264 | 
            +
                  node
         | 
| 265 | 
            +
                end
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                def self.to_datastore(type, value)
         | 
| 268 | 
            +
                  if Primitives.include?(type)
         | 
| 269 | 
            +
                    "#{type}.new(#{value})"
         | 
| 270 | 
            +
                  elsif TypeMap.include?(type)
         | 
| 271 | 
            +
                    "(#{value} ? #{type}.new(#{value}) : nil)"
         | 
| 272 | 
            +
                  else
         | 
| 273 | 
            +
                    value
         | 
| 274 | 
            +
                  end
         | 
| 275 | 
            +
                end
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                def self.from_datastore(type, value)
         | 
| 278 | 
            +
                  duby_type = TypeMap[type]
         | 
| 279 | 
            +
                  if duby_type
         | 
| 280 | 
            +
                    default = Defaults.fetch(type, "#{duby_type}(nil)")
         | 
| 281 | 
            +
                    conversion = Conversions[type]
         | 
| 282 | 
            +
                    "(#{value} ? #{type}(#{value}).#{conversion} : #{default})"
         | 
| 283 | 
            +
                  else
         | 
| 284 | 
            +
                    "#{type}(#{value})"
         | 
| 285 | 
            +
                  end
         | 
| 286 | 
            +
                end
         | 
| 287 | 
            +
             | 
| 288 | 
            +
             | 
| 289 | 
            +
                def self.add_property(name, type, transformer, fcall)
         | 
| 290 | 
            +
                  if transformer.state != @state
         | 
| 291 | 
            +
                    reset
         | 
| 292 | 
            +
                    @state = transformer.state
         | 
| 293 | 
            +
                  end
         | 
| 294 | 
            +
                  parent = fcall.parent
         | 
| 295 | 
            +
                  name = name.literal
         | 
| 296 | 
            +
                  type = type.name
         | 
| 297 | 
            +
                  type = 'Long' if type == 'Integer'
         | 
| 298 | 
            +
             | 
| 299 | 
            +
                  result = Duby::AST::ScopedBody.new(parent, fcall.position) {[]}
         | 
| 300 | 
            +
                  result.static_scope = fcall.scope.static_scope
         | 
| 301 | 
            +
                  klass = find_class(parent)
         | 
| 302 | 
            +
                  unless @models[klass]
         | 
| 303 | 
            +
                    @models[klass] = ModelState.new(
         | 
| 304 | 
            +
                        transformer, klass, parent, fcall.position, result)
         | 
| 305 | 
            +
                  end
         | 
| 306 | 
            +
                  model = @models[klass]
         | 
| 307 | 
            +
             | 
| 308 | 
            +
                  duby_type = TypeMap.fetch(type, type)
         | 
| 309 | 
            +
                  coercion = "coerce_" + duby_type.downcase.sub("[]", "s")
         | 
| 310 | 
            +
             | 
| 311 | 
            +
                  if duby_type == 'List'
         | 
| 312 | 
            +
                    model.extend_query(<<-EOF)
         | 
| 313 | 
            +
                      def #{name}(value:Object)
         | 
| 314 | 
            +
                        returns :void
         | 
| 315 | 
            +
                        _query.addFilter("#{name}", _eq_op, value)
         | 
| 316 | 
            +
                      end
         | 
| 317 | 
            +
                    EOF
         | 
| 318 | 
            +
                  else
         | 
| 319 | 
            +
                    model.extend_query(<<-EOF)
         | 
| 320 | 
            +
                      def #{name}(value:#{duby_type})
         | 
| 321 | 
            +
                        returns :void
         | 
| 322 | 
            +
                        _query.addFilter("#{name}", _eq_op, #{to_datastore(type, 'value')})
         | 
| 323 | 
            +
                      end
         | 
| 324 | 
            +
                    EOF
         | 
| 325 | 
            +
                  end
         | 
| 326 | 
            +
                  temp = transformer.tmp
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                  model.extend_read(<<-EOF)
         | 
| 329 | 
            +
                    #{temp} = e.getProperty("#{name}")
         | 
| 330 | 
            +
                    @#{name} = #{from_datastore(type, temp)}
         | 
| 331 | 
            +
                  EOF
         | 
| 332 | 
            +
             | 
| 333 | 
            +
                  model.extend_save(<<-EOF)
         | 
| 334 | 
            +
                    e.setProperty("#{name}", #{to_datastore(type, '@' + name)})
         | 
| 335 | 
            +
                  EOF
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                  model.extend_update(<<-EOF)
         | 
| 338 | 
            +
                    self.#{name} = properties.get("#{name}") if properties.containsKey("#{name}")
         | 
| 339 | 
            +
                  EOF
         | 
| 340 | 
            +
             | 
| 341 | 
            +
                  model.extend_get_properties(<<-EOF)
         | 
| 342 | 
            +
                    result.put("#{name}", #{maybe_box(duby_type, 'self.' + name)})
         | 
| 343 | 
            +
                  EOF
         | 
| 344 | 
            +
             | 
| 345 | 
            +
                  result << model.eval(parent, <<-EOF)
         | 
| 346 | 
            +
                    def #{name}
         | 
| 347 | 
            +
                      @#{name}
         | 
| 348 | 
            +
                    end
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                    def #{name}=(value:#{duby_type})
         | 
| 351 | 
            +
                      @#{name} = value
         | 
| 352 | 
            +
                    end
         | 
| 353 | 
            +
             | 
| 354 | 
            +
                    def #{name}=(value:Object)
         | 
| 355 | 
            +
                      self.#{name} = #{coercion}(value)
         | 
| 356 | 
            +
                    end
         | 
| 357 | 
            +
                  EOF
         | 
| 358 | 
            +
             | 
| 359 | 
            +
                  result
         | 
| 360 | 
            +
                end
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                def self.maybe_box(duby_type, value)
         | 
| 363 | 
            +
                  if Boxes.include?(duby_type)
         | 
| 364 | 
            +
                    "#{Boxes[duby_type]}.valueOf(#{value})"
         | 
| 365 | 
            +
                  else
         | 
| 366 | 
            +
                    value
         | 
| 367 | 
            +
                  end
         | 
| 368 | 
            +
                end
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                def self.reset
         | 
| 371 | 
            +
                  @models = {}
         | 
| 372 | 
            +
                end
         | 
| 373 | 
            +
              end
         | 
| 374 | 
            +
              ::Duby.plugins << DubyDatastorePlugin
         | 
| 375 | 
            +
            end
         | 
| Binary file | 
| Binary file | 
| Binary file | 
| @@ -0,0 +1,115 @@ | |
| 1 | 
            +
            package com.google.appengine.ext.duby.db
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig
         | 
| 4 | 
            +
            import com.google.appengine.tools.development.testing.LocalServiceTestConfig
         | 
| 5 | 
            +
            import com.google.appengine.tools.development.testing.LocalServiceTestHelper
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            import org.junit.After
         | 
| 8 | 
            +
            import org.junit.Before
         | 
| 9 | 
            +
            import org.junit.Test
         | 
| 10 | 
            +
            import org.junit.Assert
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            import com.google.appengine.api.datastore.Blob
         | 
| 13 | 
            +
            import com.google.appengine.api.datastore.Category
         | 
| 14 | 
            +
            import com.google.appengine.api.datastore.Email
         | 
| 15 | 
            +
            import com.google.appengine.api.datastore.GeoPt
         | 
| 16 | 
            +
            import com.google.appengine.api.datastore.IMHandle
         | 
| 17 | 
            +
            import com.google.appengine.api.datastore.Key
         | 
| 18 | 
            +
            import com.google.appengine.api.datastore.Link
         | 
| 19 | 
            +
            import com.google.appengine.api.datastore.PhoneNumber
         | 
| 20 | 
            +
            import com.google.appengine.api.datastore.PostalAddress
         | 
| 21 | 
            +
            import com.google.appengine.api.datastore.Rating
         | 
| 22 | 
            +
            import com.google.appengine.api.datastore.ShortBlob
         | 
| 23 | 
            +
            import com.google.appengine.api.datastore.Text
         | 
| 24 | 
            +
            import java.util.Date
         | 
| 25 | 
            +
            import java.util.HashMap
         | 
| 26 | 
            +
            import java.util.List
         | 
| 27 | 
            +
            import java.util.ArrayList
         | 
| 28 | 
            +
            import com.google.appengine.api.users.User
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            class TestModel < Model
         | 
| 31 | 
            +
              property 'date', Date
         | 
| 32 | 
            +
              property 'akey', Key
         | 
| 33 | 
            +
              property 'blob', Blob
         | 
| 34 | 
            +
              property 'shortblob', ShortBlob
         | 
| 35 | 
            +
              property 'category', Category
         | 
| 36 | 
            +
              property 'email', Email
         | 
| 37 | 
            +
              property 'geopt', GeoPt
         | 
| 38 | 
            +
              property 'imhandle', IMHandle
         | 
| 39 | 
            +
              property 'link', Link
         | 
| 40 | 
            +
              property 'phonenumber', PhoneNumber
         | 
| 41 | 
            +
              property 'address', PostalAddress
         | 
| 42 | 
            +
              property 'rating', Rating
         | 
| 43 | 
            +
              property 'text', Text
         | 
| 44 | 
            +
              property 'string', String
         | 
| 45 | 
            +
              property 'integer', Long
         | 
| 46 | 
            +
              property 'afloat', Double
         | 
| 47 | 
            +
              property 'user', User
         | 
| 48 | 
            +
              property 'list', List
         | 
| 49 | 
            +
            end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            class ModelTest
         | 
| 52 | 
            +
              include Assert
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              def helper
         | 
| 55 | 
            +
                @helper ||= begin
         | 
| 56 | 
            +
                  configs = LocalServiceTestConfig[1]
         | 
| 57 | 
            +
                  configs[0] = LocalDatastoreServiceTestConfig.new
         | 
| 58 | 
            +
                  LocalServiceTestHelper.new(configs)
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              $Before
         | 
| 63 | 
            +
              def setup
         | 
| 64 | 
            +
                returns void
         | 
| 65 | 
            +
                helper.setUp
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              $After
         | 
| 69 | 
            +
              def teardown
         | 
| 70 | 
            +
                returns void
         | 
| 71 | 
            +
                helper.tearDown
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
              $Test
         | 
| 75 | 
            +
              def test_get_and_put
         | 
| 76 | 
            +
                returns void
         | 
| 77 | 
            +
                e = TestModel.new
         | 
| 78 | 
            +
                e.string = "Hello"
         | 
| 79 | 
            +
                e.save
         | 
| 80 | 
            +
                e2 = TestModel.get(e.key)
         | 
| 81 | 
            +
                assertEquals("Hello", e2.string)
         | 
| 82 | 
            +
                assertEquals(e.key, e2.key)
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
              $Test
         | 
| 86 | 
            +
              def test_sort
         | 
| 87 | 
            +
                returns void
         | 
| 88 | 
            +
                # just make sure it compiles
         | 
| 89 | 
            +
                TestModel.all.sort('link').run
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              $Test
         | 
| 93 | 
            +
              def test_properties
         | 
| 94 | 
            +
                returns void
         | 
| 95 | 
            +
                e = TestModel.new
         | 
| 96 | 
            +
                e.string = "Hi"
         | 
| 97 | 
            +
                e.rating = 10
         | 
| 98 | 
            +
                properties = e.properties
         | 
| 99 | 
            +
                assertEquals("Hi", properties.get("string"))
         | 
| 100 | 
            +
                assertEquals(Integer.valueOf(10), properties.get("rating"))
         | 
| 101 | 
            +
                assertTrue(properties.containsKey("blob"))
         | 
| 102 | 
            +
                assertEquals(nil, properties.get("blob"))
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              $Test
         | 
| 106 | 
            +
              def test_query_list
         | 
| 107 | 
            +
                returns void
         | 
| 108 | 
            +
                e = TestModel.new
         | 
| 109 | 
            +
                e.list = ["a", "b", "c"]
         | 
| 110 | 
            +
                e.save
         | 
| 111 | 
            +
                e2 = TestModel.all.list("b").first
         | 
| 112 | 
            +
                assertEquals(e.key, e2.key)
         | 
| 113 | 
            +
              end
         | 
| 114 | 
            +
              # TODO more tests.
         | 
| 115 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,97 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            +
            name: mirah_model
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            +
              prerelease: false
         | 
| 5 | 
            +
              segments: 
         | 
| 6 | 
            +
                - 0
         | 
| 7 | 
            +
                - 0
         | 
| 8 | 
            +
                - 1
         | 
| 9 | 
            +
              version: 0.0.1
         | 
| 10 | 
            +
            platform: java
         | 
| 11 | 
            +
            authors: 
         | 
| 12 | 
            +
              - Ryan Brown
         | 
| 13 | 
            +
            autorequire: 
         | 
| 14 | 
            +
            bindir: bin
         | 
| 15 | 
            +
            cert_chain: []
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            date: 2010-12-15 08:47:11.863000 -07:00
         | 
| 18 | 
            +
            default_executable: 
         | 
| 19 | 
            +
            dependencies: 
         | 
| 20 | 
            +
              - !ruby/object:Gem::Dependency 
         | 
| 21 | 
            +
                name: mirah
         | 
| 22 | 
            +
                prerelease: false
         | 
| 23 | 
            +
                requirement: &id001 !ruby/object:Gem::Requirement 
         | 
| 24 | 
            +
                  requirements: 
         | 
| 25 | 
            +
                    - - ">="
         | 
| 26 | 
            +
                      - !ruby/object:Gem::Version 
         | 
| 27 | 
            +
                        segments: 
         | 
| 28 | 
            +
                          - 0
         | 
| 29 | 
            +
                          - 0
         | 
| 30 | 
            +
                          - 4
         | 
| 31 | 
            +
                        version: 0.0.4
         | 
| 32 | 
            +
                type: :runtime
         | 
| 33 | 
            +
                version_requirements: *id001
         | 
| 34 | 
            +
              - !ruby/object:Gem::Dependency 
         | 
| 35 | 
            +
                name: appengine-sdk
         | 
| 36 | 
            +
                prerelease: false
         | 
| 37 | 
            +
                requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 38 | 
            +
                  requirements: 
         | 
| 39 | 
            +
                    - - ~>
         | 
| 40 | 
            +
                      - !ruby/object:Gem::Version 
         | 
| 41 | 
            +
                        segments: 
         | 
| 42 | 
            +
                          - 1
         | 
| 43 | 
            +
                          - 4
         | 
| 44 | 
            +
                          - 0
         | 
| 45 | 
            +
                        version: 1.4.0
         | 
| 46 | 
            +
                type: :runtime
         | 
| 47 | 
            +
                version_requirements: *id002
         | 
| 48 | 
            +
            description: Mirah Model is a ORM library for App Engine's datastore. It is inspired by ActiveRecord and DataMapper. It's used by Dubious, a Rails-ish web framework.
         | 
| 49 | 
            +
            email: 
         | 
| 50 | 
            +
              - ribrdb@google.com
         | 
| 51 | 
            +
            executables: []
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            extensions: []
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            extra_rdoc_files: 
         | 
| 56 | 
            +
              - README.md
         | 
| 57 | 
            +
            files: 
         | 
| 58 | 
            +
              - lib/mirah_model/datastore.rb
         | 
| 59 | 
            +
              - test/model_test.mirah
         | 
| 60 | 
            +
              - test/com/google/appengine/ext/duby/db/ModelTest.class
         | 
| 61 | 
            +
              - test/com/google/appengine/ext/duby/db/TestModel$Query.class
         | 
| 62 | 
            +
              - test/com/google/appengine/ext/duby/db/TestModel.class
         | 
| 63 | 
            +
              - README.md
         | 
| 64 | 
            +
              - Rakefile
         | 
| 65 | 
            +
            has_rdoc: true
         | 
| 66 | 
            +
            homepage: http://www.mirah.org/
         | 
| 67 | 
            +
            licenses: []
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            post_install_message: 
         | 
| 70 | 
            +
            rdoc_options: 
         | 
| 71 | 
            +
              - --main
         | 
| 72 | 
            +
              - README.md
         | 
| 73 | 
            +
            require_paths: 
         | 
| 74 | 
            +
              - lib
         | 
| 75 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 76 | 
            +
              requirements: 
         | 
| 77 | 
            +
                - - ">="
         | 
| 78 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 79 | 
            +
                    segments: 
         | 
| 80 | 
            +
                      - 0
         | 
| 81 | 
            +
                    version: "0"
         | 
| 82 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 83 | 
            +
              requirements: 
         | 
| 84 | 
            +
                - - ">="
         | 
| 85 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 86 | 
            +
                    segments: 
         | 
| 87 | 
            +
                      - 0
         | 
| 88 | 
            +
                    version: "0"
         | 
| 89 | 
            +
            requirements: []
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            rubyforge_project: 
         | 
| 92 | 
            +
            rubygems_version: 1.3.6
         | 
| 93 | 
            +
            signing_key: 
         | 
| 94 | 
            +
            specification_version: 3
         | 
| 95 | 
            +
            summary: Mirah Model is a ORM library for App Engine's datastore.
         | 
| 96 | 
            +
            test_files: []
         | 
| 97 | 
            +
             |