reflexive 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/reflexive/application.rb +171 -0
- data/lib/reflexive/coderay_html_encoder.rb +46 -0
- data/lib/reflexive/coderay_ruby_scanner.rb +234 -0
- data/lib/reflexive/columnizer.rb +73 -0
- data/lib/reflexive/constantize.rb +71 -0
- data/lib/reflexive/descendants.rb +39 -0
- data/lib/reflexive/faster_open_struct.rb +116 -0
- data/lib/reflexive/helpers.rb +189 -0
- data/lib/reflexive/methods.rb +140 -0
- data/lib/reflexive/parse_tree_top_down_walker.rb +392 -0
- data/lib/reflexive/reflexive_ripper.rb +274 -0
- data/lib/reflexive/routing_helpers.rb +70 -0
- data/lib/reflexive/variables_scope.rb +16 -0
- data/lib/reflexive.rb +1 -0
- data/public/javascripts/reflexive/jquery-1.4.2.js +6240 -0
- data/public/javascripts/reflexive/reflexive.js +12 -0
- data/public/stylesheets/reflexive/coderay.css +130 -0
- data/public/stylesheets/reflexive/reflexive.css +111 -0
- data/reflexive.gemspec +25 -0
- data/views/constants_methods.html.erb +0 -0
- data/views/constants_show.erb +96 -0
- data/views/dashboard.erb +50 -0
- data/views/directories_show.erb +16 -0
- data/views/error.erb +5 -0
- data/views/files_show.erb +27 -0
- data/views/layout.erb +20 -0
- data/views/methods_apidock.erb +17 -0
- data/views/methods_definition.erb +37 -0
- data/views/methods_rubydoc.erb +16 -0
- metadata +231 -0
@@ -0,0 +1,116 @@
|
|
1
|
+
module Faster
|
2
|
+
# ## Faster::OpenStruct
|
3
|
+
#
|
4
|
+
# Up to 40 (!) times more memory efficient version of OpenStruct
|
5
|
+
#
|
6
|
+
# Differences from Ruby MRI OpenStruct:
|
7
|
+
#
|
8
|
+
# 1. Doesn't `dup` passed initialization hash (NOTE: only reference to hash is stored)
|
9
|
+
#
|
10
|
+
# 2. Doesn't convert hash keys to symbols (by default string keys are used,
|
11
|
+
# with fallback to symbol keys)
|
12
|
+
#
|
13
|
+
# 3. Creates methods on the fly on `OpenStruct` class, instead of singleton class.
|
14
|
+
# Uses `module_eval` with string to avoid holding scope references for every method.
|
15
|
+
#
|
16
|
+
# 4. Refactored, crud clean, spec covered :)
|
17
|
+
#
|
18
|
+
class OpenStruct
|
19
|
+
# Undefine particularly nasty interfering methods on Ruby 1.8
|
20
|
+
undef :type if method_defined?(:type)
|
21
|
+
undef :id if method_defined?(:id)
|
22
|
+
|
23
|
+
def initialize(hash = nil)
|
24
|
+
@hash = hash || {}
|
25
|
+
@initialized_empty = hash == nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(method_name_sym, *args)
|
29
|
+
if method_name_sym.to_s[-1] == ?=
|
30
|
+
if args.size != 1
|
31
|
+
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)", caller(1)
|
32
|
+
end
|
33
|
+
|
34
|
+
if self.frozen?
|
35
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
__new_ostruct_member__(method_name_sym.to_s.chomp("="))
|
39
|
+
send(method_name_sym, args[0])
|
40
|
+
elsif args.size == 0
|
41
|
+
__new_ostruct_member__(method_name_sym)
|
42
|
+
send(method_name_sym)
|
43
|
+
else
|
44
|
+
raise NoMethodError, "undefined method `#{method_name_sym}' for #{self}", caller(1)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def __new_ostruct_member__(method_name_sym)
|
49
|
+
self.class.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
50
|
+
def #{ method_name_sym }
|
51
|
+
@hash.fetch("#{ method_name_sym }", @hash[:#{ method_name_sym }]) # read by default from string key, then try symbol
|
52
|
+
# if string key doesn't exist
|
53
|
+
end
|
54
|
+
END_EVAL
|
55
|
+
|
56
|
+
unless method_name_sym.to_s[-1] == ?? # can't define writer for predicate method
|
57
|
+
self.class.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
|
58
|
+
def #{ method_name_sym }=(val)
|
59
|
+
if @hash.key?("#{ method_name_sym }") || @initialized_empty # write by default to string key (when it is present
|
60
|
+
# in initialization hash or initialization hash
|
61
|
+
# wasn't provided)
|
62
|
+
@hash["#{ method_name_sym }"] = val # if it doesn't exist - write to symbol key
|
63
|
+
else
|
64
|
+
@hash[:#{ method_name_sym }] = val
|
65
|
+
end
|
66
|
+
end
|
67
|
+
END_EVAL
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def empty?
|
72
|
+
@hash.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Compare this object and +other+ for equality.
|
77
|
+
#
|
78
|
+
def ==(other)
|
79
|
+
return false unless other.is_a?(self.class)
|
80
|
+
@hash == other.instance_variable_get(:@hash)
|
81
|
+
end
|
82
|
+
|
83
|
+
InspectKey = :__inspect_key__ # :nodoc:
|
84
|
+
|
85
|
+
#
|
86
|
+
# Returns a string containing a detailed summary of the keys and values.
|
87
|
+
#
|
88
|
+
def inspect
|
89
|
+
str = "#<#{ self.class }"
|
90
|
+
str << " #{ @hash.map { |k, v| "#{ k }=#{ v.inspect }" }.join(", ") }" unless @hash.empty?
|
91
|
+
str << ">"
|
92
|
+
end
|
93
|
+
|
94
|
+
def inspect_with_reentrant_guard(default = "...")
|
95
|
+
Thread.current[InspectKey] ||= []
|
96
|
+
|
97
|
+
if Thread.current[InspectKey].include?(self)
|
98
|
+
return default # reenter detected
|
99
|
+
end
|
100
|
+
|
101
|
+
Thread.current[InspectKey] << self
|
102
|
+
|
103
|
+
begin
|
104
|
+
inspect_without_reentrant_guard
|
105
|
+
ensure
|
106
|
+
Thread.current[InspectKey].pop
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
alias_method :inspect_without_reentrant_guard, :inspect
|
111
|
+
alias_method :inspect, :inspect_with_reentrant_guard
|
112
|
+
|
113
|
+
alias :to_s :inspect
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require "reflexive/routing_helpers"
|
2
|
+
require "reflexive/coderay_ruby_scanner"
|
3
|
+
require "reflexive/coderay_html_encoder"
|
4
|
+
|
5
|
+
module Reflexive
|
6
|
+
FILE_EXT = /\.\w+\z/
|
7
|
+
DL_EXT = /\.s?o\z/
|
8
|
+
def self.load_path_lookup(path)
|
9
|
+
path_with_rb_ext = path.sub(FILE_EXT, "") + ".rb"
|
10
|
+
feature = nil
|
11
|
+
$LOAD_PATH.detect do |load_path|
|
12
|
+
File.exists?(feature = File.join(load_path, path_with_rb_ext))
|
13
|
+
end
|
14
|
+
feature
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.loaded_features_lookup(path)
|
18
|
+
path_without_ext = path.sub(FILE_EXT, "")
|
19
|
+
|
20
|
+
$LOADED_FEATURES.reverse.reject do |feature|
|
21
|
+
feature =~ DL_EXT
|
22
|
+
end.detect do |feature|
|
23
|
+
feature_without_ext = feature.sub(FILE_EXT, "")
|
24
|
+
|
25
|
+
feature_without_ext =~ /\/#{ Regexp.escape(path_without_ext) }\z/
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module Helpers
|
30
|
+
include RoutingHelpers
|
31
|
+
|
32
|
+
def filter_existing_constants(constants)
|
33
|
+
constants.
|
34
|
+
select { |c| Kernel.const_defined?(c) }.
|
35
|
+
map { |c| Kernel.const_get(c) }
|
36
|
+
end
|
37
|
+
|
38
|
+
CODERAY_ENCODER_OPTIONS = {
|
39
|
+
:wrap => :div,
|
40
|
+
:css => :class,
|
41
|
+
:line_numbers => :inline
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
def highlight_file(path, options = {})
|
45
|
+
options = CODERAY_ENCODER_OPTIONS.merge(options)
|
46
|
+
if path =~ /\.rb$/
|
47
|
+
src = IO.read(path)
|
48
|
+
tokens = CodeRayRubyScanner.new(src).tokenize
|
49
|
+
encoder = CodeRayHtmlEncoder.new(options)
|
50
|
+
encoder.encode_tokens(tokens)
|
51
|
+
elsif path =~ /\.(markdown|md)$/
|
52
|
+
require "rdiscount"
|
53
|
+
src = IO.read(path)
|
54
|
+
markdown = RDiscount.new(src)
|
55
|
+
markdown.to_html
|
56
|
+
else
|
57
|
+
CodeRay.scan_file(path).html(options).div
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def constant_name(klass)
|
62
|
+
klass.name || klass.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def link_to_file(path, options = {})
|
66
|
+
link_text = if options[:file_name_only]
|
67
|
+
File.basename(path) + (path[-1] == ?/ ? "/" : "")
|
68
|
+
else
|
69
|
+
shorten_file_path(path)
|
70
|
+
end
|
71
|
+
|
72
|
+
link_to(link_text,
|
73
|
+
file_path(path),
|
74
|
+
:title => path,
|
75
|
+
:class => "path")
|
76
|
+
end
|
77
|
+
|
78
|
+
def shorten_file_path(path)
|
79
|
+
require "rbconfig"
|
80
|
+
path.
|
81
|
+
gsub(/\A#{ Regexp.escape(Gem.dir) }\/gems/, '#{gems}').
|
82
|
+
gsub(/\A#{ Config::CONFIG["rubylibdir"] }/, '#{rubylib}')
|
83
|
+
end
|
84
|
+
|
85
|
+
def load_and_highlight(location)
|
86
|
+
tokens = CodeRay.scan(IO.read(location), :ruby)
|
87
|
+
|
88
|
+
tokens.html(:line_numbers => :inline, :wrap => :page)
|
89
|
+
end
|
90
|
+
|
91
|
+
def methods_table(constant, lookup_path)
|
92
|
+
linked_methods = lookup_path.map do |name, visibility|
|
93
|
+
link_to_method(lookup_path.module_name.gsub(/\[|\]/, ""),
|
94
|
+
name,
|
95
|
+
visibility)
|
96
|
+
end
|
97
|
+
|
98
|
+
Reflexive::Columnizer.columnize(linked_methods, 120)
|
99
|
+
end
|
100
|
+
|
101
|
+
def new_methods_table(constant, level, methods)
|
102
|
+
linked_methods = methods.sort.map do |name|
|
103
|
+
new_link_to_method(constant, level, name)
|
104
|
+
end
|
105
|
+
Reflexive::Columnizer.columnize(linked_methods, 120)
|
106
|
+
end
|
107
|
+
|
108
|
+
def constants_table(base_constant, constants, width = 120)
|
109
|
+
Reflexive::Columnizer.columnize(constants_links(base_constant, constants), width)
|
110
|
+
end
|
111
|
+
|
112
|
+
def constants_list(base_constant, constants)
|
113
|
+
constants_links(base_constant, constants).join("<br/>")
|
114
|
+
end
|
115
|
+
|
116
|
+
def constants_links(base_constant, constants)
|
117
|
+
constants.map do |constant|
|
118
|
+
full_name = constant_name(constant)
|
119
|
+
[ full_name, constant ]
|
120
|
+
end.sort_by(&:first).map do |full_name, constant|
|
121
|
+
link_text = full_name.gsub("#{ base_constant }::", "")
|
122
|
+
link_to(link_text, constant_path(constant), :title => full_name)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def instance_methods_table(lookup_path)
|
127
|
+
linked_methods = []
|
128
|
+
|
129
|
+
%w(public protected private).each do |visibility|
|
130
|
+
methods = lookup_path.module.send("#{ visibility }_instance_methods", false)
|
131
|
+
|
132
|
+
linked_methods += methods.map do |name|
|
133
|
+
link_to_method(lookup_path.module_name.gsub(/\[|\]/, ""),
|
134
|
+
name,
|
135
|
+
visibility)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
Reflexive::Columnizer.columnize(linked_methods, 120)
|
140
|
+
end
|
141
|
+
|
142
|
+
def just_methods_table(klass)
|
143
|
+
linked_methods = []
|
144
|
+
|
145
|
+
ancestors_with_methods = ActiveRecord::Base.ancestors.map do |a|
|
146
|
+
[a, a.methods(false).sort] unless a.methods(false).empty?
|
147
|
+
end.compact
|
148
|
+
|
149
|
+
ancestors_with_methods.each do |ancestor, ancestor_methods|
|
150
|
+
linked_methods += ancestor_methods.map do |name|
|
151
|
+
link_to_method(ancestor.name, name)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
Reflexive::Columnizer.columnize(linked_methods, 120)
|
156
|
+
end
|
157
|
+
|
158
|
+
def link_to_method(constant, method_name, visibility = nil)
|
159
|
+
link_text = truncate(method_name)
|
160
|
+
link_to(Rack::Utils.escape_html(link_text),
|
161
|
+
method_path(constant, method_name),
|
162
|
+
:title => (method_name if link_text.include?("...")))
|
163
|
+
end
|
164
|
+
|
165
|
+
def new_link_to_method(constant, level, method_name)
|
166
|
+
link_text = truncate(method_name)
|
167
|
+
link_to(Rack::Utils.escape_html(link_text),
|
168
|
+
new_method_path(constant, level, method_name),
|
169
|
+
:title => (method_name if link_text.include?("...")))
|
170
|
+
end
|
171
|
+
|
172
|
+
##
|
173
|
+
# Truncates a given text after a given :length if text is longer than :length (defaults to 30).
|
174
|
+
# The last characters will be replaced with the :omission (defaults to "в_│") for a total length not exceeding :length.
|
175
|
+
#
|
176
|
+
# ==== Examples
|
177
|
+
#
|
178
|
+
# truncate("Once upon a time in a world far far away", :length => 8) => "Once upon..."
|
179
|
+
#
|
180
|
+
def truncate(text, options={})
|
181
|
+
options.reverse_merge!(:length => 30, :omission => "...")
|
182
|
+
if text
|
183
|
+
len = options[:length] - options[:omission].length
|
184
|
+
chars = text
|
185
|
+
(chars.length > options[:length] ? chars[0...len] + options[:omission] : text).to_s
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Reflexive
|
2
|
+
class Methods
|
3
|
+
def initialize(klass_or_module, options = {})
|
4
|
+
@klass_or_module = klass_or_module
|
5
|
+
@ancestor_name_formatter = options.fetch(:ancestor_name_formatter,
|
6
|
+
default_ancestor_name_formatter)
|
7
|
+
@exclude_trite = options.fetch(:exclude_trite, true)
|
8
|
+
end
|
9
|
+
|
10
|
+
def all
|
11
|
+
@all ||= find_all
|
12
|
+
end
|
13
|
+
|
14
|
+
def files
|
15
|
+
@files ||= find_all_files
|
16
|
+
end
|
17
|
+
|
18
|
+
def constants
|
19
|
+
@constants ||= @klass_or_module.constants(true).map do |c|
|
20
|
+
@klass_or_module.const_get(c) rescue nil
|
21
|
+
end.compact.select do |c|
|
22
|
+
c.instance_of?(Class) || c.instance_of?(Module)
|
23
|
+
end # rescue r(@klass_or_module.constants)
|
24
|
+
end
|
25
|
+
|
26
|
+
def descendants
|
27
|
+
@descendants ||= Reflexive.descendants(@klass_or_module)
|
28
|
+
end
|
29
|
+
|
30
|
+
VISIBILITIES = [ :public, :protected, :private ].freeze
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def each_immediate_class_and_instance_method(&block)
|
35
|
+
VISIBILITIES.each do |visibility|
|
36
|
+
[ @klass_or_module, @klass_or_module.singleton_class ].each do |klass|
|
37
|
+
methods = klass.send("#{ visibility }_instance_methods", false)
|
38
|
+
methods.each { |m| block.call(klass.instance_method(m)) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_all_files
|
44
|
+
source_locations = []
|
45
|
+
each_immediate_class_and_instance_method do |meth|
|
46
|
+
if location = meth.source_location
|
47
|
+
source_locations << location[0] unless source_locations.include?(location[0])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
source_locations
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_ancestor_name_formatter
|
54
|
+
proc do |ancestor, singleton|
|
55
|
+
ancestor_name(ancestor, singleton)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_all
|
60
|
+
ancestors = [] # flattened ancestors (both normal and singleton)
|
61
|
+
|
62
|
+
(@klass_or_module.ancestors - trite_ancestors).each do |ancestor|
|
63
|
+
ancestor_singleton = ancestor.singleton_class
|
64
|
+
|
65
|
+
# Modules don't inherit class methods from included modules
|
66
|
+
unless @klass_or_module.instance_of?(Module) && ancestor != @klass_or_module
|
67
|
+
class_methods = collect_instance_methods(ancestor_singleton)
|
68
|
+
end
|
69
|
+
|
70
|
+
instance_methods = collect_instance_methods(ancestor)
|
71
|
+
|
72
|
+
append_ancestor_entry(ancestors, @ancestor_name_formatter[ancestor, false],
|
73
|
+
class_methods, instance_methods)
|
74
|
+
|
75
|
+
(singleton_ancestors(ancestor) || []).each do |singleton_ancestor|
|
76
|
+
class_methods = collect_instance_methods(singleton_ancestor)
|
77
|
+
append_ancestor_entry(ancestors, @ancestor_name_formatter[singleton_ancestor, true],
|
78
|
+
class_methods)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
ancestors
|
83
|
+
end
|
84
|
+
|
85
|
+
# singleton ancestors with ancestor introduced
|
86
|
+
def singleton_ancestors(ancestor)
|
87
|
+
@singleton_ancestors ||= all_singleton_ancestors
|
88
|
+
@singleton_ancestors[ancestor]
|
89
|
+
end
|
90
|
+
|
91
|
+
def all_singleton_ancestors
|
92
|
+
all = {}
|
93
|
+
seen = []
|
94
|
+
(@klass_or_module.ancestors - trite_ancestors).reverse.each do |ancestor|
|
95
|
+
singleton_ancestors = ancestor.singleton_class.ancestors - trite_singleton_ancestors
|
96
|
+
introduces = singleton_ancestors - seen
|
97
|
+
all[ancestor] = introduces unless introduces.empty?
|
98
|
+
seen.concat singleton_ancestors
|
99
|
+
end
|
100
|
+
all
|
101
|
+
end
|
102
|
+
|
103
|
+
def ancestor_name(ancestor, singleton)
|
104
|
+
"#{ singleton ? "S" : ""}[#{ ancestor.is_a?(Class) ? "C" : "M" }] #{ ancestor.name || ancestor.to_s }"
|
105
|
+
end
|
106
|
+
|
107
|
+
# ancestor is included only when contributes some methods
|
108
|
+
def append_ancestor_entry(ancestors, ancestor, class_methods, instance_methods = nil)
|
109
|
+
if class_methods || instance_methods
|
110
|
+
ancestor_entry = {}
|
111
|
+
ancestor_entry[:class] = class_methods if class_methods
|
112
|
+
ancestor_entry[:instance] = instance_methods if instance_methods
|
113
|
+
ancestors << {ancestor => ancestor_entry}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns hash { :public => [...public methods...],
|
118
|
+
# :protected => [...private methods...],
|
119
|
+
# :private => [...private methods...] }
|
120
|
+
# keys with empty values are excluded,
|
121
|
+
# when no methods are found - returns nil
|
122
|
+
def collect_instance_methods(klass)
|
123
|
+
methods_with_visibility = VISIBILITIES.map do |visibility|
|
124
|
+
methods = klass.send("#{ visibility }_instance_methods", false)
|
125
|
+
[visibility, methods] unless methods.empty?
|
126
|
+
end.compact
|
127
|
+
Hash[methods_with_visibility] unless methods_with_visibility.empty?
|
128
|
+
end
|
129
|
+
|
130
|
+
def trite_singleton_ancestors
|
131
|
+
return [] unless @exclude_trite
|
132
|
+
@trite_singleton_ancestors ||= Class.new.singleton_class.ancestors
|
133
|
+
end
|
134
|
+
|
135
|
+
def trite_ancestors
|
136
|
+
return [] unless @exclude_trite
|
137
|
+
@trite_ancestors ||= Class.new.ancestors
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|