diff --git a/Mavenfile b/Mavenfile index 6318f16..801d471 100644 --- a/Mavenfile +++ b/Mavenfile @@ -24,18 +24,6 @@ packaging 'java-gem' build.final_name '${project.artifactId}_ext' -profile(:transient) do |t| - t.plugin(:rspec).configuration[:specSourceDirectory] = 'spec/transient' -end - -profile(:adapter) do |t| - t.plugin(:rspec).configuration[:specSourceDirectory] = 'spec/abstract_adapter' -end - -profile(:dm) do |t| - t.plugin(:rspec).configuration[:specSourceDirectory] = 'spec/dm_core' -end - # copy the pom to pom.xml so java IDEs can use it execute_in_phase(:initialize) do require 'fileutils' diff --git a/lib/dm-hibernate-adapter.rb b/lib/dm-hibernate-adapter.rb index d284216..b363bc0 100644 --- a/lib/dm-hibernate-adapter.rb +++ b/lib/dm-hibernate-adapter.rb @@ -47,6 +47,7 @@ require 'dm-hibernate-adapter/hibernate/property_shim' require 'dm-hibernate-adapter/hibernate/dialects' require 'dm-hibernate-adapter/hibernate/transaction' +require 'dm-hibernate-adapter/hibernate/dynamic_java' require 'dm-hibernate-adapter/hibernate/model' require 'dm-hibernate-adapter/data_mapper/adapters/hibernate_adapter' require 'dm-hibernate-adapter/data_mapper/dm-core' diff --git a/lib/dm-hibernate-adapter/data_mapper/dm-core.rb b/lib/dm-hibernate-adapter/data_mapper/dm-core.rb index 427fb97..c63f455 100644 --- a/lib/dm-hibernate-adapter/data_mapper/dm-core.rb +++ b/lib/dm-hibernate-adapter/data_mapper/dm-core.rb @@ -1,3 +1,18 @@ +# -*- coding: utf-8 -*- +# Copyright 2011 Douglas Ferreira, Kristian Meier, Piotr Gęga + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + module DataMapper def self.finalize Model.descendants.each do |model| diff --git a/lib/dm-hibernate-adapter/hibernate/dynamic_java.rb b/lib/dm-hibernate-adapter/hibernate/dynamic_java.rb new file mode 100644 index 0000000..e30dfbe --- /dev/null +++ b/lib/dm-hibernate-adapter/hibernate/dynamic_java.rb @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# Copyright 2011 Douglas Ferreira, Kristian Meier, Piotr Gęga + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module Hibernate::DynamicJava + + def make_java_class(annotations = {}) + add_class_annotation annotations if !annotations.empty? + + reload = Hibernate.allow_reload + become_java!(reload) + + if reload + unless java.lang.Thread.currentThread.context_class_loader.is_a? JRubyClassLoader + cl = java.lang.Thread.currentThread.context_class_loader + if cl.is_a? org.jruby.util.JRubyClassLoader + java.lang.Thread.currentThread.context_class_loader = JRubyClassLoader.new(cl) + else + java.lang.Thread.currentThread.context_class_loader = 'TODO' + end + end + + java.lang.Thread.currentThread.context_class_loader.register(self.java_class) + end + self + end + + def add_get_accessor(name, target_type, annotations = {}, &blk) + get_name = "get#{name.to_s.capitalize}" + define_method(get_name.to_sym, &blk) + mapped_type = to_java_type(target_type).java_class + add_method_signature get_name, [mapped_type] + add_method_annotation get_name, annotations if !annotations.empty? + end + + def add_set_accessor(name, target_type, annotations = {}, &blk) + set_name = "set#{name.to_s.capitalize}" + define_method(set_name.to_sym, &blk) + mapped_type = to_java_type(target_type).java_class + add_method_signature set_name, [JVoid, mapped_type] + add_method_annotation set_name, annotations if !annotations.empty? + end + + def to_java_type(target_type) + target_type = target_type.primitive if target_type.respond_to?(:primitive) + TYPES[target_type] || target_type + end +end diff --git a/lib/dm-hibernate-adapter/hibernate/model.rb b/lib/dm-hibernate-adapter/hibernate/model.rb index d255a40..83051e1 100644 --- a/lib/dm-hibernate-adapter/hibernate/model.rb +++ b/lib/dm-hibernate-adapter/hibernate/model.rb @@ -14,20 +14,10 @@ # limitations under the License. module Hibernate::Model - - TYPES = { - ::String => java.lang.String, - ::Integer => java.lang.Integer, - ::Float => java.lang.Double, - ::BigDecimal => java.math.BigDecimal, - ::Date => java.util.Date, - ::DateTime => java.util.Date, - ::Time => java.util.Date, - ::TrueClass => java.lang.Boolean, - } - def self.included(model) model.extend(ClassMethods) + model.extend(Hibernate::DynamicJava) + model.extend(PropertyTransformer) end module ClassMethods @@ -47,11 +37,6 @@ def auto_migrate!(repo = nil) def auto_upgrade!(repo = nil) end - def to_java_type(type) - TYPES[type] || self.to_java_type(type.primitive) - end - - def to_java_class_name # http://jira.codehaus.org/browse/JRUBY-4601 "rubyobj."+self.to_s.gsub("::",".") @@ -66,10 +51,13 @@ def hibernate! relationship.target_key end - properties.each do |prop| + filtered_properties = multi_keys? ? properties.reject {|prop| prop.key?} : properties + filtered_properties.each do |prop| discriminator = add_java_property(prop) || discriminator end + add_composite_key_property if multi_keys? + # "stolen" from http://github.com/superchris/hibernate annotation = { javax.persistence.Entity => { }, @@ -81,140 +69,159 @@ def hibernate! annotation[javax.persistence.DiscriminatorColumn] = { "name" => discriminator } end - add_class_annotation annotation + Hibernate.add_model(make_java_class(annotation), self) - reload = Hibernate.allow_reload - Hibernate.add_model(become_java!(reload), self) - - if reload - - unless java.lang.Thread.currentThread.context_class_loader.is_a? JRubyClassLoader - cl = java.lang.Thread.currentThread.context_class_loader - if cl.is_a? org.jruby.util.JRubyClassLoader - java.lang.Thread.currentThread.context_class_loader = JRubyClassLoader.new(cl) - else - java.lang.Thread.currentThread.context_class_loader = 'TODO' - end - end - - java.lang.Thread.currentThread.context_class_loader.register(java_class) - end @@logger.debug "become_java! #{java_class}" else @@logger.debug "become_java! fired already #{java_class}" end end - private + def multi_keys? + key.size > 1 + end - # "stolen" from http://github.com/superchris/hibernate - def add_java_property(prop) - @@logger.info("#{prop.model.name} gets property added #{prop.name}") - name = prop.name - type = prop.class - return name if (type == DataMapper::Property::Discriminator) + def add_composite_key_property + add_get_accessor(:composite_key, composite_class, javax.persistence.EmbeddedId => {}) do + # if !instance_variable_defined?(:@composite_key) || @composite_key.nil? + # any_instance_key_defined = !key.map {|prop| attribute_get(prop.name) }.reject {|elem| elem.nil? }.empty + # @composite_key = any_instance_key_defined ? composite_class.new : nil + # end + # @composite_key + @composite_key = @@composite_class.new + @composite_key.owner = self + + @composite_key + end + + add_set_accessor(:composite_key, composite_class) do |value| + @composite_key = value + @composite_key.owner = self + end + end + + def composite_class + @@composite_class ||= make_composite_class + end + + def make_composite_class + model = self + Class.new do + include java.io.Serializable + extend Hibernate::DynamicJava + extend PropertyTransformer + + attr_accessor :owner + + model.key.each do |prop_key| + self.add_java_property(prop_key, :simple_key? => false) + end + self.make_java_class(javax.persistence.Embeddable => {}) + end + end + end + module PropertyTransformer + @@logger = Slf4r::LoggerFacade.new(Hibernate::Model) - column_name = prop.field - annotation = {} + def add_java_property(prop, options = {:simple_key? => true}) + @@logger.info("#{prop.model.name} gets property added #{prop.name}") + name = prop.name + property_type = prop.class + return name if (property_type == DataMapper::Property::Discriminator) + make_accessors(name, property_type, build_annotation(prop, options)) + nil + end + + def build_annotation(prop, options) + annotation = {} + + if options[:simple_key?] if prop.serial? annotation[javax.persistence.Id] = {} annotation[javax.persistence.GeneratedValue] = {} - elsif prop.key? + elsif prop.key? annotation[javax.persistence.Id] = {} end - - annotation[javax.persistence.Column] = { - "unique" => prop.unique?, - "name" => prop.field - } - - unless prop.index.nil? - if (prop.index == true) - annotation[org.hibernate.annotations.Index] - elsif (prop.index.class == Symbol) - annotation[org.hibernate.annotations.Index] = {"name" => prop.index.to_s} - else - # TODO arrays !! - #annotation[org.hibernate.annotations.Index] = {"name" => []} - #prop.index.each do|index| - # annotation[org.hibernate.annotations.Index]["name"] << index.to_s - #end - end - end - if prop.required? - annotation[javax.persistence.Column]["nullable"] = !prop.required? - end - if (prop.respond_to?(:length) && !prop.length.nil?) - annotation[javax.persistence.Column]["length"] = java.lang.Integer.new(prop.length) - end - if (prop.respond_to?(:scale) && !prop.scale.nil?) - annotation[javax.persistence.Column]["scale"] = java.lang.Integer.new(prop.scale) - end - if (prop.respond_to?(:precision) && !prop.precision.nil?) - annotation[javax.persistence.Column]["precision"] = java.lang.Integer.new(prop.precision) - end - - get_name = "get#{name.to_s.capitalize}" - set_name = "set#{name.to_s.capitalize}" - - # TODO Time, Discriminator, EmbededValue - # to consider: in my opinion those methods should set from/get to java objects... - if (type == DataMapper::Property::Date) - class_eval <<-EOT - def #{set_name.intern}(d) - attribute_set(:#{name} , d.nil? ? nil : Date.civil(d.year + 1900, d.month + 1, d.date)) - end - EOT - class_eval <<-EOT - def #{get_name.intern} - d = attribute_get(:#{name} ) - org.joda.time.DateTime.new(d.year, d.month, d.day, 0, 0, 0, 0).to_date if d - end - EOT - elsif (type == DataMapper::Property::DateTime) - class_eval <<-EOT - def #{set_name.intern}(d) - attribute_set(:#{name} , d.nil? ? nil : DateTime.civil(d.year + 1900, d.month + 1, d.date, d.hours, d.minutes, d.seconds)) - end - EOT - class_eval <<-EOT - def #{get_name.intern} - d = attribute_get(:#{name}) - org.joda.time.DateTime.new(d.year, d.month, d.day, d.hour, d.min, d.sec, 0).to_date if d - end - EOT - elsif (type.to_s == BigDecimal || type == DataMapper::Property::Decimal) - class_eval <<-EOT - def #{set_name.intern}(d) - attribute_set(:#{name} , d.nil? ? nil :#{type}.new(d.to_s)) - end - EOT - class_eval <<-EOT - def #{get_name.intern} - d = attribute_get(:#{name}) - java.math.BigDecimal.new(d.to_i) if d - end - EOT + end + + annotation[javax.persistence.Column] = { + "unique" => prop.unique?, + "name" => prop.field + } + + unless prop.index.nil? + if (prop.index == true) + annotation[org.hibernate.annotations.Index] + elsif (prop.index.class == Symbol) + annotation[org.hibernate.annotations.Index] = {"name" => prop.index.to_s} else - class_eval <<-EOT - def #{set_name.intern}(d) - attribute_set(:#{name} , d) - end - EOT - class_eval <<-EOT - def #{get_name.intern} - d = attribute_get(:#{name}) - d - end - EOT + # TODO arrays !! + #annotation[org.hibernate.annotations.Index] = {"name" => []} + #prop.index.each do|index| + # annotation[org.hibernate.annotations.Index]["name"] << index.to_s + #end end - - mapped_type = to_java_type(type).java_class - add_method_signature get_name, [mapped_type] - add_method_annotation get_name, annotation - add_method_signature set_name, [JVoid, mapped_type] - nil end + if prop.required? + annotation[javax.persistence.Column]["nullable"] = !prop.required? + end + if (prop.respond_to?(:length) && !prop.length.nil?) + annotation[javax.persistence.Column]["length"] = java.lang.Integer.new(prop.length) + end + if (prop.respond_to?(:scale) && !prop.scale.nil?) + annotation[javax.persistence.Column]["scale"] = java.lang.Integer.new(prop.scale) + end + if (prop.respond_to?(:precision) && !prop.precision.nil?) + annotation[javax.persistence.Column]["precision"] = java.lang.Integer.new(prop.precision) + end + annotation + end + + def make_accessors(name, property_type, accessor_annotations = {}) + get_accessor_block = AccessorStrategy.get_accessor_block(name, property_type) + add_get_accessor(name, property_type, accessor_annotations, &get_accessor_block) + + set_accessor_block = AccessorStrategy.set_accessor_block(name, property_type) + add_set_accessor(name, property_type, accessor_annotations, &set_accessor_block) + end end + + module AccessorStrategy + + STRATEGY = { + ::Date => { + :set => Proc.new {|d| Date.civil(d.year + 1900, d.month + 1, d.date) }, + :get => Proc.new {|d| org.joda.time.DateTime.new(d.year, d.month, d.day, 0, 0, 0, 0).to_date } + }, + ::DateTime => { + :set => Proc.new {|d| DateTime.civil(d.year + 1900, d.month + 1, d.date, d.hours, d.minutes, d.seconds) }, + :get => Proc.new {|d| org.joda.time.DateTime.new(d.year, d.month, d.day, d.hour, d.min, d.sec, 0).to_date } + }, + + ::BigDecimal => { + :set => Proc.new {|d| BigDecimal.new(d.to_s) }, + :get => Proc.new {|d| java.math.BigDecimal.new(d.to_s) } + }, + } + + def self.get_accessor_block(property_name, property_type) + Proc.new do + owner_instance = self.respond_to?(:owner) ? self.owner : self + AccessorStrategy.strategy_of(:get, property_name, property_type, owner_instance.attribute_get(property_name.to_sym)) + end + end + + def self.set_accessor_block(property_name, property_type) + Proc.new do |value| + owner_instance = self.respond_to?(:owner) ? self.owner : self + owner_instance.attribute_set(property_name.to_sym, AccessorStrategy.strategy_of(:set, property_name, property_type, value)) + end + end + + def self.strategy_of(strategy_type, property_name, property_type, value) + value = STRATEGY[property_type][strategy_type].call(value) if !STRATEGY[property_type].nil? && !value.nil? + value + end + end end diff --git a/lib/dm-hibernate-adapter/utils/constants.rb b/lib/dm-hibernate-adapter/utils/constants.rb index 71359a0..d6dcdf0 100644 --- a/lib/dm-hibernate-adapter/utils/constants.rb +++ b/lib/dm-hibernate-adapter/utils/constants.rb @@ -16,3 +16,15 @@ JClass = java.lang.Class JVoid = java.lang.Void::TYPE +TYPES = { + ::String => java.lang.String, + ::Integer => java.lang.Integer, + ::Float => java.lang.Double, + ::BigDecimal => java.math.BigDecimal, + ::Date => java.util.Date, + ::DateTime => java.util.Date, + ::Time => java.util.Date, + ::TrueClass => java.lang.Boolean, + ::FalseClass => java.lang.Boolean, +} + diff --git a/pom.xml b/pom.xml index c10cfaf..a16f6ec 100644 --- a/pom.xml +++ b/pom.xml @@ -187,17 +187,29 @@ ${project.artifactId}_ext + + de.saumya.mojo + rspec-maven-plugin + ${jruby.plugins.version} + + + + test + + + + de.saumya.mojo gem-maven-plugin ${jruby.plugins.version} true - lib/dm-hibernate-adapter_ext.jar,lib/dm-hibernate-adapter.rb,lib/dm-hibernate-adapter,lib/dm-hibernate-adapter/data_mapper,lib/dm-hibernate-adapter/data_mapper/adapters,lib/dm-hibernate-adapter/data_mapper/adapters/hibernate_adapter.rb,lib/dm-hibernate-adapter/hibernate,lib/dm-hibernate-adapter/hibernate/dialects.rb,lib/dm-hibernate-adapter/hibernate/hibernate.rb,lib/dm-hibernate-adapter/hibernate/model.rb,lib/dm-hibernate-adapter/hibernate/property_shim.rb,lib/dm-hibernate-adapter/hibernate/transaction.rb,lib/dm-hibernate-adapter/utils,lib/dm-hibernate-adapter/utils/constants.rb,lib/dm-hibernate-adapter/utils/logger.rb,spec/adapter_spec.rb,spec/rcov.opts,spec/spec.opts,spec/spec_helper.rb + lib/dm-hibernate-adapter_ext.jar,lib/dm-hibernate-adapter.rb,lib/dm-hibernate-adapter,lib/dm-hibernate-adapter/hibernate,lib/dm-hibernate-adapter/hibernate/property_shim.rb,lib/dm-hibernate-adapter/hibernate/hibernate.rb,lib/dm-hibernate-adapter/hibernate/model.rb,lib/dm-hibernate-adapter/hibernate/dynamic_java.rb,lib/dm-hibernate-adapter/hibernate/model.rb.~8afce901e5b51d010121b5545cffcd4a1e913bdb~,lib/dm-hibernate-adapter/hibernate/dialects.rb,lib/dm-hibernate-adapter/hibernate/transaction.rb,lib/dm-hibernate-adapter/spec,lib/dm-hibernate-adapter/spec/setup.rb,lib/dm-hibernate-adapter/data_mapper,lib/dm-hibernate-adapter/data_mapper/adapters,lib/dm-hibernate-adapter/data_mapper/adapters/hibernate_adapter.rb,lib/dm-hibernate-adapter/data_mapper/dm-core.rb,lib/dm-hibernate-adapter/utils,lib/dm-hibernate-adapter/utils/constants.rb,lib/dm-hibernate-adapter/utils/logger.rb,spec/spec_helper.rb,spec/rcov.opts,spec/cpk_spec.rb,spec/target,spec/target/rubygems-rake-maven-plugin,spec/spec.opts,spec/adapter_spec.rb dm-hibernate-adapter.gemspec java 1.3.1]]> - spec/adapter_spec.rb + spec/cpk_spec.rb,spec/adapter_spec.rb @@ -213,62 +225,6 @@ - - de.saumya.mojo - rspec-maven-plugin - ${jruby.plugins.version} - - - - test - - - - - - - transient - - - - de.saumya.mojo - rspec-maven-plugin - - spec/transient - - - - - - - adapter - - - - de.saumya.mojo - rspec-maven-plugin - - spec/abstract_adapter - - - - - - - dm - - - - de.saumya.mojo - rspec-maven-plugin - - spec/dm_core - - - - - - diff --git a/spec/adapter_spec.rb b/spec/adapter_spec.rb index 3277274..b39984c 100644 --- a/spec/adapter_spec.rb +++ b/spec/adapter_spec.rb @@ -19,6 +19,6 @@ require 'dm-core/spec/shared/adapter_spec' describe DataMapper::Adapters::HibernateAdapter do - it_should_behave_like 'An Adapter' + it_should_behave_like 'An Adapter' end diff --git a/spec/cpk_spec.rb b/spec/cpk_spec.rb new file mode 100644 index 0000000..54f4cad --- /dev/null +++ b/spec/cpk_spec.rb @@ -0,0 +1,8 @@ +require 'spec_helper' + +dm_core_spec_path = $:.select {|path| path =~ /dm-core/ }.first.gsub(/lib$/, 'spec') + +# load from dm-core specs still not working +# spec_helper not work with multiples specs +# cpk implementation are broken in 'should be able to access the child' +# require "#{dm_core_spec_path}/public/associations/many_to_one_with_boolean_cpk_spec" diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c90bebe..537c323 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -18,7 +18,13 @@ require 'dm-core/spec/lib/pending_helpers' require 'dm-core/spec/lib/adapter_helpers' -require 'dm-core/spec/lib/collection_helpers' +require 'dm-core/spec/lib/spec_helper' + +require 'dm-core/spec/setup' + +ENV['ADAPTER'] ||= 'hibernate' +ENV['ADAPTER_SUPPORTS'] = 'all' +ENV['RELOAD'] = 'true' DB_CONFIGS = { :H2_EMB => { :adapter => "hibernate", :dialect => "H2", :username => "sa", :url => "jdbc:h2:target/jibernate" }, @@ -32,12 +38,20 @@ Spec::Runner.configure do |config| config.include DataMapper::Spec::PendingHelpers - # config.include DataMapper::Spec::AdaptersHelpers - # config.include DataMapper::Spec::CollectionHelpers + config.extend( DataMapper::Spec::Adapters::Helpers) + config.include(DataMapper::Spec::Helpers) config.before :all do - @adapter = DataMapper.setup(:default, DB_CONFIGS[(ENV['DIALECT'] || :H2_EMB).to_sym]) - DataMapper.auto_migrate! + #TODO: loading multiples specs not work + # we need to avoid setup adapter here + @adapter = DataMapper.setup(:default, DB_CONFIGS[(ENV['DIALECT'] || :H2_EMB).to_sym].merge(:reload => 'true')) + DataMapper.auto_migrate! + end + + config.after :all do + #DataMapper::Spec.cleanup_models + # DataMapper::Spec.remove_ivars(self, instance_variables.reject { |ivar| ivar[0, 2] == '@_' }) + # DataMapper::Spec.remove_ivars(Spec::Matchers.last_matcher, %w[ @expected ]) end end