module Railroader::Util

This is a mixin containing utility methods.

Constants

ALL_COOKIES
ALL_PARAMETERS
COOKIES
COOKIES_SEXP
PARAMETERS
PARAMS_SEXP

These are never modified

PATH_PARAMETERS
QUERY_PARAMETERS
REQUEST_COOKIES
REQUEST_ENV
REQUEST_PARAMETERS
REQUEST_PARAMS
SAFE_LITERAL
SESSION
SESSION_SEXP

Public Instance Methods

array?(exp) click to toggle source

Check if exp represents an array: s(:array, […])

# File lib/railroader/util.rb, line 156
def array? exp
  exp.is_a? Sexp and exp.node_type == :array
end
block?(exp) click to toggle source

Check if exp represents a block of code

# File lib/railroader/util.rb, line 214
def block? exp
  exp.is_a? Sexp and (exp.node_type == :block or
                      exp.node_type == :rlist)
end
call?(exp) click to toggle source

Check if exp represents a method call: s(:call, …)

# File lib/railroader/util.rb, line 175
def call? exp
  exp.is_a? Sexp and
    (exp.node_type == :call or exp.node_type == :safe_call)
end
camelize(lower_case_and_underscored_word) click to toggle source

Convert a string from “something_like_this” to “SomethingLikeThis”

Taken from ActiveSupport.

# File lib/railroader/util.rb, line 34
def camelize lower_case_and_underscored_word
  lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
end
class_name(exp) click to toggle source

Returns a class name as a Symbol. If class name cannot be determined, returns exp.

# File lib/railroader/util.rb, line 56
def class_name exp
  case exp
  when Sexp
    case exp.node_type
    when :const
      exp.value
    when :lvar
      exp.value.to_sym
    when :colon2
      "#{class_name(exp.lhs)}::#{exp.rhs}".to_sym
    when :colon3
      "::#{exp.value}".to_sym
    when :self
      @current_class || @current_module || nil
    else
      exp
    end
  when Symbol
    exp
  when nil
    nil
  else
    exp
  end
end
constant?(exp) click to toggle source
# File lib/railroader/util.rb, line 263
def constant? exp
  node_type? exp, :const, :colon2, :colon3
end
contains_class?(exp) click to toggle source

Returns true if the given exp contains a :class node.

Useful for checking if a module is just a module or if it is a namespace.

# File lib/railroader/util.rb, line 280
def contains_class? exp
  todo = [exp]

  until todo.empty?
    current = todo.shift

    if node_type? current, :class
      return true
    elsif sexp? current
      todo = current[1..-1].concat todo
    end
  end

  false
end
context_for(app_tree, warning, tracker = nil) click to toggle source

Return array of lines surrounding the warning location from the original file.

# File lib/railroader/util.rb, line 410
def context_for app_tree, warning, tracker = nil
  file = file_for warning, tracker
  context = []
  return context unless warning.line and file and @app_tree.path_exists? file

  current_line = 0
  start_line = warning.line - 5
  end_line = warning.line + 5

  start_line = 1 if start_line < 0

  File.open file do |f|
    f.each_line do |line|
      current_line += 1

      next if line.strip == ""

      if current_line > end_line
        break
      end

      if current_line >= start_line
        context << [current_line, line]
      end
    end
  end

  context
end
cookies?(exp) click to toggle source
# File lib/railroader/util.rb, line 236
def cookies? exp
  if exp.is_a? Sexp
    return true if exp.node_type == :cookies or ALL_COOKIES.include? exp

    if call? exp
      if cookies? exp[1]
        return true
      elsif exp[2] == :[]
        return cookies? exp[1]
      end
    end
  end

  false
end
false?(exp) click to toggle source

Check if exp represents a :false or :nil node

# File lib/railroader/util.rb, line 208
def false? exp
  exp.is_a? Sexp and (exp.node_type == :false or
                      exp.node_type == :nil)
end
file_by_name(name, type, tracker = nil) click to toggle source

Attempt to determine path to context file based on the reported name in the warning.

For example,

file_by_name FileController #=> "/rails/root/app/controllers/file_controller.rb
# File lib/railroader/util.rb, line 364
def file_by_name name, type, tracker = nil
  return nil unless name
  string_name = name.to_s
  name = name.to_sym

  unless type
    if string_name =~ /Controller$/
      type = :controller
    elsif camelize(string_name) == string_name # This is not always true
      type = :model
    else
      type = :template
    end
  end

  path = tracker.app_path

  case type
  when :controller
    if tracker.controllers[name]
      path = tracker.controllers[name].file
    else
      path += "/app/controllers/#{underscore(string_name)}.rb"
    end
  when :model
    if tracker.models[name]
      path = tracker.models[name].file
    else
      path += "/app/models/#{underscore(string_name)}.rb"
    end
  when :template
    if tracker.templates[name] and tracker.templates[name].file
      path = tracker.templates[name].file
    elsif string_name.include? " "
      name = string_name.split[0].to_sym
      path = file_for tracker, name, :template
    else
      path = nil
    end
  end

  path
