Class: Idxfence::ModelParser
- Inherits:
-
Object
- Object
- Idxfence::ModelParser
- Defined in:
- lib/idxfence/model_parser.rb
Overview
Parses one Ruby source file with Ripper (structural boundaries --
class start/end -- come from the s-expression, never from do/end
counting; a narrower regex pass then runs only over the text of a
class body already located structurally, the same hybrid technique
this workspace's mailstall/jobclash/lockstall packages use) looking
for a Rails model class (superclass ApplicationRecord or
ActiveRecord::Base) and, within its own body:
- an explicit `self.table_name = "..."` override, if present
- every `validates :col[, :col2, ...], uniqueness: true` (or
`uniqueness: { scope: ..., ... }`) and
`validates_uniqueness_of :col[, :col2, ...][, scope: ...]`
declaration, one UniquenessValidation per column named.
This is deliberately one file's worth of information -- resolving it against a database-level unique index requires a second pass over db/schema.rb, done separately by Idxfence::SchemaParser and joined by Idxfence::Checker. See DETAILS.md's "two passes" section.
Defined Under Namespace
Classes: ParsedModel, UniquenessValidation
Constant Summary collapse
- MODEL_SUPERCLASS =
/\A(?:ApplicationRecord|ActiveRecord::Base)\z/.freeze
- LEADING_COLUMNS =
One
validates/validates_uniqueness_ofstatement's leading list of bare:symbolcolumn arguments, e.g. the:email, :usernameinvalidates :email, :username, uniqueness: true. /\A\s*(?:validates(?:_uniqueness_of)?)\s+((?::\w+\s*,\s*)*:\w+)/.freeze
- SCOPE_SYMBOL =
/scope:\s*:(\w+)/.freeze
- SCOPE_ARRAY =
/scope:\s*\[([^\]]*)\]/.freeze
- SCOPE_PCT_I =
/scope:\s*%i\[([^\]]*)\]/.freeze
- SELF_TABLE_NAME =
/self\.table_name\s*=\s*["']([^"']+)["']/.freeze
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(path:) ⇒ ModelParser
constructor
A new instance of ModelParser.
Constructor Details
#initialize(path:) ⇒ ModelParser
Returns a new instance of ModelParser.
39 40 41 |
# File 'lib/idxfence/model_parser.rb', line 39 def initialize(path:) @path = path end |
Instance Method Details
#call ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/idxfence/model_parser.rb', line 43 def call source = File.read(path) sexp = Ripper.sexp(source) return [] unless sexp lines = source.lines models = [] each_class_node(sexp) do |class_name, class_node| next unless model_class?(class_node) start_line = min_line(class_node) end_line = max_line(class_node) next unless start_line && end_line body_source = lines[(start_line - 1)...end_line].join models << ParsedModel.new( class_name: class_name, table_name_override: (m = body_source.match(SELF_TABLE_NAME)) && m[1], validations: uniqueness_validations(body_source, lines, start_line), file: path ) end models end |