optionsful 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -8,29 +8,50 @@ Provide dynamic information about resources via the OPTIONS' HTTP method on a RE
8
8
 
9
9
  h2. "-No! No! No! *-Show me the code!*"
10
10
 
11
+ h3. Retrieving an HTTP OPTIONS request via telnet:
12
+
11
13
  <pre>
12
14
 
13
- $ telnet kayaman.baurets.net 3000
14
- OPTIONS /notes HTTP/1.1
15
- Host: kayaman.baurets.net
15
+ $ telnet localhost 3000
16
+ OPTIONS /posts HTTP/1.1
17
+ Host: http://localhost:3000
16
18
 
17
19
  HTTP/1.1 204 No Content
18
20
  Allow: GET, POST
19
- Date: Sat, 10 Jul 2010 23:46:00 GMT
20
- Link: <http://baurets.net/api/resources>; type=text/html; rel=help
21
+ Connection: close
22
+ Date: Thu, 22 Jul 2010 17:20:27 GMT
23
+ Link: <http://localhost:3000/opts/posts>; type=text/html; rel=help
21
24
 
22
- OPTIONS /notes/1 HTTP/1.1
23
- Host: kayaman.baurets.net
25
+ OPTIONS /posts/1 HTTP/1.1
26
+ Host: http://localhost:3000
24
27
 
25
28
  HTTP/1.1 204 No Content
26
29
  Allow: GET, PUT, DELETE
27
- Date: Sat, 10 Jul 2010 23:47:47 GMT
28
- Link: <http://baurets.net/api/resources>; type=text/html; rel=help
30
+ Connection: close
31
+ Date: Thu, 22 Jul 2010 18:14:24 GMT
32
+ Link: <http://localhost:3000/opts/posts/1/>; type=text/html; rel=help
29
33
 
30
- </pre>
34
+ OPTIONS /posts/1/comments HTTP/1.1
35
+ Host: http://localhost:3000
36
+
37
+ HTTP/1.1 204 No Content
38
+ Allow: GET, POST
39
+ Connection: close
40
+ Date: Thu, 22 Jul 2010 18:12:43 GMT
41
+ Link: <http://localhost:3000/opts/posts/1/comments>; type=text/html; rel=help
31
42
 
43
+ </pre>
32
44
  ~Note the empty line which is part of the HTTP protocol.~
33
45
 
46
+ h3. Retrieving the resource/service documentation right from your browser:
47
+
48
+ * Access the given Link header through your preferable browser. Yeah, via an HTTP GET request.
49
+ * @http://localhost:3000/opts/posts/1/comments@ for instance.
50
+ * Try up adding some RDoc-like comments above your actions definitions on the appropriate controllers.
51
+ * Refresh the page and your documentation and have fun!
52
+ * There's a lot of helpful things we can do with this extracted 'on-the-fly' information (metadata).
53
+
54
+
34
55
  h2. INSTALLATION:
35
56
 
36
57
  # Change directory to your Ruby on Rails web application,
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
3
3
  require 'spec/rake/spectask'
4
4
 
5
5
  Spec::Rake::SpecTask.new(:spec) do |t|
6
- t.spec_files = Dir.glob( File.dirname(__FILE__) + '/spec/**/*_spec.rb')
6
+ t.spec_files = Dir.glob( File.dirname(__FILE__) + '/spec/**/*_spec.rb' )
7
7
  t.spec_opts << '--format specdoc'
8
8
  t.rcov = true
9
9
  end
