reflexive 0.0.6 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -2,15 +2,17 @@ source "http://rubygems.org"
2
2
 
3
3
  # keeping this in sync with reflexive.gemspec manually
4
4
 
5
- gem "rack"
6
- gem "sinatra"
7
- gem "sinatra_more"
8
- gem "coderay"
9
- gem "rdiscount"
5
+ gem "rack", "1.1.0"
6
+ gem "sinatra", "1.0"
7
+ gem "sinatra_more", "0.3.40"
8
+ gem "coderay", "0.9.3"
9
+ # gem "rdiscount"
10
10
 
11
11
  group :development do
12
12
  gem "rails", "3.0.0.beta3"
13
13
  gem "rspec", "2.0.0.beta.8"
14
- gem "sinatra-reloader"
15
- gem "thin"
14
+ gem "sinatra-reloader", "0.4.1"
15
+ gem "thin", "1.2.7"
16
+ gem "rack-test", "0.5.3"
17
+ gem "webrat", "0.7.1"
16
18
  end
data/Rakefile CHANGED
@@ -8,14 +8,20 @@ end
8
8
  GEM_NAME = "reflexive"
9
9
 
10
10
  desc "Relese next version of reflexive gem (do that just after `git commit')"
11
- task :release do
11
+ task :release => :spec do
12
12
  require "rubygems"
13
13
  require "rubygems/version"
14
14
  require "yaml"
15
15
 
16
- current_version = YAML.load(`gem specification #{ GEM_NAME } -r`)["version"] || Gem::Version.new("0.0.0")
17
- new_version = (current_version.segments[0..-2] + [current_version.segments[-1].succ]).join(".")
18
- ENV["GEM_VERSION"] = new_version
16
+ if ENV["GEM_VERSION"]
17
+ # release version passed in ENV["GEM_VERSION"]
18
+ new_version = ENV["GEM_VERSION"]
19
+ else
20
+ # by default release next minor version
21
+ current_version = YAML.load(`gem specification #{ GEM_NAME } -r`)["version"] || Gem::Version.new("0.0.0")
22
+ new_version = (current_version.segments[0..-2] + [current_version.segments[-1].succ]).join(".")
23
+ ENV["GEM_VERSION"] = new_version
24
+ end
19
25
 
20
26
  puts "Releasing #{ GEM_NAME } #{ new_version }"
21
27
 
data/config.ru CHANGED
@@ -1,3 +1,3 @@
1
- require File.expand_path("../lib/reflexive/application", __FILE__)
1
+ require ::File.expand_path("../lib/reflexive/application", __FILE__)
2
2
 
3
3
  run Reflexive::Application
@@ -12,10 +12,12 @@ require "reflexive/columnizer"
12
12
  require "reflexive/constantize"
13
13
  require "reflexive/descendants"
14
14
  require "reflexive/methods"
15
+ require "reflexive/method_lookup"
15
16
 
16
17
  if ENV["SINATRA_RELOADER"]
17
18
  require "rails/all"
18
19
  require "arel"
20
+ require File.expand_path("../../../spec/integration_spec_fixture", __FILE__)
19
21
 
20
22
  module ::Kernel
21
23
  def r(*args)
@@ -60,10 +62,11 @@ module Reflexive
60
62
  end
61
63
 
62
64
  action "constant_lookup" do
63
- if klass = Reflexive.constant_lookup(*params.values_at(:name, :scope))
65
+ if (klass = Reflexive.constant_lookup(*params.values_at(:name, :scope))) &&
66
+ (klass.instance_of?(Class) || klass.instance_of?(Module))
64
67
  redirect(constant_path(klass.to_s))
65
68
  else
66
- e "failed to lookup constant `#{ params[:name] }' in scope #{ params[:scope] }"
69
+ e "failed to lookup class/module with name `#{ params[:name] }' in scope #{ params[:scope] }"
67
70
  end
68
71
  end
69
72
 
@@ -87,54 +90,71 @@ module Reflexive
87
90
  end
88
91
  end
89
92
 
90
- get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)/definition> do |klass, method|
93
+ def definition_action(klass, level, name)
91
94
  find_klass(klass)
