reflexive 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|