@@ -0,0 +1,35 @@
1
+ module Baurets
2
+ module Optionsful
3
+
4
+ class Config
5
+
6
+ def initialize
7
+ # Initialize default settings- May be overriden if RAILS_ROOT/config/optionsful.yml exists.
8
+ @config = { :http => {:base_path => "/optionsful"} }
9
+ setup
10
+ end
11
+
12
+ def setup
13
+ require "yaml"
14
+ begin
15
+ yaml_file = File.join(RAILS_ROOT, "config", "optionsful.yml")
16
+ if File.exist? yaml_file
17
+ conf = YAML::load_file(yaml_file)[RAILS_ENV].symbolize_keys
18
+ configure(conf) if conf
19
+ end
20
+ rescue
21
+ end
22
+ end
23
+ def configure(conf)
24
+ @config[:http][:base_path] = conf[:http][:base_path] if (conf[:http] && conf[:http][:base_path])
25
+ end
26
+
27
+
28
+ def base_path
29
+ @config[:http][:base_path]
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,127 @@
1
+ module Baurets
2
+ module Optionsful
3
+ class Documentator
4
+
5
+ def initialize(app)
6
+ @app = app
7
+ @config = ::Baurets::Optionsful::Config.new
8
+ end
9
+
10
+ def call(env)
11
+ unless env["PATH_INFO"].index(@config.base_path) == 0
12
+ @app.call(env)
13
+ else
14
+ begin
15
+ return extract_documentation(env)
16
+ rescue => e
17
+ [500, {}, e.backtrace.join("\n")]
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def extract_documentation(env)
25
+ path = env["PATH_INFO"]
26
+ path = path.gsub(@config.base_path, '')
27
+ # do routing introspection:
28
+ routes = ::Baurets::Optionsful::Introspections.do_routing_introspection
29
+ # do request path investigation
30
+ route_guess = ::Baurets::Optionsful::Introspections.guess_route(routes,path)
31
+ # do the matches:
32
+ allow = ::Baurets::Optionsful::Introspections.do_the_matches(routes, route_guess)
33
+ http_methods = allow.split(", ")
34
+
35
+ path_parts = ::Baurets::Optionsful::Introspections.prepare_request_path(path)
36
+
37
+ controller_actions = []
38
+ http_methods.each do |verb|
39
+ controller_actions << [verb, relate_action_to_method(path_parts, verb)]
40
+ end
41
+ controller_actions.delete_if {|pair| pair[1].empty? }
42
+
43
+ controller_name = ::Baurets::Optionsful::Introspections.discover_controller_name(path_parts) + "_controller"
44
+ file = File.join(RAILS_ROOT, "app", "controllers", controller_name + ".rb")
45
+ controller_class = controller_name.camelize
46
+
47
+ service_doc = extract_comments_above(file, find_line_for(file, controller_class, :class)).join("\n")
48
+
49
+ methods_docs = []
50
+ controller_actions.each do |info|
51
+ methods_docs << [info, extract_comments_above(file, find_line_for(file, info[1], :method)).join("\n")]
52
+ end
53
+
54
+ body = "\n\nService: \n" + service_doc + "\n"
55
+ methods_docs.each do |md|
56
+ body += "\n\nMethod: #{md[0][0]} \n Action: #{md[0][1]} \n Metadata: \n #{md[1]}\n\n-- end ---\n"
57
+ end
58
+
59
+ [200, {}, "Under development!!! \n #{body}"]
60
+ end
61
+
62
+ def relate_action_to_method(path, verb)
63
+ action = ""
64
+ routes = ::Baurets::Optionsful::Introspections.do_routing_introspection
65
+ route_guess = ::Baurets::Optionsful::Introspections.guess_route(routes, path)
66
+ routes.each do |route|
67
+ if ((route.first == route_guess) && (route[1][0] == verb))
68
+ action = route[1][1] unless route[1][1].empty?
69
+ end
70
+ end
71
+ action
72
+ end
73
+
74
+ def file_lines(file_name)
75
+ lines = []
76
+ begin
77
+ file = File.new(file_name, "r")
78
+ while (line = file.gets)
79
+ line = line.strip
80
+ lines << line unless line.empty?
81
+ end
82
+ file.close
83
+ rescue => err
84
+ puts "Exception: #{err}"
85
+ err
86
+ end
87
+ lines.delete(nil)
88
+ lines
89
+ end
90
+
91
+ def extract_comments_above(file_name, line_number)
92
+ lines = file_lines(file_name)
93
+ doc = []
94
+ line_number = line_number -1
95
+ while ((line_number = line_number -1) && (line_number >= 0) && (!lines.nil?) && (!lines[line_number].empty?))
96
+ line = lines[line_number].lstrip
97
+ if line[0] == 35
98
+ doc << line
99
+ else
100
+ line_number = 0
101
+ end
102
+ end
103
+ doc.reverse
104
+ doc
105
+ end
106
+
107
+ def find_line_for(file, name, type)
108
+ lines = file_lines(file)
109
+ signature = ""
110
+ if type == :class
111
+ signature = "class " + name
112
+ elsif type == :method
113
+ signature = "def " + name
114
+ end
115
+ counter = 1;
116
+ lines.each do |line|
117
+ if line.include? signature
118
+ return counter
119
+ end
120
+ counter += 1
121
+ end
122
+ counter = 0
123
+ end
124
+ end
125
+
126
+ end
127
+ end
@@ -0,0 +1,79 @@
1
+ module Baurets
2
+ module Optionsful
3
+ module Introspections
4
+
5
+ def self.is_part_static?(routes, index, value)
6
+ routes.each do |route|
7
+ return true if route[0][index] == value
8
+ end
9
+ return false
10
+ end
11
+
12
+ def self.do_routing_introspection
13
+ returning Array.new do |routes|
14
+ route_requirements = nil
15
+ ActionController::Routing::Routes.routes.each do |route|
16
+ static_path = []
17
+ route.segments.each do |segment|
18
+ route_requirements = route.requirements #TODO validate
19
+ if segment.kind_of?(ActionController::Routing::StaticSegment)
20
+ static_path << segment.value if (segment.respond_to?(:value) && segment.value != "/")
21
+ elsif segment.kind_of?(ActionController::Routing::DynamicSegment)
22
+ static_path << :dynamic unless (segment.respond_to?(:key) && segment.key == :format)
23
+ end
24
+ end
25
+ routes << [static_path, [route.conditions[:method].to_s.upcase, route_requirements[:action]], route_requirements] unless route.conditions[:method].nil?
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.guess_route(routes, path)
31
+ guess = []
32
+ parts = prepare_request_path(path)
33
+ index = 0
34
+ parts.each do |part|
35
+ if is_part_static?(routes, index, part)
36
+ guess << part
37
+ else
38
+ guess << :dynamic
39
+ end
40
+ index += 1
41
+ end
42
+ guess
43
+ end
44
+
45
+ def self.discover_controller_name(path)
46
+ routes = do_routing_introspection
47
+ guess = guess_route(routes, path)
48
+ routes.each do |route|
49
+ if route[0] == guess
50
+ return route[2][:controller]
51
+ end
52
+ end
53
+ end
54
+
55
+ def self.do_the_matches(routes, route_guess)
56
+ allow = ""
57
+ routes.each do |route|
58
+ if route.first == route_guess
59
+ allow += route[1][0].to_s.upcase + "|"
60
+ end
61
+ end
62
+ allow = allow.split("|").join(", ")
63
+ end
64
+
65
+ def self.prepare_request_path(path)
66
+ path_parts = []
67
+ unless path.kind_of? Array
68
+ path = path[0..(path.rindex('.')-1)] if path.include?('.')
69
+ path_parts = path.split("/")
70
+ else
71
+ path_parts = path
72
+ end
73
+ path_parts.delete("")
74
+ path_parts
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,47 @@
1
+ module Baurets
2
+ module Optionsful
3
+ class Server
4
+
5
+ def initialize(app)
6
+ @app = app
7
+ @config = ::Baurets::Optionsful::Config.new
8
+ end
9
+
10
+ def call(env)
11
+ unless env["REQUEST_METHOD"] == "OPTIONS"
12
+ @app.call(env)
13
+ else
14
+ extract_options_information(env)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def extract_options_information(env)
21
+ allows = extract_allowed_methods(env)
22
+ if allows.empty?
23
+ [404, {}, "Not found."]
24
+ else
25
+ [204, {"Allow" => allows, "Link" => build_help_link(env)}, ""]
26
+ end
27
+ end
28
+
29
+ def extract_allowed_methods(env)
30
+ # do routing introspection:
31
+ routes = ::Baurets::Optionsful::Introspections.do_routing_introspection
32
+ # do request path investigation
33
+ path = env["PATH_INFO"]
34
+ route_guess = ::Baurets::Optionsful::Introspections.guess_route(routes, path)
35
+ # do the matches:
36
+ allow = ::Baurets::Optionsful::Introspections.do_the_matches(routes, route_guess)
37
+ end
38
+
39
+ def build_help_link(env)
40
+ server_name = env["SERVER_NAME"]
41
+ server_port = env["SERVER_PORT"]
42
+ "<http://#{server_name}:#{server_port}" + @config.base_path + "#{env["PATH_INFO"]}>; type=text/html; rel=help"
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -4,7 +4,7 @@ module Baurets
4
4
 
