Class: RubyLint::DefinitionGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-lint/definition_generator.rb

Overview

The DefinitionGenerator class is used for generating definitions based on the data that is available in the current Ruby runtime. Using this generator the otherwise painful and time consuming task of adding definitions for large projects (e.g. Ruby itself or Rails) becomes very easy up to the point where it will only take a minute or two.

Note that this generator works best on Ruby implementations that provide accurate parameter information using UnboundMethod#parameters. Currently the only implementation where this is the case is Rubinius HEAD. Both MRI and Jruby provide inaccurate information.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(constant, directory, options = {}) ⇒ DefinitionGenerator

Returns a new instance of DefinitionGenerator

Parameters:

  • constant (Class)
  • directory (String)
  • options (Hash) (defaults to: {})


40
41
42
43
44
45
46
47
# File 'lib/ruby-lint/definition_generator.rb', line 40

def initialize(constant, directory, options = {})
  @options   = default_options.merge(options)
  @inspector = Inspector.new(constant)
  @directory = directory
  @template  = File.read(
    File.expand_path('../template/definition.erb', __FILE__)
  )
end

Instance Attribute Details

#directoryString (readonly)

Returns The directory to store the generated definitions in.

Returns:

  • (String)

    The directory to store the generated definitions in.



32
33
34
35
36
37
38
39
40
41
42
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
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/ruby-lint/definition_generator.rb', line 32

class DefinitionGenerator
  attr_reader :directory, :options, :template, :inspector

  ##
  # @param [Class] constant
  # @param [String] directory
  # @param [Hash] options
  #
  def initialize(constant, directory, options = {})
    @options   = default_options.merge(options)
    @inspector = Inspector.new(constant)
    @directory = directory
    @template  = File.read(
      File.expand_path('../template/definition.erb', __FILE__)
    )
  end

  ##
  # Generates the definitions for every constant.
  #
  def generate
    constants = inspector.inspect_constants(
      inspector.constant,
      options[:ignore].dup
    )

    constants = constants.sort

    group_constants(constants).each do |root, names|
      filepath  = File.join(directory, "#{root.snake_case}.rb")
      constants = []

      if File.file?(filepath) and !options[:overwrite]
        next
      end

      names.each do |name|
        current_inspector = Inspector.new(name)
        inspected_methods = inspect_methods(current_inspector)
        superclass        = nil

        if current_inspector.inspect_superclass
          superclass = current_inspector.inspect_superclass.to_s
        end

        constant = GeneratedConstant.new(
          :name       => current_inspector.constant_name,
          :constant   => current_inspector.constant,
          :methods    => method_information(inspected_methods),
          :superclass => superclass,

          # Kernel is ignored since its included already in core/object.rb
          :modules    => current_inspector.inspect_modules - [Kernel]
        )

        constants << constant
      end

      render_template(filepath, template, constants)
    end
  end

  private

  ##
  # @param [String] path
  # @param [String] template
  # @param [Array] constants
  #
  def render_template(path, template, constants)
    erb = render_erb(template, :constants => constants)

    File.open(path, 'w') do |handle|
      handle.write(erb)
    end
  end

  ##
  # Groups constants together based on the top level namespace segment.
  #
  # @param [Array] constants
  # @return [Hash]
  #
  def group_constants(constants)
    grouped = Hash.new { |hash, key| hash[key] = [] }

    constants.each do |name|
      root           = name.split('::')[0]
      grouped[root] << name
    end

    return grouped
  end

  ##
  # @return [Hash]
  #
  def default_options
    return {:ignore => [], :overwrite => false}
  end

  ##
  # @param [RubyLint::Inspector] inspector
  # @return [Hash]
  #
  def inspect_methods(inspector)
    return {
      :method          => inspector.inspect_methods,
      :instance_method => inspector.inspect_instance_methods
    }
  end

  ##
  # Returns a Hash containing all the instance and class methods and their
  # arguments.
  #
  # @param [Hash] inspected
  # @return [Hash]
  #
  def method_information(inspected)
    arg_mapping = argument_mapping
    info        = {:method => {}, :instance_method => {}}

    inspected.each do |type, methods|
      methods.each do |method|
        args = []

        method.parameters.each_with_index do |arg, index|
          name = arg[1] || "arg#{index + 1}"
          args << {:type => arg_mapping[arg[0]], :name => name}
        end

        info[type][method.name] = args
      end
    end

    return info
  end

  ##
  # @return [Hash]
  #
  def argument_mapping
    return {
      :req   => :argument,
      :opt   => :optional_argument,
      :rest  => :rest_argument,
      :block => :block_argument
    }
  end

  ##
  # @param [String] template
  # @param [Hash] variables
  #
  def render_erb(template, variables = {})
    scope = Template::Scope.new(variables)
    erb   = ERB.new(template, nil, '-').result(scope.get_binding)

    # Trim excessive newlines.
    erb.gsub!(/\n{3,}/, "\n\n")

    # Get rid of the occasional empty newline before `end` tokens.
    erb.gsub!(/\n{2,}end/, "\nend")

    return erb
  end
