Class: ActiveRecord::Base
- Inherits:
-
Object
- Object
- ActiveRecord::Base
- Defined in:
- lib/connection_adapters/ibm_db2_adapter.rb,
lib/base.rb,
lib/validations.rb,
lib/associations.rb
Overview
These extensions in ActiveRecord::Base are for iSeries support
Constant Summary collapse
- @@class_name_hash =
Stores a dictionary of table_name => class_name mappings to facilitate reverse lookup with acceptable performance
{}
- @@logger =
Logger.new(STDERR)
Class Method Summary collapse
- .associations ⇒ Object
- .base_class_name ⇒ Object
- .base_instantiate ⇒ Object
-
.base_set_table_name ⇒ Object
Save table name keys as all UPPERCASE to simplify lookup.
- .base_table_exists? ⇒ Object
-
.class_name(table_name = table_name) ⇒ Object
:nodoc:.
- .clear_cached_associations ⇒ Object
- .clear_cached_validations ⇒ Object
-
.find_one(id, options) ⇒ Object
Finds a record based on the
id
value. -
.find_some(ids, options) ⇒ Object
Finds a record based on a list of
ids
. -
.foreign_constraints ⇒ Object
foreign_constraints are those constraints that are defined on other tables, but reference this table (i.e. FKs into this table).
-
.generate_associations ⇒ Object
The @associations instvar is used as an indicator as to whether the associations have already been generated or not.
-
.generate_orm ⇒ Object
This method will generate associations and validations for all defined subclasses of ActiveRecord::Base.
-
.generate_validations ⇒ Object
The @validations instvar is used as an indicator as to whether the validations have already been generated or not.
- .ibm_find_one ⇒ Object
- .ibm_find_some ⇒ Object
- .instantiate(record) ⇒ Object
- .logger ⇒ Object
-
.primary_key ⇒ Object
Overrides primary_key in ActiveRecord::Base.
- .regenerate_cached_schema ⇒ Object
- .set_table_name(value = nil, &block) ⇒ Object
-
.table_constraints ⇒ Object
table_constraints are those constraints defined on this table.
- .table_exists?(class_name = nil) ⇒ Boolean
Instance Method Summary collapse
- #base_initialize ⇒ Object
- #constraints ⇒ Object
-
#initialize(attributes = nil) ⇒ Base
constructor
A new instance of Base.
Constructor Details
#initialize(attributes = nil) ⇒ Base
Returns a new instance of Base.
169 170 171 172 173 |
# File 'lib/base.rb', line 169 def initialize(attributes = nil) base_initialize self.class.generate_associations self.class.generate_validations end |
Class Method Details
.associations ⇒ Object
91 92 93 |
# File 'lib/base.rb', line 91 def associations @associations ? @associations : {} end |
.base_class_name ⇒ Object
62 |
# File 'lib/base.rb', line 62 alias :base_class_name :class_name |
.base_instantiate ⇒ Object
73 |
# File 'lib/base.rb', line 73 alias :base_instantiate :instantiate |
.base_set_table_name ⇒ Object
Save table name keys as all UPPERCASE to simplify lookup
35 |
# File 'lib/base.rb', line 35 alias :base_set_table_name :set_table_name |
.base_table_exists? ⇒ Object
95 |
# File 'lib/base.rb', line 95 alias :base_table_exists? :table_exists? |
.class_name(table_name = table_name) ⇒ Object
:nodoc:
63 64 65 66 67 68 69 70 |
# File 'lib/base.rb', line 63 def class_name(table_name = table_name) # :nodoc: #Attempt to retrieve class name from cache before attempting to generate it name = @@class_name_hash[table_name.upcase] if !name return base_class_name(table_name) end name end |
.clear_cached_associations ⇒ Object
19 20 21 22 |
# File 'lib/associations.rb', line 19 def clear_cached_associations @associations = nil logger.info("DRYSQL >> Cleared cached schema for: #{self}") end |
.clear_cached_validations ⇒ Object
102 103 104 105 |
# File 'lib/validations.rb', line 102 def clear_cached_validations @validations = nil logger.info("DRYSQL >> Cleared cached validations for: #{self}") end |
.find_one(id, options) ⇒ Object
Finds a record based on the id
value. The column associated with the primary key can be of a numeric type however DB2 doesn’t allow quoted numeric values (e.g WHERE id = ‘10’). Therefore this overrides the default method to check for the column type, and leaves the value in the query unquoted in cases of numeric columns
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/connection_adapters/ibm_db2_adapter.rb', line 19 def find_one(id, ) # If the adapter in use is the IBM DB2 Adapter if connection.kind_of?(ConnectionAdapters::IBM_DB2Adapter) if !connection.iseries then return ibm_find_one(id, ) end conditions = " AND (#{sanitize_sql([:conditions])})" if [:conditions] # Retrieves the sql type of the column associated to the primary key cstmt = connection.execute("SELECT coltype FROM SYSCOLUMNS WHERE \ table_name ='#{table_name.upcase}' AND column_name ='#{primary_key.upcase}'") DB2::fetch_row(cstmt) primary_key_type = DB2::result(cstmt, 0) # Frees the results set associated with the statement DB2::free_result(cstmt) # If the column is a numeric type if primary_key_type =~ /int|double|real|decimal|numeric/i # Assign the unquoted id value to san_id san_id = sanitize_numeric(id) else # The column is not numeric, so +sanitize+ it san_id = sanitize(id) end # Adds the options based on the san_id value .update :conditions => "#{table_name}.#{primary_key} = #{san_id}#{conditions}" if result = find_initial() result else raise RecordNotFound, "Couldn't find #{name} with ID=#{id}#{conditions}" end else # You're not using the IBM DB2 Adapter therefore the default method is recalled default_find_one(id, ) end end |
.find_some(ids, options) ⇒ Object
Finds a record based on a list of ids
. The primary key column type could be numeric, and DB2 doesn’t allow quoted numeric values. Therefore this version of the method checks for the data type and if it’s a numeric type the value in the query is unquoted. If the column datatype is not numeric, the value is properly quoted/sanitized
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/connection_adapters/ibm_db2_adapter.rb', line 63 def find_some(ids, ) # If the adapter in use is the IBM DB2 Adapter if connection.kind_of?(ConnectionAdapters::IBM_DB2Adapter) if !connection.iseries then return ibm_find_some(ids, ) end conditions = " AND (#{sanitize_sql([:conditions])})" if [:conditions] # Retrieves the sql type of the column associated to the primary key cstmt = connection.execute("SELECT typename FROM SYSCOLUMNS WHERE \ table_name ='#{table_name.upcase}' AND column_name ='#{primary_key.upcase}'") DB2::fetch_row(cstmt) primary_key_type = DB2::result(cstmt, 0) # Frees the results set associated with the statement DB2::free_result(cstmt) # If the column is a numeric type if primary_key_type =~ /int|double|real|decimal|numeric/i # Generates a comma separated list of ids ids_list = ids.map {|id| sanitize_numeric(id) }.join(',') else # Generates a comma separated list of quoted/sanitized ids ids_list = ids.map { |id| sanitize(id) }.join(',') end # Adds the options to the query, based on the generated +ids_list+ .update :conditions => "#{table_name}.#{primary_key} IN (#{ids_list})#{conditions}" result = find_every() if result.size == ids.size result else raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}" end else # You're not using the IBM DB2 Adapter therefore the default method is recalled default_find_some(ids, ) end end |
.foreign_constraints ⇒ Object
foreign_constraints are those constraints that are defined on other tables, but reference this table (i.e. FKs into this table)
87 88 89 |
# File 'lib/base.rb', line 87 def foreign_constraints constraints.select {|constraint| constraint.is_foreign_constraint?(table_name)} end |
.generate_associations ⇒ Object
The @associations instvar is used as an indicator as to whether the associations have already been generated or not. The first time an instance of a DrySQL-enabled subclass of ActiveRecord::Base is created, the associations are generated for the class.
10 11 12 13 14 15 16 17 |
# File 'lib/associations.rb', line 10 def generate_associations unless @associations generate_belongs_to_associations generate_has_many_associations generate_through_associations @associations = reflections end end |
.generate_orm ⇒ Object
This method will generate associations and validations for all defined subclasses of ActiveRecord::Base.
If any of the constraints processed refer to undefined model classes, these classes will be defined as long as they conform to the ActiveRecord naming conventions.
This method swallows exceptions that occur during the generation of associations and validations, but logs these exceptions in detail to STDERR.
128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/base.rb', line 128 def generate_orm models = @@subclasses.to_a.flatten.uniq models.each do |model| begin if model != Base model.generate_associations model.generate_validations end rescue Exception logger.error("DRYSQL ERROR >> Could not generate ORM for #{model}") logger.error($!.backtrace.join("\n")) end end end |
.generate_validations ⇒ Object
The @validations instvar is used as an indicator as to whether the validations have already been generated or not. The first time an instance of a DrySQL-enabled subclass of ActiveRecord::Base is created, the validations are generated for the class.
**NOTE: Some of this code and/or the ideas behind some of this code was borrowed from Simon Harris
and Red Hill Consulting's Schema Validations plug-in. Thanks Simon!
See http://www.redhillconsulting.com.au/rails_plugins.html#schema_validations
for info about this plug-in
Additional features added to Schema_Validations: 1) proper error message generated for boolean inclusion failure (:inclusion)
2) all validations are generated with allow_nil=>true because…
3) all validations are superceded with the new validates_nullability_of,
which generates an error for any field whose associated column has a NOT NULL constraint upon it,
for whom no default value is specified, and for whom the value is not generated by the DB
4) do not generate validates_uniqueness_of by default for unique fields. This is a performance hit, and
in my opinion completely defeats the purpose of an application-side data validation because it
requires a query of the DB. Why not just let the DB do the work and handle the duplicate key exception?
5) Do not generate validates_presence_of for non-nullable fields. This will throw an exception for fields that
contain an empty string, even though that may in fact be a valid value according to the table constraints.
This approach also fails to consider that a default value might be specified for a non-nullable field in which case
we do not need to block the null field from being saved to the DB
6) Perform validation auto-generation on all columns rather than just those returned by Base.content_columns
I believe there is value in validating our PK, FK, and counter columns
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/validations.rb', line 74 def generate_validations unless @validations columns.each do |column| if !(column.null || column.default_specified? || column.generated?) self.validates_nullability_of column.name logger.info("DRYSQL >> GENERATED VALIDATION: #{self.name} validates_nullability_of #{column.name}") end if column.type == :integer self.validates_numericality_of column.name, :allow_nil => true, :only_integer => true logger.info("DRYSQL >> GENERATED VALIDATION: #{self.name} validates_numericality_of #{column.name}, :allow_nil=>true, :only_integer=>true") elsif column.number? self.validates_numericality_of column.name, :allow_nil => true logger.info("DRYSQL >> GENERATED VALIDATION: #{self.name} validates_numericality_of #{column.name}, :allow_nil=>true") elsif column.text? && column.limit self.validates_length_of column.name, :allow_nil => true, :maximum => column.limit logger.info("DRYSQL >> GENERATED VALIDATION: #{self.name} validates_length_of #{column.name}, :allow_nil=>true, :maximum=>#{column.limit}") elsif column.type == :boolean self.validates_inclusion_of column.name, :in => [true, false], :allow_nil =>true, :message => ActiveRecord::Errors.[:inclusion] logger.info("DRYSQL >> GENERATED VALIDATION: #{self.name} validates_inclusion_of #{column.name}, :in=>[true, false], \ :allow_nil=>true, :message=>ActiveRecord::Errors.default_error_messages[:inclusion]") end end end @validations=true end |
.ibm_find_one ⇒ Object
14 |
# File 'lib/connection_adapters/ibm_db2_adapter.rb', line 14 alias_method :ibm_find_one, :find_one |
.ibm_find_some ⇒ Object
58 |
# File 'lib/connection_adapters/ibm_db2_adapter.rb', line 58 alias_method :ibm_find_some, :find_some |
.instantiate(record) ⇒ Object
74 75 76 77 78 |
# File 'lib/base.rb', line 74 def instantiate(record) generate_associations generate_validations base_instantiate(record) end |
.logger ⇒ Object
30 31 32 |
# File 'lib/base.rb', line 30 def logger @@logger end |
.primary_key ⇒ Object
Overrides primary_key in ActiveRecord::Base
This implementation will need to be adjusted should composite primary keys be supported in the future
set_primary_key generates an accessor method on the caller that returns the string value –> this generated accessor will be invoked on future calls to primary_key
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/base.rb', line 49 def primary_key primary = table_constraints.detect {|constraint| constraint.primary_key?} if primary.nil? logger.error("DRYSQL >> No primary key defined for table #{table_name}") else primary_name = primary.column_name.to_a[0] set_primary_key(primary_name) logger.info("DRYSQL >> Identified PRIMARY KEY for #{self}: #{primary_name}") end primary_name end |
.regenerate_cached_schema ⇒ Object
107 108 109 110 111 112 113 114 115 116 |
# File 'lib/base.rb', line 107 def regenerate_cached_schema clear_cached_associations clear_cached_validations @constraints = nil @columns = nil primary = table_constraints.detect {|constraint| constraint.primary_key?} primary_name = primary.column_name set_primary_key(primary_name) logger.info("DRYSQL >> Reset PRIMARY KEY for #{self}: #{primary_name}") end |
.set_table_name(value = nil, &block) ⇒ Object
36 37 38 39 |
# File 'lib/base.rb', line 36 def set_table_name(value = nil, &block) base_set_table_name(value) @@class_name_hash["#{table_name.upcase}"] = self.name end |
.table_constraints ⇒ Object
table_constraints are those constraints defined on this table
81 82 83 |
# File 'lib/base.rb', line 81 def table_constraints constraints.reject {|constraint| constraint.is_foreign_constraint?(table_name)} end |
.table_exists?(class_name = nil) ⇒ Boolean
96 97 98 99 100 101 102 103 104 105 |
# File 'lib/base.rb', line 96 def table_exists?(class_name=nil) if class_name.nil? then base_table_exists? else if connection.respond_to?(:tables) connection.tables.include? construct_table_name_from_class_name(class_name) else false end end end |
Instance Method Details
#base_initialize ⇒ Object
168 |
# File 'lib/base.rb', line 168 alias :base_initialize :initialize |
#constraints ⇒ Object
175 176 177 |
# File 'lib/base.rb', line 175 def constraints self.class.table_constraints end |