5
5
  MAJOR = 0
6
6
  MINOR = 1
7
- TINY = 5
7
+ TINY = 6
8
8
 
9
9
  def self.to_s
10
10
  "#{MAJOR}.#{MINOR}.#{TINY}"
data/lib/optionsful.rb CHANGED
@@ -1,57 +1,11 @@
1
- require File.join(File.dirname(__FILE__), 'introspections.rb')
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
2
2
 
3
+ require 'baurets/optionsful/config'
4
+ require 'baurets/optionsful/introspections'
5
+ require 'baurets/optionsful/server'
6
+ require 'baurets/optionsful/documentator'
3
7
 
4
- class Optionsful
5
-
8
+ module Optionsful
9
+
6
10
 
7
- def initialize(app)
8
- @app = app
9
- @doc = {:controller => nil, :actions => [] } # { :action => "index", :method => "GET" }, { :action => "create", :method => "POST" }
10
- end
11
-
12
- def call(env)
13
- unless env["REQUEST_METHOD"] == "OPTIONS"
14
- @app.call(env)
15
- else
16
- extract_options_information(env)
17
- end
18
- end
19
-
20
- private
21
-
22
- def extract_options_information(env)
23
- [204, {"Allow" => extract_allowed_methods(env), "Link" => build_help_link(env)}, ""]
24
- end
25
-
26
- def extract_allowed_methods(env)
27
- # do routing introspection:
28
- routes = Introspections.do_routing_introspection
29
- # do request path investigation
30
- route_guess = Introspections.guess_route(routes, env["PATH_INFO"])
31
- # do the matches:
32
- allow = do_the_matches(routes, route_guess)
33
- end
34
-
35
- private
36
-
37
- def do_the_matches(routes, route_guess)
38
- allow = ""
39
- routes.each do |route|
40
- if route.first == route_guess
41
- allow += route[1][0].to_s.upcase + "|"
42
- @doc[:controller] = route[2][:controller]
43
- @doc[:actions] << { :action => (route[2][:action]), :method => (route[1].to_s.upcase!) }
44
- end
45
- end
46
- allow = allow.split("|").join(", ")
47
- end
48
-
49
- def build_help_link(env)
50
- server_name = env["SERVER_NAME"]
51
- server_port = env["SERVER_PORT"]
52
- "<http://#{server_name}:#{server_port}/opts#{env["PATH_INFO"]}>; type=text/html; rel=help"
53
- end
54
-
55
-
56
-
57
11
  end
