Class Liftoff::Parser
In: vendor/plugins/liftoff/lib/parser.rb
Parent: Object

Methods

new   parse_associations   run  

Public Class methods

[Source]

    # File vendor/plugins/liftoff/lib/parser.rb, line 35
35:     def initialize(calfile, modeldir)
36:       @log = Logger.new(STDOUT);
37:       @log.level = Logger::ERROR;
38:       @log.debug "\n\nCAL file: " + File.expand_path(calfile)
39:       @log.debug "Model path: " + File.expand_path(modeldir)
40:       run(calfile, modeldir) 
41:     end

Public Instance methods

[Source]

     # File vendor/plugins/liftoff/lib/parser.rb, line 53
 53:     def parse_associations(calfile)
 54:       by_from = {};
 55:       by_to = {};
 56:       line_number = 0;
 57:       calfile.each_line do |line|
 58:         line_number += 1;
 59: 
 60:         # First, strip trailing newlines and comments.
 61:         line = line.chomp;
 62:         @log.debug "\nReading line " + line_number.to_s + ": " +  line;
 63: 
 64:         arr = line.strip.split(%r{#+})
 65: 
 66:         if arr.length == 0
 67:           @log.debug "Ignoring blank line or empty comment..."
 68:           next
 69:         end
 70: 
 71:         # Practice for a future implementation that might interpret these comment fields.
 72:         if arr[0].length == 0
 73:           @log.debug("Ignoring line comment: " + "\"" + arr[1].strip + "\"" ) if arr[1] != nil && arr[1].length > 0
 74:           next
 75:         end
 76:         if arr[1] != nil && arr[1].length > 0
 77:           @log.debug "Ignoring side comment: " + "\"" + arr[1].strip + "\""
 78:         end
 79: 
 80:         # If we get this far, then the current line contains non-comment elements.
 81:         line = arr[0].rstrip
 82:         @log.debug "Found possible CAL chain: " + line
 83:         @log.debug("Tokenizing  ...");
 84: 
 85:         # Each element is either a word (table) or an operator.
 86:         arr=line.scan(%r{\w+|<>|<=|=>|<|>});
 87: 
 88:         if arr.length < 3
 89:           @log.error "Syntax error at line " + line_number.to_s + ": line is too short or does not contain a valid operator.";
 90:           exit(1);
 91:         end
 92: 
 93:         @log.debug "token array = " + arr.inspect;
 94: 
 95:         # Parse the token array into a set of associations by visiting each operator
 96:         # in left to right order. Because operators correspond to odd-numbered elements,
 97:         # the index is initialized to 1 and incremented by 2 at the end of each cycle.
 98:         index = 1;
 99:         while index < arr.length
100:           @log.debug "operator element = " + index.to_s;
101: 
102:           # This step is critical. The operator determines the type of
103:           # the association while left and right specify the tables to which
104:           # it applies.
105:           left, op, right = arr[index-1 .. index+1];
106:           if op == nil || right == nil
107:             @log.error "Syntax error at line " + line_number.to_s + ": " + "missing token."
108:             exit(1)
109:           end  
110: 
111:           sides = [left, right].map {|side| side.downcase.singularize}
112: 
113: 
114:           # Construct the association corresponding to the operator.  The first
115:           # parameter to the constructor corresponds to the "from" side of the
116:           # relationship, whereas the second parameter corresponds to the "to"
117:           # side. Many-to-one and "right-has-one" associations are canonicalized
118:           # as One-to-many and "left-has-one", respectively, by reversing the
119:           # arguments to the constructor.
120:           result = case op
121:                    when "<"
122:                      OneToMany.new(*sides)
123:                    when ">"
124:                      OneToMany.new(*sides.reverse)
125:                    when "=>"
126:                      LeftHasOne.new(*sides)
127:                    when "<="
128:                      LeftHasOne.new(*sides.reverse)
129:                    when "<>"
130:                      ManyToMany.new(*sides)
131:                    else
132:                      @log.error "Parse error at line #{line_number.to_s}: unrecognized operator: #{op}.";
133:                      exit(1);
134:                    end # case
135: 
136:           # Add each side of the relationship to the corresponding hash table.
137:           # This will allow the model generator to compute all of the
138:           # associations for a given model file by combining the by_from and
139:           # by_to arrays of values that share the same model/table name as a
140:           # common key.
141:           @log.debug "Indexing association by 'from' side: " + result.from + "<=" + [result].to_s;
142:           if ! by_from[result.from]
143:             by_from[result.from] = [result];
144:           else
145:             by_from[result.from].push(result);
146:           end
147:           @log.debug "Indexing association by 'to' side: " + result.to + "<=" + [result].to_s;
148:           if ! by_to[result.to]
149:             by_to[result.to] = [result];
150:           else
151:             by_to[result.to].push(result);
152:           end
153:           index += 2;
154:         end
155:       end 
156:       return by_from, by_to;
157:     end

[Source]

    # File vendor/plugins/liftoff/lib/parser.rb, line 44
44:     def run(calfile, modeldir)
45:       File.open(calfile, "r") do |file|
46:         associations = parse_associations(file)
47:         generator = ModelGenerator.new
48:         generator.generate_model_files(associations, modeldir)
49:       end
50:     end

[Validate]