end

#inspectorRubyLint::Inspector (readonly)

Returns:



32
33
34
35
36
37
38
39
40
41
42
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
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/ruby-lint/definition_generator.rb', line 32

class DefinitionGenerator
  attr_reader :directory, :options, :template, :inspector

  ##
  # @param [Class] constant
  # @param [String] directory
  # @param [Hash] options
  #
  def initialize(constant, directory, options = {})
    @options   = default_options.merge(options)
    @inspector = Inspector.new(constant)
    @directory = directory
    @template  = File.read(
      File.expand_path('../template/definition.erb', __FILE__)
    )
  end

  ##
  # Generates the definitions for every constant.
  #
  def generate
    constants = inspector.inspect_constants(
      inspector.constant,
      options[:ignore].dup
    )

    constants = constants.sort

    group_constants(constants).each do |root, names|
      filepath  = File.join(directory, "#{root.snake_case}.rb")
      constants = []

      if File.file?(filepath) and !options[:overwrite]
        next
      end

      names.each do |name|
        current_inspector = Inspector.new(name)
        inspected_methods = inspect_methods(current_inspector)
        superclass        = nil

        if current_inspector.inspect_superclass
          superclass = current_inspector.inspect_superclass.to_s
        end

        constant = GeneratedConstant.new(
          :name       => current_inspector.constant_name,
          :constant   => current_inspector.constant,
          :methods    => method_information(inspected_methods),
          :superclass => superclass,

          # Kernel is ignored since its included already in core/object.rb
          :modules    => current_inspector.inspect_modules - [Kernel]
        )

        constants << constant
      end

      render_template(filepath, template, constants)
    end
  end

  private

  ##
  # @param [String] path
  # @param [String] template
  # @param [Array] constants
  #
  def render_template(path, template, constants)
    erb = render_erb(template, :constants => constants)

    File.open(path, 'w') do |handle|
      handle.write(erb)
    end
  end

  ##
  # Groups constants together based on the top level namespace segment.
  #
  # @param [Array] constants
  # @return [Hash]
  #
  def group_constants(constants)
    grouped = Hash.new { |hash, key| hash[key] = [] }

    constants.each do |name|
      root           = name.split('::')[0]
      grouped[root] << name
    end

    return grouped
  end

  ##
  # @return [Hash]
  #
  def default_options
    return {:ignore => [], :overwrite => false}
  end

  ##
  # @param [RubyLint::Inspector] inspector
  # @return [Hash]
  #
  def inspect_methods(inspector)
    return {
      :method          => inspector.inspect_methods,
      :instance_method => inspector.inspect_instance_methods
    }
  end

  ##
  # Returns a Hash containing all the instance and class methods and their
  # arguments.
  #
  # @param [Hash] inspected
  # @return [Hash]
  #
  def method_information(inspected)
    arg_mapping = argument_mapping
    info        = {:method => {}, :instance_method => {}}

    inspected.each do |type, methods|
      methods.each do |method|
        args = []

        method.parameters.each_with_index do |arg, index|
          name = arg[1] || "arg#{index + 1}"
          args << {:type => arg_mapping[arg[0]], :name => name}
        end

        info[type][method.name] = args
      end
    end

    return info
  end

  ##
  # @return [Hash]
  #
  def argument_mapping
    return {
      :req   => :argument,
      :opt   => :optional_argument,
      :rest  => :rest_argument,
      :block => :block_argument
    }
  end

  ##
  # @param [String] template
  # @param [Hash] variables
  #
  def render_erb(template, variables = {})
    scope = Template::Scope.new(variables)
    erb   = ERB.new(template, nil, '-').result(scope.get_binding)

    # Trim excessive newlines.
    erb.gsub!(/\n{3,}/, "\n\n")

    # Get rid of the occasional empty newline before `end` tokens.
    erb.gsub!(/\n{2,}end/, "\nend")

    return erb
  end