data/rails/init.rb CHANGED
@@ -1,15 +1,3 @@
1
1
  # Adding Optionsful to the Rack middleware stack:
2
- ActionController::Dispatcher.middleware.use Optionsful
3
- ActionController::Dispatcher.middleware.use OptionsfulDocs
4
-
5
-
6
- require 'rubygems'
7
- require 'yard'
8
- YARD::Templates::Engine.register_template_path File.dirname(__FILE__) + '/../templates'
9
-
10
- # Define custom tags
11
- YARD::Tags::Library.define_tag("URL for Service", :url)
12
- YARD::Tags::Library.define_tag("Topic for Service", :topic)
13
- YARD::Tags::Library.define_tag("Arguments", :argument, :with_types_and_name)
14
- YARD::Tags::Library.define_tag("Example Responses", :example_response)
15
- YARD::Tags::Library.define_tag("Response Fields", :response_field, :with_name)
2
+ ActionController::Dispatcher.middleware.use Baurets::Optionsful::Server
3
+ ActionController::Dispatcher.middleware.use Baurets::Optionsful::Documentator
@@ -1,24 +1,26 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
 
4
- describe "OptionsfulDocs" do
4
+ describe Baurets::Optionsful::Documentator do
5
5
 
6
6
  include Rack::Test::Methods
7
7
 
8
+ config = ::Baurets::Optionsful::Config.new
9
+ path = config.base_path
8
10
 
9
11
  describe "as a Rack middleware" do
10
12
 
11
13
  it "is a Ruby object that responds to call;" do
12
- assert OptionsfulDocs.new(app).respond_to? :call
14
+ assert ::Baurets::Optionsful::Documentator.new(app).respond_to? :call
13
15
  end
14
16
 
