optionsful 0.1.5 → 0.1.6

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/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