end

#optionsHash (readonly)

Returns:

  • (Hash)


32
33
34
35
36
37
38
39
40
41
42
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
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/ruby-lint/definition_generator.rb', line 32

class DefinitionGenerator
  attr_reader :directory, :options, :template, :inspector

  ##
  # @param [Class] constant
  # @param [String] directory
  # @param [Hash] options
  #
  def initialize(constant, directory, options = {})
    @options   = default_options.merge(options)
    @inspector = Inspector.new(constant)
    @directory = directory
    @template  = File.read(
      File.expand_path('../template/definition.erb', __FILE__)
    )
  end

  ##
  # Generates the definitions for every constant.
  #
  def generate
    constants = inspector.inspect_constants(
      inspector.constant,
      options[:ignore].dup
    )

    constants = constants.sort

    group_constants(constants).each do |root, names|
      filepath  = File.join(directory, "#{root.snake_case}.rb")
      constants = []

      if File.file?(filepath) and !options[:overwrite]
        next
      end

      names.each do |name|
        current_inspector = Inspector.new(name)
        inspected_methods = inspect_methods(current_inspector)
        superclass        = nil

        if current_inspector.inspect_superclass
          superclass = current_inspector.inspect_superclass.to_s
        end

        constant = GeneratedConstant.new(
          :name       => current_inspector.constant_name,
          :constant   => current_inspector.constant,
          :methods    => method_information(inspected_methods),
          :superclass => superclass,

          # Kernel is ignored since its included already in core/object.rb
          :modules    => current_inspector.inspect_modules - [Kernel]
        )

        constants << constant
      end

      render_template(filepath, template, constants)
    end
  end

  private

  ##
  # @param [String] path
  # @param [String] template
  # @param [Array] constants
  #
  def render_template(path, template, constants)
    erb = render_erb(template, :constants => constants)

    File.open(path, 'w') do |handle|
      handle.write(erb)
    end
  end

  ##
  # Groups constants together based on the top level namespace segment.
  #
  # @param [Array] constants
  # @return [Hash]
  #
  def group_constants(constants)
    grouped = Hash.new { |hash, key| hash[key] = [] }

    constants.each do |name|
      root           = name.split('::')[0]
      grouped[root] << name
    end

    return grouped
  end

  ##
  # @return [Hash]
  #
  def default_options
    return {:ignore => [], :overwrite => false}
  end

  ##
  # @param [RubyLint::Inspector] inspector
  # @return [Hash]
  #
  def inspect_methods(inspector)
    return {
      :method          => inspector.inspect_methods,
      :instance_method => inspector.inspect_instance_methods
    }
  end

  ##
  # Returns a Hash containing all the instance and class methods and their
  # arguments.
  #
  # @param [Hash] inspected
  # @return [Hash]
  #
  def method_information(inspected)
    arg_mapping = argument_mapping
    info        = {:method => {}, :instance_method => {}}

    inspected.each do |type, methods|
      methods.each do |method|
        args = []

        method.parameters.each_with_index do |arg, index|
          name = arg[1] || "arg#{index + 1}"
          args << {:type => arg_mapping[arg[0]], :name => name}
        end

        info[type][method.name] = args
      end
    end

    return info
  end

  ##
  # @return [Hash]
  #
  def argument_mapping
    return {
      :req   => :argument,
      :opt   => :optional_argument,
      :rest  => :rest_argument,
      :block => :block_argument
    }
  end

  ##
  # @param [String] template
  # @param [Hash] variables
  #
  def render_erb(template, variables = {})
    scope = Template::Scope.new(variables)
    erb   = ERB.new(template, nil, '-').result(scope.get_binding)

    # Trim excessive newlines.
    erb.gsub!(/\n{3,}/, "\n\n")

    # Get rid of the occasional empty newline before `end` tokens.
    erb.gsub!(/\n{2,}end/, "\nend")

    return erb
  end