15
17
  it "takes exactly one argument, (the environment) and returns an Array;" do
16
- response = OptionsfulDocs.new(app).call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => "/opts/"}))
18
+ response = ::Baurets::Optionsful::Documentator.new(app).call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => path}))
17
19
  assert response.kind_of?(Array)
18
20
  end
19
21
 
20
22
  it "the returned Array must have exactly three values: the status, the headers and the body;" do
21
- response = OptionsfulDocs.new(app).call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => "/opts/"}))
23
+ response = ::Baurets::Optionsful::Documentator.new(app).call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => path}))
22
24
  assert response.size.should == 3
23
25
  assert response[0].kind_of? Fixnum
24
26
  assert response[1].kind_of? Hash
@@ -26,7 +28,7 @@ describe "OptionsfulDocs" do
26
28
  end
27
29
 
28
30
  it "must be nice, acting somewhere on a Rack middleware stack." do
29
- response = fake_docs_app.call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => "/opts/"}))
31
+ response = fake_docs_app.call(mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => "/lobster"}))
30
32
  assert response.size.should == 3
31
33
  assert response[0].kind_of? Fixnum
32
34
  assert response[0].should == 200
@@ -1,7 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/spec_helper'
2
2
 
3
3
 
4
- describe "Optionsful," do
4
+ describe Baurets::Optionsful::Server do
5
5
 
6
6
  include Rack::Test::Methods
7
7
 
@@ -15,16 +15,16 @@ describe "Optionsful," do
15
15
  describe "as a Rack middleware" do
16
16
 
17
17
  it "is a Ruby object that responds to call;" do
18
- assert Optionsful.new(app).respond_to? :call
18
+ assert ::Baurets::Optionsful::Server.new(app).respond_to? :call
19
19
  end
20
20
 
21
21
  it "takes exactly one argument, (the environment) and returns an Array;" do
22
- response = Optionsful.new(app).call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
22
+ response = ::Baurets::Optionsful::Server.new(app).call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
23
23
  assert response.kind_of?(Array)
24
24
  end
25
25
 
26
26
  it "the returned Array must have exactly three values: the status, the headers and the body;" do
27
- response = Optionsful.new(app).call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
27
+ response = ::Baurets::Optionsful::Server.new(app).call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
28
28
  assert response.size.should == 3
29
29
  assert response[0].kind_of? Fixnum
30
30
  assert response[1].kind_of? Hash
@@ -90,6 +90,16 @@ describe "Optionsful," do
90
90
  assert response.kind_of?(Array)
91
91
  assert allows?(response[1], "DELETE")
92
92
  end
93
+
94
+ it "not found a path" do
95
+ response = http_options_request("/sblingers/sblongers")
96
+ assert response.kind_of?(Array)
97
+ assert response[0].should == 404
98
+ end
99
+
100
+ it "not finding a path, pretend you're dead and.. let it goes through" do
101
+ pending
102
+ end
93
103
 
94
104
  end
95
105
 
data/spec/spec.opts CHANGED
@@ -1,4 +1,4 @@
1
- --colour
1
+ --color
2
2
  --format progress
3
3
  --loadby mtime
4
4
  --reverse
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,8 @@
1
- require File.join(File.dirname(__FILE__), '..', 'lib','optionsful.rb')
2
- require File.join(File.dirname(__FILE__), '..', 'lib','optionsful_docs.rb')
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "config.rb")
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "server.rb")
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "documentator.rb")
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', "baurets", 'optionsful', "introspections.rb")
5
+ # TODO ^ this was ugly?! :-S
3
6
 
4
7
  require 'rubygems'
5
8
  require 'sinatra'
@@ -41,7 +44,7 @@ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "
41
44
  app = Rack::Builder.new {
42
45
  use Rack::CommonLogger
43
46
  use Rack::ShowExceptions
44
- use Optionsful
47
+ use Baurets::Optionsful::Server
45
48
  map "/lobster" do
46
49
  use Rack::Lint
47
50
  run Rack::Lobster.new
@@ -53,7 +56,7 @@ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "
53
56
  app = Rack::Builder.new {
54
57
  use Rack::CommonLogger
55
58
  use Rack::ShowExceptions
56
- use OptionsfulDocs
59
+ use Baurets::Optionsful::Documentator
57
60
  map "/lobster" do
58
61
  use Rack::Lint
59
62
  run Rack::Lobster.new
@@ -67,7 +70,7 @@ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "
67
70
 
68
71
  def http_options_request(path)
69
72
  complex_env = mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => path })
