Class: ActiveRecord::Base
- Inherits:
-
Object
- Object
- ActiveRecord::Base
- Defined in:
- lib/base.rb,
lib/validations.rb,
lib/associations.rb
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
- .constraints ⇒ Object
-
.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.
- .instantiate(record) ⇒ Object
- .logger ⇒ Object
-
.primary_key ⇒ Object
Overrides primary_key in ActiveRecord::Base.
- .regenerate_cached_schema ⇒ Object
- .set_drysql_log_output(file) ⇒ 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.
173 174 175 176 177 |
# File 'lib/base.rb', line 173 def initialize(attributes = nil) base_initialize(attributes) self.class.generate_associations self.class.generate_validations end |
Class Method Details
.associations ⇒ Object
96 97 98 |
# File 'lib/base.rb', line 96 def associations @associations ? @associations : {} end |
.base_class_name ⇒ Object
67 |
# File 'lib/base.rb', line 67 alias :base_class_name :class_name |
.base_instantiate ⇒ Object
78 |
# File 'lib/base.rb', line 78 alias :base_instantiate :instantiate |
.base_set_table_name ⇒ Object
Save table name keys as all UPPERCASE to simplify lookup
40 |
# File 'lib/base.rb', line 40 alias :base_set_table_name :set_table_name |
.base_table_exists? ⇒ Object
100 |
# File 'lib/base.rb', line 100 alias :base_table_exists? :table_exists? |
.class_name(table_name = table_name) ⇒ Object
:nodoc:
68 69 70 71 72 73 74 75 |
# File 'lib/base.rb', line 68 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
107 108 109 110 |
# File 'lib/validations.rb', line 107 def clear_cached_validations @validations = nil logger.info("DRYSQL >> Cleared cached validations for: #{self}") end |
.constraints ⇒ Object
149 150 151 152 153 154 |
# File 'lib/base.rb', line 149 def constraints unless @constraints @constraints = connection.constraints(table_name, "#{name} Constraints") end @constraints 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)
92 93 94 |
# File 'lib/base.rb', line 92 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.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/base.rb', line 133 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 101 102 103 104 105 |
# File 'lib/validations.rb', line 74 def generate_validations unless @validations columns.each do |column| # Columns are considered nullable if any of the following are true: # # 1) Column does not have a NOT NULL constraint # 2) Column has a default value specified # 3) Column is generated # (DrySQL makes the assumption that if a column is a PK and of type integer, then it is generated) if !(column.is_nullable? || (column.name.upcase == self.primary_key.upcase && column.type == :integer)) 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 |
.instantiate(record) ⇒ Object
79 80 81 82 83 |
# File 'lib/base.rb', line 79 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
54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/base.rb', line 54 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_names[0] set_primary_key(primary_name) logger.info("DRYSQL >> Identified PRIMARY KEY for #{self}: #{primary_name}") end primary_name end |
.regenerate_cached_schema ⇒ Object
112 113 114 115 116 117 118 119 120 121 |
# File 'lib/base.rb', line 112 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_names[0] set_primary_key(primary_name) logger.info("DRYSQL >> Reset PRIMARY KEY for #{self}: #{primary_name}") end |
.set_drysql_log_output(file) ⇒ Object
34 35 36 37 |
# File 'lib/base.rb', line 34 def set_drysql_log_output(file) @@logger = Logger.new(file) @@logger.level = Logger::INFO end |
.set_table_name(value = nil, &block) ⇒ Object
41 42 43 44 |
# File 'lib/base.rb', line 41 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
86 87 88 |
# File 'lib/base.rb', line 86 def table_constraints constraints.reject {|constraint| constraint.is_foreign_constraint?(table_name)} end |
.table_exists?(class_name = nil) ⇒ Boolean
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/base.rb', line 101 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
172 |
# File 'lib/base.rb', line 172 alias :base_initialize :initialize |
#constraints ⇒ Object
179 180 181 |
# File 'lib/base.rb', line 179 def constraints self.class.constraints end |