end

#templateString (readonly)

Returns The ERB template to use.

Returns:

  • (String)

    The ERB template to use.



32
33
34
35
36
37
38
39
40
41
42
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
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/ruby-lint/definition_generator.rb', line 32

class DefinitionGenerator
  attr_reader :directory, :options, :template, :inspector

  ##
  # @param [Class] constant
  # @param [String] directory
  # @param [Hash] options
  #
  def initialize(constant, directory, options = {})
    @options   = default_options.merge(options)
    @inspector = Inspector.new(constant)
    @directory = directory
    @template  = File.read(
      File.expand_path('../template/definition.erb', __FILE__)
    )
  end

  ##
  # Generates the definitions for every constant.
  #
  def generate
    constants = inspector.inspect_constants(
      inspector.constant,
      options[:ignore].dup
    )

    constants = constants.sort

    group_constants(constants).each do |root, names|
      filepath  = File.join(directory, "#{root.snake_case}.rb")
      constants = []

      if File.file?(filepath) and !options[:overwrite]
        next
      end

      names.each do |name|
        current_inspector = Inspector.new(name)
        inspected_methods = inspect_methods(current_inspector)
        superclass        = nil

        if current_inspector.inspect_superclass
          superclass = current_inspector.inspect_superclass.to_s
        end

        constant = GeneratedConstant.new(
          :name       => current_inspector.constant_name,
          :constant   => current_inspector.constant,
          :methods    => method_information(inspected_methods),
          :superclass => superclass,

          # Kernel is ignored since its included already in core/object.rb
          :modules    => current_inspector.inspect_modules - [Kernel]
        )

        constants << constant
      end

      render_template(filepath, template, constants)
    end
  end

  private

  ##
  # @param [String] path
  # @param [String] template
  # @param [Array] constants
  #
  def render_template(path, template, constants)
    erb = render_erb(template, :constants => constants)

    File.open(path, 'w') do |handle|
      handle.write(erb)
    end
  end

  ##
  # Groups constants together based on the top level namespace segment.
  #
  # @param [Array] constants
  # @return [Hash]
  #
  def group_constants(constants)
    grouped = Hash.new { |hash, key| hash[key] = [] }

    constants.each do |name|
      root           = name.split('::')[0]
      grouped[root] << name
    end

    return grouped
  end

  ##
  # @return [Hash]
  #
  def default_options
    return {:ignore => [], :overwrite => false}
  end

  ##
  # @param [RubyLint::Inspector] inspector
  # @return [Hash]
  #
  def inspect_methods(inspector)
    return {
      :method          => inspector.inspect_methods,
      :instance_method => inspector.inspect_instance_methods
    }
  end

  ##
  # Returns a Hash containing all the instance and class methods and their
  # arguments.
  #
  # @param [Hash] inspected
  # @return [Hash]
  #
  def method_information(inspected)
    arg_mapping = argument_mapping
    info        = {:method => {}, :instance_method => {}}

    inspected.each do |type, methods|
      methods.each do |method|
        args = []

        method.parameters.each_with_index do |arg, index|
          name = arg[1] || "arg#{index + 1}"
          args << {:type => arg_mapping[arg[0]], :name => name}
        end

        info[type][method.name] = args
      end
    end

    return info
  end

  ##
  # @return [Hash]
  #
  def argument_mapping
    return {
      :req   => :argument,
      :opt   => :optional_argument,
      :rest  => :rest_argument,
      :block => :block_argument
    }
  end

  ##
  # @param [String] template
  # @param [Hash] variables
  #
  def render_erb(template, variables = {})
    scope = Template::Scope.new(variables)
    erb   = ERB.new(template, nil, '-').result(scope.get_binding)

    # Trim excessive newlines.
    erb.gsub!(/\n{3,}/, "\n\n")

    # Get rid of the occasional empty newline before `end` tokens.
    erb.gsub!(/\n{2,}end/, "\nend")

    return erb
  end