70
- response = Optionsful.new(app).call(complex_env)
73
+ response = Baurets::Optionsful::Server.new(app).call(complex_env)
71
74
  end
72
75
 
73
76
  def allows?(headers, method)
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: optionsful
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 5
10
- version: 0.1.5
9
+ - 6
10
+ version: 0.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Marco Antonio Gonzalez Junior
@@ -30,17 +30,19 @@ extra_rdoc_files: []
30
30
  files:
31
31
  - README.textile
32
32
  - MIT-LICENSE
33
- - lib/optionsful.rb
33
+ - lib/baurets/optionsful/server.rb
34
34
  - rails/init.rb
35
35
  - lib/tasks/optionsful.rake
36
36
  - Rakefile
37
- - spec/optionsful_spec.rb
37
+ - spec/optionsful_server_spec.rb
38
38
  - spec/spec.opts
39
39
  - spec/spec_helper.rb
40
- - lib/optionsful_docs.rb
41
- - lib/introspections.rb
40
+ - lib/baurets/optionsful/documentator.rb
41
+ - lib/baurets/optionsful/introspections.rb
42
42
  - lib/baurets/optionsful/version.rb
43
- - spec/optionsful_docs_spec.rb
43
+ - lib/baurets/optionsful/config.rb
44
+ - lib/optionsful.rb
45
+ - spec/optionsful_documentator_spec.rb
44
46
  has_rdoc: true
45
47
  homepage: http://optionsful.rubyforge.org
46
48
  licenses: []