end
file_for(warning, tracker = nil) click to toggle source

Return file name related to given warning. Uses warning.file if it exists

# File lib/railroader/util.rb, line 333
def file_for warning, tracker = nil
  if tracker.nil?
    tracker = @tracker || self.tracker
  end

  if warning.file
    File.expand_path warning.file, tracker.app_path
  elsif warning.template and warning.template.file
    warning.template.file
  else
    case warning.warning_set
    when :controller
      file_by_name warning.controller, :controller, tracker
    when :template
      file_by_name warning.template.name, :template, tracker
    when :model
      file_by_name warning.model, :model, tracker
    when :warning
      file_by_name warning.class, nil, tracker
    else
      nil
    end
  end
end
github_url(file, line=nil) click to toggle source
# File lib/railroader/util.rb, line 458
def github_url file, line=nil
  if repo_url = @tracker.options[:github_url] and file and not file.empty? and file.start_with? '/'
    url = "#{repo_url}/#{relative_path(file)}"
    url << "#L#{line}" if line
  else
    nil
  end
end
hash?(exp) click to toggle source

Check if exp represents a hash: s(:hash, {…}) This also includes pseudo hashes params, session, and cookies.

# File lib/railroader/util.rb, line 148
def hash? exp
  exp.is_a? Sexp and (exp.node_type == :hash or
                      exp.node_type == :params or
                      exp.node_type == :session or
                      exp.node_type == :cookies)
end
hash_access(hash, key) click to toggle source

Get value from hash using key.

If key is a Symbol, it will be converted to a Sexp(:lit, key).

# File lib/railroader/util.rb, line 121
def hash_access hash, key
  if key.is_a? Symbol
    key = Sexp.new(:lit, key)
  end

  if index = hash.find_index(key) and index > 0
    return hash[index + 1]
  end

  nil
end
hash_insert(hash, key, value) click to toggle source

Insert value into Hash Sexp

# File lib/railroader/util.rb, line 103
def hash_insert hash, key, value
  index = 1
  hash_iterate hash.dup do |k, v|
    if k == key
      hash[index + 1] = value
      return hash
    end
    index += 2
  end

  hash << key << value

  hash
end
hash_iterate(hash) { |hash, hash| ... } click to toggle source

Takes an Sexp like (:hash, (:lit, :key), (:str, “value”)) and yields the key and value pairs to the given block.

For example:

h = Sexp.new(:hash, (:lit, :name), (:str, “bob”), (:lit, :name), (:str, “jane”)) names = [] hash_iterate(h) do |key, value|

if symbol? key and key[1] == :name
  names << value[1]
end

end names #[“bob”]

# File lib/railroader/util.rb, line 96
def hash_iterate hash
  1.step(hash.length - 1, 2) do |i|
    yield hash[i], hash[i + 1]
  end
end
integer?(exp) click to toggle source

Check if exp represents an Integer: s(:lit, …)

# File lib/railroader/util.rb, line 186
def integer? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Integer
end
make_call(target, method, *args) click to toggle source
# File lib/railroader/util.rb, line 296
def make_call target, method, *args
  call = Sexp.new(:call, target, method)

  if args.empty? or args.first.empty?
    # nothing to do
  elsif node_type? args.first, :arglist
    call.concat args.first[1..-1]
  elsif args.first.node_type.is_a? Sexp # just a list of args
    call.concat args.first
  else
    call.concat args
  end

  call
end
node_type?(exp, *types) click to toggle source

Check if exp is a Sexp and the node type matches one of the given types.

# File lib/railroader/util.rb, line 273
def node_type? exp, *types
  exp.is_a? Sexp and types.include? exp.node_type
end
number?(exp) click to toggle source

Check if exp represents a number: s(:lit, …)

# File lib/railroader/util.rb, line 191
def number? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Numeric
end
params?(exp) click to toggle source

Check if exp is a params hash

# File lib/railroader/util.rb, line 220
def params? exp
  if exp.is_a? Sexp
    return true if exp.node_type == :params or ALL_PARAMETERS.include? exp

    if call? exp
      if params? exp[1]
        return true
      elsif exp[2] == :[]
        return params? exp[1]
      end
    end
  end

  false
end
pluralize(word) click to toggle source

stupid simple, used to delegate to ActiveSupport

# File lib/railroader/util.rb, line 50
def pluralize word
  word + "s"
end
rails_version() click to toggle source
# File lib/railroader/util.rb, line 328
def rails_version
  @tracker.config.rails_version
end
regexp?(exp) click to toggle source

Check if exp represents a Regexp: s(:lit, /…/)