end

Instance Method Details

#argument_mappingHash (private)

Returns:

  • (Hash)


174
175
176
177
178
179
180
181
# File 'lib/ruby-lint/definition_generator.rb', line 174

def argument_mapping
  return {
    :req   => :argument,
    :opt   => :optional_argument,
    :rest  => :rest_argument,
    :block => :block_argument
  }
end

#default_optionsHash (private)

Returns:

  • (Hash)


129
130
131
# File 'lib/ruby-lint/definition_generator.rb', line 129

def default_options
  return {:ignore => [], :overwrite => false}
end

#generateObject

Generates the definitions for every constant.



52
53
54
55
56
57
58
59
60
61
62
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
# File 'lib/ruby-lint/definition_generator.rb', line 52

def generate
  constants = inspector.inspect_constants(
    inspector.constant,
    options[:ignore].dup
  )

  constants = constants.sort

  group_constants(constants).each do |root, names|
    filepath  = File.join(directory, "#{root.snake_case}.rb")
    constants = []

    if File.file?(filepath) and !options[:overwrite]
      next
    end

    names.each do |name|
      current_inspector = Inspector.new(name)
      inspected_methods = inspect_methods(current_inspector)
      superclass        = nil

      if current_inspector.inspect_superclass
        superclass = current_inspector.inspect_superclass.to_s
      end

      constant = GeneratedConstant.new(
        :name       => current_inspector.constant_name,
        :constant   => current_inspector.constant,
        :methods    => method_information(inspected_methods),
        :superclass => superclass,

        # Kernel is ignored since its included already in core/object.rb
        :modules    => current_inspector.inspect_modules - [Kernel]
      )

      constants << constant
    end

    render_template(filepath, template, constants)
  end
end

#group_constants(constants) ⇒ Hash (private)

Groups constants together based on the top level namespace segment.

Parameters:

  • constants (Array)

Returns:

  • (Hash)


115
116
117
118
119
120
121
122
123
124
# File 'lib/ruby-lint/definition_generator.rb', line 115

def group_constants(constants)
  grouped = Hash.new { |hash, key| hash[key] = [] }

  constants.each do |name|
    root           = name.split('::')[0]
    grouped[root] << name
  end

  return grouped
end

#inspect_methods(inspector) ⇒ Hash (private)

Parameters:

Returns:

  • (Hash)


137
138
139
140
141
142
# File 'lib/ruby-lint/definition_generator.rb', line 137

def inspect_methods(inspector)
  return {
    :method          => inspector.inspect_methods,
    :instance_method => inspector.inspect_instance_methods
  }
end

#method_information(inspected) ⇒ Hash (private)

Returns a Hash containing all the instance and class methods and their arguments.

Parameters:

  • inspected (Hash)

Returns:

  • (Hash)


151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/ruby-lint/definition_generator.rb', line 151

def method_information(inspected)
  arg_mapping = argument_mapping
  info        = {:method => {}, :instance_method => {}}

  inspected.each do |type, methods|
    methods.each do |method|
      args = []

      method.parameters.each_with_index do |arg, index|
        name = arg[1] || "arg#{index + 1}"
        args << {:type => arg_mapping[arg[0]], :name => name}
      end

      info[type][method.name] = args
    end
  end

  return info
end

#render_erb(template, variables = {}) ⇒ Object (private)

Parameters:

  • template (String)
  • variables (Hash) (defaults to: {})


187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/ruby-lint/definition_generator.rb', line 187

def render_erb(template, variables = {})
  scope = Template::Scope.new(variables)
  erb   = ERB.new(template, nil, '-').result(scope.get_binding)

  # Trim excessive newlines.
  erb.gsub!(/\n{3,}/, "\n\n")

  # Get rid of the occasional empty newline before `end` tokens.
  erb.gsub!(/\n{2,}end/, "\nend")

  return erb
end

#render_template(path, template, constants) ⇒ Object (private)

Parameters:



101
102
103
104
105
106
107
# File 'lib/ruby-lint/definition_generator.rb', line 101

def render_template(path, template, constants)
  erb = render_erb(template, :constants => constants)

  File.open(path, 'w') do |handle|
    handle.write(erb)
  end
end