92
- @method_name = method
93
- @path, @line = @klass.method(@method_name).source_location
94
- @source = highlight_file(@path, :highlight_lines => [@line])
95
- erb :methods_definition
95
+ @method_name = name
96
+ @path, @line = @klass.send(level == :class ? :method : :instance_method, @method_name).source_location
97
+ if @path.include?("(eval)")
98
+ e "#{ name } #{ level } method was generated using `eval' function and can't be browsed"
99
+ else
100
+ @source = highlight_file(@path, :highlight_lines => [@line])
101
+ erb :methods_definition
102
+ end
103
+ end
104
+
105
+ get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)/definition> do |klass, method|
106
+ definition_action(klass, :class, method)
96
107
  end
97
108
 
98
109
  get %r</reflexive/constants/([^/&#]+)/instance_methods/([^/&#]+)/definition> do |klass, method|
110
+ definition_action(klass, :instance, method)
111
+ end
112
+
113
+ get %r</reflexive/constants/([^/&#]+)/instance_methods/([^/&#]+)/apidock> do |klass, method|
99
114
  find_klass(klass)
100
115
  @method_name = method
101
- @path, @line = @klass.instance_method(@method_name).source_location
102
- @source = highlight_file(@path, :highlight_lines => [@line])
103
- erb :methods_definition
116
+ @level = :instance
117
+ erb :methods_apidock
104
118
  end
105
119
 
106
- get %r</reflexive/constants/([^/&#]+)/methods/([^/&#]+)/apidock> do |klass, method|
120
+ get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)/apidock> do |klass, method|
107
121
  find_klass(klass)
108
122
  @method_name = method
123
+ @level = :class
109
124
  erb :methods_apidock
110
125
  end
111
126
 
112
- get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)> do |klass, method|
113
- find_klass(klass)
114
- begin
115
- if @klass.method(method).source_location
116
- redirect(class_method_definition_path(klass, method) +
117
- "#highlighted")
127
+ def method_lookup_action(klass, level, name)
128
+ lookup = MethodLookup.new(klass: klass, level: level, name: name)
129
+ if definitions = lookup.definitions
130
+ if definitions.size == 1
131
+ redirect(new_method_definition_path(*definitions[0]) + "#highlighted")
132
+ else
133
+ @definitions, @klass, @level, @name, @last_resort_lookup_used =
134
+ definitions, klass, level, name, lookup.last_resort_lookup_used?
135
+ erb :methods_choose
136
+ end
137
+ elsif documentations = lookup.documentations
138
+ if documentations.size == 1
139
+ redirect(method_documentation_path(*documentations[0]))
118
140
  else
119
- redirect(method_documentation_path(klass, method))
141
+ raise ArgumentError, "don't know how to handle multiple documentations"
120
142
  end
121
- rescue NameError
122
- e "failed to find `#{ method }' class method for #{ klass }"
143
+ else
144
+ e "failed to find `#{ name }' #{ level } method for #{ klass }"
123
145
  end
146
+ #
147
+ # e "failed to find `#{ method }' instance method for #{ klass }"
148
+ end
149
+
150
+ get %r</reflexive/constants/([^/&#]+)/class_methods/([^/&#]+)> do |klass, method|
151
+ find_klass(klass)
152
+ method_lookup_action(@klass, :class, method)
124
153
  end
125
154
 
126
155
  get %r</reflexive/constants/([^/&#]+)/instance_methods/([^/&#]+)> do |klass, method|
127
156
  find_klass(klass)
128
- begin
129
- if @klass.instance_method(method).source_location
130
- redirect(instance_method_definition_path(klass, method) +
131
- "#highlighted")
132
- else
133
- redirect(method_documentation_path(klass, method))
134
- end
135
- rescue NameError
136
- e "failed to find `#{ method }' instance method for #{ klass }"
137
- end
157
+ method_lookup_action(@klass, :instance, method)
138
158
  end
139
159
 
140
160
  get %r</reflexive/constants/([^/&#]+)> do |klass|
@@ -33,7 +33,7 @@ module Reflexive
33
33
  super(text, type)
34
34
  @out << "</a>"
35
35
  else
36
- super(text, type) rescue raise([text, type].inspect)
36
+ super(text, type)
37
37
  end
38
38
  end
39
39
 
@@ -49,6 +49,7 @@ module Reflexive
49
49
  in_backtick = false
50
50
  in_symbol = false
51
51
  in_embexpr_nesting = 0
52
+
52
53
  scanner_events.each do |scanner_event|
53
54
  token_val, event, tags =
54
55
  ReflexiveRipper.destruct_scanner_event(scanner_event)
@@ -107,6 +108,7 @@ module Reflexive
107
108
  # }
108
109
  # ;
109
110
  @coderay_tokens << [token_val, :content]
111
+ @coderay_tokens.last << tags if tags
110
112
  @coderay_tokens << [:close, :symbol]
111
113
  in_symbol = false
112
114
  elsif ripper_token == :regexp_beg
@@ -0,0 +1,44 @@
1
+ class Module
2
+ if RUBY_VERSION > '1.9.1'
3
+ alias reflexive_public_instance_methods public_instance_methods
4
+ alias reflexive_private_instance_methods private_instance_methods
5
+ alias reflexive_protected_instance_methods protected_instance_methods
6
+ else
7
+ # Try to workaround 1.9.1 *_instance_methods issues
8
+ def reflexive_public_instance_methods(inc_super = true)
9
+ if inc_super
10
+ public_instance_methods(true)
11
+ else
12
+ methods = public_instance_methods(false)
13
+ methods -= Module.public_instance_methods(false) unless self === Module
14
+ methods -= Class.public_instance_methods(false) unless self === Class
15
+ # ancestors.each do |ancestor|
16
+ # methods -= ancestor.public_instance_methods(false)
17
+ # end
18
+ methods
19
+ end
20
+ end
21
+
22
+ def reflexive_protected_instance_methods(inc_super = true)
23
+ if inc_super
24
+ protected_instance_methods(true)
25
+ else
26
+ methods = protected_instance_methods(false)
27
+ methods -= Module.protected_instance_methods(false) unless self === Module
28
+ methods -= Class.protected_instance_methods(false) unless self === Class
29
+ methods
30
+ end
31
+ end
32
+
33
+ def reflexive_private_instance_methods(inc_super = true)
34
+ if inc_super
35
+ private_instance_methods(true)
36
+ else
37
+ methods = private_instance_methods(false)
38
+ methods -= Module.private_instance_methods(false) unless self === Module
39
+ methods -= Class.private_instance_methods(false) unless self === Class
40
+ methods
41
+ end
42
+ end
43
+ end
44
+ end
@@ -34,8 +34,18 @@ module Reflexive
34
34
  ancestors = c.singleton_class.ancestors[0 .. (generations || -1)]
35
35
  subclass << c if ancestors.include?(klass)
36
36
  end
37
-
38
- subclass
37
+
38
+ if klass.instance_of?(Module)
39
+ # descendants of module are also modules which include the module
40
+ ObjectSpace.each_object(Module) do |c|
41
+ next if c == klass
42
+
43
+ ancestors = c.ancestors[0 .. (generations || -1)]
44
+ subclass << c if ancestors.include?(klass)
45
+ end
46
+ end
47
+
48
+ subclass.uniq
39
49
  end
40
50
  end
41
51
 
@@ -3,15 +3,26 @@ require "reflexive/coderay_ruby_scanner"
3
3
  require "reflexive/coderay_html_encoder"
4
4
 
5
5
  module Reflexive
6
+ def self.class_reloading_active?
7
+ if defined?(Rails)
8
+ !Rails.configuration.cache_classes rescue false
9
+ else
10
+ false
11
+ end
12
+ end
13
+
6
14
  FILE_EXT = /\.\w+\z/
7
15
  DL_EXT = /\.s?o\z/
8
16
  def self.load_path_lookup(path)
9
17
  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))
18
+
19
+ $LOAD_PATH.each do |load_path|
20
+ if feature = File.join(load_path, path_with_rb_ext)
21
+ return feature if File.exists?(feature)
22
+ end
13
23
  end
14
- feature
24
+
25
+ nil
15
26
  end
16
27
 
17
28
  def self.loaded_features_lookup(path)
@@ -48,11 +59,11 @@ module Reflexive
48
59
  tokens = CodeRayRubyScanner.new(src).tokenize
49
60
  encoder = CodeRayHtmlEncoder.new(options)
50
61
  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
62
+ # elsif path =~ /\.(markdown|md)$/
63
+ # require "rdiscount"
64
+ # src = IO.read(path)
65
+ # markdown = RDiscount.new(src)
66
+ # markdown.to_html
56
67
  else
57
68
  CodeRay.scan_file(path).html(options).div
58
69
  end
@@ -169,6 +180,12 @@ module Reflexive
169
180
  :title => (method_name if link_text.include?("...")))
170
181
  end
171
182
 
183
+ def full_name_link_to_method(constant, level, method_name)
184
+ link_text = "#{ constant }#{ level == :instance ? "#" : "." }#{ method_name }"
185
+ link_to(Rack::Utils.escape_html(link_text),
186
+ new_method_path(constant, level, method_name))
187
+ end
188
+
172
189
  ##
173
190
  # Truncates a given text after a given :length if text is longer than :length (defaults to 30).
174
191
  # The last characters will be replaced with the :omission (defaults to "в_│") for a total length not exceeding :length.
@@ -0,0 +1,199 @@
1
+ require "reflexive/constantize"
2
+
3
+ module Reflexive
4
+ class MethodLookup
5
+ def initialize(options)
6
+ unless (@klass, @level, @name = options.values_at(:klass, :level, :name)).all?
7
+ raise ArgumentError, "must pass :klass, :level, :name as named arguments"
8
+ end
9
+ @level, @name = @level.to_sym, @name.to_sym
10
+ end
11
+
12
+ def definitions
13
+ lookup unless @lookup_done
14
+ @definitions
15
+ end
16
+
17
+ def documentations
18
+ lookup unless @lookup_done
19
+ @documentations
20
+ end
21
+
22
+ def last_resort_lookup_used?
23
+ @last_resort_lookup_used
24
+ end
25
+
26
+ private
27
+
28
+ def lookup
29
+ begin
30
+ defined_method_lookup
31
+ rescue NameError => e
32
+ # don't swallow NameError if it's not related to the method we're looking for
33
+ raise unless e.message.include?(@name.to_s)
34
+ heuristic_lookup
35
+ last_resort_lookup unless lookup_succeed?
36
+ end
37
+ @lookup_done = true
38
+ end
39
+
40
+ def lookup_succeed?
41
+ @definitions && @definitions.size > 0 ||
42
+ @documentations && @documentations.size > 0
43
+ end
44
+
45
+ def defined_method_lookup
46
+ unbound_method = @klass.send(method_getter, @name)
47
+ if unbound_method.source_location
48
+ @definitions = [[@klass, @level, @name]]
49
+ elsif @klass.instance_of?(Class) && @level == :class && @name == :new &&
50
+ (@klass.instance_method(:initialize).source_location rescue false)
51
+ @definitions = [[@klass, :instance, :initialize]]
52
+ elsif core_klass = unbound_method.owner
53
+ if core_klass == Kernel
54
+ @documentations = [[Kernel, :instance, @name]]
55
+ elsif core_klass == Module || core_klass.to_s == "#<Class:Module>"
56
+ if (Module.methods(false) + Module.private_methods(false)).include?(@name)
57
+ @documentations = [[Module, :class, @name]]
58
+ else
59
+ @documentations = [[Module, :instance, @name]]
60
+ end
61
+ elsif core_klass == Class
62
+ if (Class.methods(false) + Class.private_methods(false)).include?(@name)
63
+ @documentations = [[Class, :class, @name]]
64
+ else
65
+ @documentations = [[Class, :instance, @name]]
66
+ end
67
+ else
68
+ if @level == :class
69
+ if core_klass == @klass
70
+ @documentations = [[core_klass, :class, @name]]
71
+ elsif core_klass.to_s =~ /^#<Class:(.+)>$/
72
+ # get class from singleton class
73
+ @documentations = [[Reflexive.constantize($1), :class, @name]]
74
+ end
75
+ else
76
+ @documentations = [[core_klass, @level, @name]]
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ def heuristic_lookup
83
+ if @klass.instance_of?(Module) && @level == :instance
84
+ # only instance methods are inherited from modules
85
+ potential_receivers = included_by_classes.select do |included_by_class|
86
+ all_instance_methods(included_by_class).include?(@name)
87
+ end
88
+
89
+ potential_receivers += included_by_modules.select do |included_by_module|
90
+ all_instance_methods(included_by_module).include?(@name)
91
+ end
92
+
93
+ potential_receivers.uniq.each do |receiver|
94
+ # TODO heuristic lookup shouldn't assume that found method is not core method
95
+ (@definitions ||= []) << [receiver, :instance, @name]
96
+ end
97
+
98
+ potential_class_receivers = included_by_singleton_classes.select do |included_by_singleton_class|
99
+ all_class_methods(included_by_singleton_class).include?(@name)
100
+ end
101
+
102
+ potential_class_receivers.uniq.each do |class_receiver|
103
+ (@definitions ||= []) << [class_receiver, :class, @name]
104
+ end
105
+ elsif @klass.instance_of?(Class)
106
+ # both instance and class methods are inherited by classes
107
+
108
+ potential_receivers = inherited_by_classes.select do |inherited_by_class|
109
+ all_level_methods(inherited_by_class).include?(@name)
110
+ end
111
+
112
+ potential_receivers.uniq.each do |receiver|
113
+
114
+ (@definitions ||= []) << [receiver, @level, @name]
115
+ end
116
+ end
117
+ end
118
+
119
+ def last_resort_lookup
120
+ @last_resort_lookup_used = true
121
+ seen = []
122
+ [Module, Class].each do |module_or_class|
123
+ ObjectSpace.each_object(module_or_class) do |m|
124
+ next if seen.include?(m)
125
+ if all_instance_methods(m).include?(@name)
126
+ (@definitions ||= []) << [m, :instance, @name]
127
+ end
128
+ if all_class_methods(m).include?(@name)
129
+ (@definitions ||= []) << [m, :class, @name]
130
+ end
131
+ seen << m
132
+ end
133
+ end
134
+ end
135
+
136
+ def all_instance_methods(klass)
137
+ klass.instance_methods(false) + klass.private_instance_methods(false)
138
+ end
139
+
140
+ def all_class_methods(klass)
141
+ klass.methods(false) + klass.private_methods(false)
142
+ end
143
+
144
+ def all_level_methods(klass)
145
+ @level == :instance ? all_instance_methods(klass) : all_class_methods(klass)
146
+ end
147
+
148
+ def included_by_classes
149
+ included_by = []
150
+ ObjectSpace.each_object(Class) do |c|
151
+ next if c == @klass
152
+
153
+ ancestors = c.ancestors
154
+ included_by << c if ancestors.include?(@klass)
155
+ end
156
+ included_by
157
+ end
158
+
159
+ alias inherited_by_classes included_by_classes
160
+
161
+ def included_by_modules
162
+ included_by = []
163
+ ObjectSpace.each_object(Module) do |m|
164
+ next if m == @klass
165
+
166
+ ancestors = m.ancestors
167
+ included_by << m if ancestors.include?(@klass)
168
+ end
169
+ included_by
170
+ end
171
+
172
+ def included_by_singleton_classes
173
+ included_by = []
174
+ ObjectSpace.each_object(Class) do |c|
175
+ next if c == @klass
176
+
177
+ ancestors = c.singleton_class.ancestors
178
+ included_by << c if ancestors.include?(@klass)
179
+ end
180
+ included_by
181
+ end
182
+
183
+ def method_getter
184
+ @level == :instance ? :instance_method : :method
185
+ end
186
+
187
+ def methods_getter
188
+ @level == :instance ? :instance_methods : :methods
189
+ end
190
+
191
+ def reverse_methods_getter
192
+ @level == :instance ? :methods : :instance_methods
193
+ end
194
+
195
+ def reverse_level
196
+ @level == :instance ? :class : :instance
197
+ end
198
+ end
199
+ end