# File lib/railroader/util.rb, line 181
def regexp? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Regexp
end
relative_path(file) click to toggle source
# File lib/railroader/util.rb, line 440
def relative_path file
  pname = Pathname.new file
  if file and not file.empty? and pname.absolute?
    pname.relative_path_from(Pathname.new(@tracker.app_path)).to_s
  else
    file
  end
end
request_env?(exp) click to toggle source
# File lib/railroader/util.rb, line 252
def request_env? exp
  call? exp and (exp == REQUEST_ENV or exp[1] == REQUEST_ENV)
end
request_value?(exp) click to toggle source

Check if exp is params, cookies, or request_env

# File lib/railroader/util.rb, line 257
def request_value? exp
  params? exp or
  cookies? exp or
  request_env? exp
end
result?(exp) click to toggle source

Check if exp represents a result: s(:result, …)

# File lib/railroader/util.rb, line 196
def result? exp
  exp.is_a? Sexp and exp.node_type == :result
end
safe_literal(line = nil) click to toggle source
# File lib/railroader/util.rb, line 312
def safe_literal line = nil
  s(:lit, :BRAKEMAN_SAFE_LITERAL).line(line || 0)
end
safe_literal?(exp) click to toggle source
# File lib/railroader/util.rb, line 316
def safe_literal? exp
  exp == SAFE_LITERAL
end
safe_literal_target?(exp) click to toggle source
# File lib/railroader/util.rb, line 320
def safe_literal_target? exp
  if call? exp
    safe_literal_target? exp.target
  else
    safe_literal? exp
  end
end
set_env_defaults() click to toggle source

Adds params, session, and cookies to environment so they can be replaced by their respective Sexps.

# File lib/railroader/util.rb, line 140
def set_env_defaults
  @env[PARAMETERS] = PARAMS_SEXP
  @env[SESSION] = SESSION_SEXP
  @env[COOKIES] = COOKIES_SEXP
end
sexp?(exp) click to toggle source

Check if exp is a Sexp.

# File lib/railroader/util.rb, line 268
def sexp? exp
  exp.is_a? Sexp
end
string?(exp) click to toggle source

Check if exp represents a String: s(:str, “…”)

# File lib/railroader/util.rb, line 161
def string? exp
  exp.is_a? Sexp and exp.node_type == :str
end
string_interp?(exp) click to toggle source
# File lib/railroader/util.rb, line 165
def string_interp? exp
  exp.is_a? Sexp and exp.node_type == :dstr
end
symbol?(exp) click to toggle source

Check if exp represents a Symbol: s(:lit, :…)

# File lib/railroader/util.rb, line 170
def symbol? exp
  exp.is_a? Sexp and exp.node_type == :lit and exp[1].is_a? Symbol
end
table_to_csv(table) click to toggle source

rely on Terminal::Table to build the structure, extract the data out in CSV format

# File lib/railroader/util.rb, line 488
def table_to_csv table
  return "" unless table

  Railroader.load_railroader_dependency 'terminal-table'
  headings = table.headings
  if headings.is_a? Array
    headings = headings.first
  end

  output = CSV.generate_line(headings.cells.map{|cell| cell.to_s.strip})
  table.rows.each do |row|
    output << CSV.generate_line(row.cells.map{|cell| cell.to_s.strip})
  end
  output
end
template_path_to_name(path) click to toggle source

Convert path/filename to view name

views/test/something.html.erb -> test/something

# File lib/railroader/util.rb, line 452
def template_path_to_name path
  names = path.split("/")
  names.last.gsub!(/(\.(html|js)\..*|\.(rhtml|haml|erb|slim))$/, '')
  names[(names.index("views") + 1)..-1].join("/").to_sym
end
true?(exp) click to toggle source

Check if exp represents a :true, :lit, or :string node

# File lib/railroader/util.rb, line 201
def true? exp
  exp.is_a? Sexp and (exp.node_type == :true or
                      exp.node_type == :lit or
                      exp.node_type == :string)
end
truncate_table(str) click to toggle source
# File lib/railroader/util.rb, line 467
def truncate_table str
  @terminal_width ||= if @tracker.options[:table_width]
                        @tracker.options[:table_width]
                      elsif $stdin && $stdin.tty?
                        Railroader.load_railroader_dependency 'highline'
                        ::HighLine.new.terminal_size[0]
                      else
                        80
                      end
  lines = str.lines

  lines.map do |line|
    if line.chomp.length > @terminal_width
      line[0..(@terminal_width - 3)] + ">>\n"
    else
      line
    end
  end.join
end
underscore(camel_cased_word) click to toggle source

Convert a string from “Something::LikeThis” to “something/like_this”

Taken from ActiveSupport.

# File lib/railroader/util.rb, line 41
def underscore camel_cased_word
  camel_cased_word.to_s.gsub(/::/, '/').
    gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
    gsub(/([a-z\d])([A-Z])/, '\1_\2').
    tr("-", "_").
    downcase
end