@@ -1,94 +0,0 @@
1
-
2
- module Introspections
3
-
4
-
5
-
6
- def self.guess_route(routes, path)
7
- parts = prepare_request_path(path)
8
- guess = []
9
- index = 0
10
- parts.each do |part|
11
- if is_part_static?(routes, index, part)
12
- guess << part
13
- else
14
- guess << :dynamic
15
- end
16
- index += 1
17
- end
18
- guess
19
- end
20
-
21
- def self.is_part_static?(routes, index, value)
22
- routes.each do |route|
23
- if route[0][index] == value
24
- return true
25
- end
26
- end
27
- return false
28
- end
29
-
30
- def self.do_routing_introspection
31
- routes = []
32
- route_requirements = nil
33
- ActionController::Routing::Routes.routes.each do |route|
34
- static_path = []
35
- route.segments.each do |segment|
36
- route_requirements = route.requirements #TODO validate
37
- if segment.kind_of?(ActionController::Routing::StaticSegment)
38
- static_path << segment.value if (segment.respond_to?(:value) && segment.value != "/")
39
- elsif segment.kind_of?(ActionController::Routing::DynamicSegment)
40
- static_path << :dynamic unless (segment.respond_to?(:key) && segment.key == :format)
41
- end
42
- end
43
- routes << [static_path, [route.conditions[:method].to_s.upcase, route_requirements[:action]], route_requirements] unless route.conditions[:method].nil?
44
- end
45
- routes
46
- end
47
-
48
- def self.guess_route(routes, path)
49
- parts = prepare_request_path(path)
50
- guess = []
51
- index = 0
52
- parts.each do |part|
53
- if is_part_static?(routes, index, part)
54
- guess << part
55
- else
56
- guess << :dynamic
57
- end
58
- index += 1
59
- end
60
- guess
61
- end
62
-
63
- def self.discover_controller_name(path)
64
- routes = do_routing_introspection
65
- guess = guess_route(routes, path)
66
- routes.each do |route|
67
- if route[0] == guess
68
- return route[2][:controller]
69
- end
70
- end
71
-
72
- end
73
-
74
- def self.do_the_matches(routes, route_guess)
75
- allow = ""
76
- routes.each do |route|
77
- if route.first == route_guess
78
- allow += route[1][0].to_s.upcase + "|"
79
- end
80
- end
81
- allow = allow.split("|").join(", ")
82
- end
83
-
84
- def self.prepare_request_path(path)
85
- unless path.kind_of? Array
86
- path = path[0..(path.rindex('.')-1)] if path.include?('.')
87
- path_parts = path.split("/")
88
- else
89
- path_parts = path
90
- end
91
- path_parts.delete("")
92
- path_parts
93
- end
94
- end
@@ -1,129 +0,0 @@
1
- require 'rubygems'
2
- require 'yard'
3
- require 'rdoc/rdoc'
4
- require 'fileutils'
5
- require File.join(File.dirname(__FILE__), 'introspections.rb')
6
-
7
- class OptionsfulDocs
8
-
9
-
10
- def initialize(app)
11
- @app = app
12
- end
13
-
14
- def call(env)
15
- unless env["PATH_INFO"].index("/opts/") == 0
16
- @app.call(env)
17
- else
18
- extract_documentation(env)
19
- end
20
- end
21
-
22
- def extract_documentation(env)
23
-
24
- parts = env["PATH_INFO"].split("/")
25
- parts.delete("")
26
- parts.delete("opts")
27
- # do routing introspection:
28
- routes = Introspections.do_routing_introspection
29
- # do request path investigation
30
- path = parts.join("/")
31
- puts "\n #{path} \n"
32
- route_guess = Introspections.guess_route(routes,path)
33
- # do the matches:
34
- allow = Introspections.do_the_matches(routes, route_guess)
35
- http_methods = allow.split(", ")
36
-
37
- controller_actions = []
38
- http_methods.each do |verb|
39
- controller_actions << [verb, relate_action_to_method(parts, verb)]
40
- end
41
-
42
- controller_actions.delete_if {|pair| pair[1].empty? }
43
- puts "controller_actions: #{controller_actions.inspect}"
44
- controller_name = Introspections.discover_controller_name(parts) + "_controller"
45
- file = File.join(RAILS_ROOT, "app", "controllers", controller_name + ".rb")
46
- controller_class = controller_name.camelize
47
-
48
- service_doc = extract_comments_above(file, find_line_for(file, controller_class, :class)).join("\n")
49
-
50
- methods_docs = []
51
- controller_actions.each do |info|
52
- methods_docs << [info, extract_comments_above(file, find_line_for(file, info[1], :method)).join("\n")]
53
- end
54
-
55
- body = "\n\nService: \n" + service_doc + "\n"
56
- methods_docs.each do |md|
57
- body += "\n\nMethod: #{md[0][0]} \n Action: #{md[0][1]} \n Metadata: \n #{md[1]}\n\n-- end ---\n"
58
- end
59
-
60
- [200, {}, "Under development!!! \n #{body}"]
61
- end
62
-
63
- def relate_action_to_method(path, verb)
64
- action = ""
65
- routes = Introspections.do_routing_introspection
66
- route_guess = Introspections.guess_route(routes, path)
67
- routes.each do |route|
68
- if ((route.first == route_guess) && (route[1][0] == verb))
69
- action = route[1][1] unless route[1][1].empty?
70
- end
71
- end
72
- action
73
- end
74
-
75
-
76
-
77
- def file_lines(file_name)
78
- lines = []
79
- begin
80
- file = File.new(file_name, "r")
81
- while (line = file.gets)
82
- line = line.strip
83
- lines << line unless line.empty?
84
- end
85
- file.close
86
- rescue => err
87
- puts "Exception: #{err}"
88
- err
89
- end
90
- lines.delete(nil)
91
- lines
92
- end
93
-
94
- def extract_comments_above(file_name, line_number)
95
- lines = file_lines(file_name)
96
- doc = []
97
- line_number = line_number -1
98
- while ((line_number = line_number -1) && (line_number >= 0) && (!lines.nil?) && (!lines[line_number].empty?))
99
- line = lines[line_number].lstrip
100
- if line[0] == 35
101
- doc << line
102
-
103
- else
104
- line_number = 0
105
- end
106
- end
107
- doc.reverse
108
- puts doc.inspect
109
- doc
110
- end
111
-
112
- def find_line_for(file, name, type)
113
- lines = file_lines(file)
114
- signature = ""
115
- if type == :class
116
- signature = "class " + name
117
- elsif type == :method
118
- signature = "def " + name
119
- end
120
- counter = 1;
121
- lines.each do |line|
122
- if line.include? signature
123
- return counter
124
- end
125
- counter += 1
126
- end
127
- end
128
-
129
- end