Module: Jinx::Introspector
Overview
Meta-data mix-in to infer attribute meta-data from Java properties.
Instance Attribute Summary
Attributes included from Propertied
Class Method Summary collapse
-
.ensure_introspected(klass) ⇒ Object
Introspects the given class, if necessary.
Instance Method Summary collapse
-
#add_attribute_value_initializer ⇒ Object
Augments the introspected class
new
method as follows: * Adds an optional attribute=>value constructor parameter. -
#add_java_property(pd) ⇒ Property
private
Makes a standard attribute for the given property descriptor.
-
#alias_property_accessors(property) ⇒ Object
private
Aliases the given Ruby property reader and writer to its underlying Java property reader and writer, resp.
-
#create_java_property(pd) ⇒ Property
private
The new property.
-
#define_java_property(pd) ⇒ Object
private
Defines the Java property attribute and standard attribute methods, e.g.
-
#delegate_to_property(aliaz, property) ⇒ Object
private
Defines methods aliaz and aliaz= which calls the standard attribute and attribute= accessor methods, resp.
-
#introspect ⇒ Object
Defines the Java attribute access methods, e.g.
-
#wrap_java_date_property(property) ⇒ Object
private
Adds a Java-Ruby Date filter to the given Date property Ruby access methods.
-
#wrap_java_property(property) ⇒ Object
private
Adds a filter to the given property access methods if it is a String or Date.
-
#wrap_java_string_property(property) ⇒ Object
private
Adds a number -> string filter to the given String property Ruby access methods.
Methods included from Propertied
#add_alternate_key_attribute, #add_attribute, #add_attribute_aliases, #add_attribute_default, #add_attribute_defaults, #add_mandatory_attribute, #add_mandatory_attributes, #add_primary_key_attribute, #add_property, #add_restriction, #add_secondary_key_attribute, #alias_attribute, #alias_standard_attribute_hash, #all_key_attributes, #alternate_key_attributes, #append_ancestor_enum, #attribute_filter, #collect_mandatory_attributes, #collection_attribute?, #compose_property, #create_nonjava_property, #default_mandatory_local_attributes, #dependent_attributes, #dependent_properties, #domain_attribute?, #domain_attributes, #domain_properties, #each_property, #independent_attributes, #init_property_classifiers, #introspected?, #java_attributes, #mandatory_attributes, #mandatory_owner_attribute, #most_specific_domain_attribute, #nondomain_attribute?, #nondomain_attributes, #nondomain_java_attributes, #nonowner_attributes, #offset_attribute, #primary_key_attributes, #properties, #property, #property_defined?, #property_hash, #property_path, #qualify_attribute, #qualify_property, #register_property_alias, #remove_attribute, #secondary_key_attributes, #secondary_key_non_owner_domain_attributes, #set_alternate_key_attributes, #set_attribute_type, #set_primary_key_attributes, #set_secondary_key_attributes, #standard_attribute, #unidirectional_dependent_attributes
Class Method Details
.ensure_introspected(klass) ⇒ Object
Introspects the given class, if necessary. Some member of the class hierarchy must first be introspected.
18 19 20 21 22 23 24 25 26 |
# File 'lib/jinx/metadata/introspector.rb', line 18 def self.ensure_introspected(klass) return if klass < Resource and klass.introspected? sc = klass.superclass ensure_introspected(sc) unless sc == Java::java.lang.Object logger.debug { "Introspecting the fetched object class #{klass}..." } # Resolving the class name in the context of the domain module # introspects the class. sc.domain_module.const_get(klass.name.demodulize) end |
Instance Method Details
#add_attribute_value_initializer ⇒ Object
Augments the introspected class new
method as follows:
-
Adds an optional attribute=>value constructor parameter.
-
Calls the Resource#post_initialize method after initialization.
31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/jinx/metadata/introspector.rb', line 31 def add_attribute_value_initializer class << self def new(opts=nil) obj = super() obj.post_initialize obj.merge_attributes(opts) if opts obj end end logger.debug { "#{self} is extended with an optional {attribute=>value} constructor parameter." } end |
#add_java_property(pd) ⇒ Property (private)
Makes a standard attribute for the given property descriptor. Adds a camelized Java-like alias to the standard attribute.
165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/jinx/metadata/introspector.rb', line 165 def add_java_property(pd) # make the attribute metadata prop = create_java_property(pd) add_property(prop) # the property name is an alias for the standard attribute pa = prop.attribute # the Java property name as an attribute symbol ja = pd.name.to_sym delegate_to_property(ja, prop) unless prop.reader == ja prop end |
#alias_property_accessors(property) ⇒ Object (private)
Aliases the given Ruby property reader and writer to its underlying Java property reader and writer, resp.
152 153 154 155 156 157 158 |
# File 'lib/jinx/metadata/introspector.rb', line 152 def alias_property_accessors(property) # the Java reader and writer accessor method symbols jra, jwa = property.java_accessors # strip the Java reader and writer is/get/set prefix and make a symbol alias_method(property.reader, jra) alias_method(property.writer, jwa) end |
#create_java_property(pd) ⇒ Property (private)
Returns the new property.
179 180 181 |
# File 'lib/jinx/metadata/introspector.rb', line 179 def create_java_property(pd) JavaProperty.new(pd, self) end |
#define_java_property(pd) ⇒ Object (private)
Defines the Java property attribute and standard attribute methods, e.g. study_protocol
and studyProtocol
. A boolean attribute is provisioned with an additional reader alias, e.g. available?
for is_available
.
A standard attribute which differs from the property attribute delegates to the property attribute, e.g. study_protocol
delegates to studyProtocol
rather than aliasing setStudyProtocol
. Redefining these methods results in a call to the redefined method. This contrasts with a Ruby alias, where each attribute alias is bound to the respective attribute reader or writer.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/jinx/metadata/introspector.rb', line 87 def define_java_property(pd) if transient?(pd) then logger.debug { "Ignoring #{name.demodulize} transient attribute #{pd.name}." } return end # the standard underscore lower-case attributes prop = add_java_property(pd) # delegate the standard attribute accessors to the attribute accessors alias_property_accessors(prop) # add special wrappers wrap_java_property(prop) # create Ruby alias for boolean, e.g. alias :empty? for :empty if pd.property_type.name[/\w+$/].downcase == 'boolean' then # Strip the leading is_, if any, before appending a question mark. aliaz = prop.to_s[/^(is_)?(\w+)/, 2] << '?' delegate_to_property(aliaz, prop) end end |
#delegate_to_property(aliaz, property) ⇒ Object (private)
Defines methods aliaz and aliaz= which calls the standard attribute and attribute= accessor methods, resp. Calling rather than aliasing the attribute accessor allows the aliaz accessor to reflect a change to the attribute accessor.
187 188 189 190 191 192 193 |
# File 'lib/jinx/metadata/introspector.rb', line 187 def delegate_to_property(aliaz, property) ra, wa = property.accessors if aliaz == ra then raise MetadataError.new("Cannot delegate #{self} #{aliaz} to itself.") end define_method(aliaz) { send(ra) } define_method("#{aliaz}=".to_sym) { |value| send(wa, value) } register_property_alias(aliaz, property.attribute) end |
#introspect ⇒ Object
Defines the Java attribute access methods, e.g. study_protocol
and studyProtocol
. A boolean attribute is provisioned with an additional reader alias, e.g. available?
for is_available
.
Each Java property attribute delegates to the Java attribute getter and setter. Each standard attribute delegates to the Java property attribute. Redefining these methods results in a call to the redefined method. This contrasts with a Ruby alias, where the alias remains bound to the original method body.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/jinx/metadata/introspector.rb', line 52 def introspect # Set up the attribute data structures; delegates to Propertied. init_property_classifiers logger.debug { "Introspecting #{qp} metadata..." } # check for method conflicts conflicts = instance_methods(false) & Resource.instance_methods(false) unless conflicts.empty? then logger.warn("#{self} methods conflict with #{Resource} methods: #{conflicts.qp}") end # If this is a Java class rather than interface, then define the Java property # attributes. if Class === self then # the Java attributes defined by this class with both a read and a write method pds = property_descriptors(false) # Define the standard Java attribute methods. pds.each { |pd| define_java_property(pd) } end logger.debug { "Introspection of #{qp} metadata complete." } self end |
#wrap_java_date_property(property) ⇒ Object (private)
Adds a Java-Ruby Date filter to the given Date property Ruby access methods. The reader returns a Ruby date. The writer sets a Java date.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/jinx/metadata/introspector.rb', line 130 def wrap_java_date_property(property) ra, wa = property.accessors jra, jwa = property.java_accessors # filter the attribute reader define_method(ra) do value = send(jra) Java::JavaUtil::Date === value ? value.to_ruby_date : value end # filter the attribute writer define_method(wa) do |value| value = Java::JavaUtil::Date.from_ruby_date(value) if ::Date === value send(jwa, value) end logger.debug { "Filtered #{qp} #{ra} and #{wa} methods with Java Date <-> Ruby Date converter." } end |
#wrap_java_property(property) ⇒ Object (private)
Adds a filter to the given property access methods if it is a String or Date.
107 108 109 110 111 112 113 114 |
# File 'lib/jinx/metadata/introspector.rb', line 107 def wrap_java_property(property) pd = property.property_descriptor if pd.property_type == Java::JavaLang::String.java_class then wrap_java_string_property(property) elsif pd.property_type == Java::JavaUtil::Date.java_class then wrap_java_date_property(property) end end |
#wrap_java_string_property(property) ⇒ Object (private)
Adds a number -> string filter to the given String property Ruby access methods.
117 118 119 120 121 122 123 124 125 126 |
# File 'lib/jinx/metadata/introspector.rb', line 117 def wrap_java_string_property(property) ra, wa = property.accessors jra, jwa = property.java_accessors # filter the attribute writer define_method(wa) do |value| stdval = Math.numeric?(value) ? value.to_s : value send(jwa, stdval) end logger.debug { "Filtered #{qp} #{wa} method with non-String -> String converter." } end |