optionsful 0.1.2 → 0.1.5

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.
@@ -0,0 +1,16 @@
1
+ module Baurets
2
+ module Optionsful
3
+ module Version
4
+
5
+ MAJOR = 0
6
+ MINOR = 1
7
+ TINY = 5
8
+
9
+ def self.to_s
10
+ "#{MAJOR}.#{MINOR}.#{TINY}"
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,94 @@
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
data/lib/optionsful.rb CHANGED
@@ -1,90 +1,57 @@
1
+ require File.join(File.dirname(__FILE__), 'introspections.rb')
1
2
 
2
- class Optionsful
3
3
 
4
- def initialize(app)
5
- @app = app
6
- end
4
+ class Optionsful
7
5
 
8
- def call(env)
9
- unless env["REQUEST_METHOD"] == "OPTIONS"
10
- @app.call(env)
11
- else
12
- extract_options_information(env)
13
- end
14
- end
15
-
16
- private
6
+
7
+ def initialize(app)
8
+ @app = app
9
+ @doc = {:controller => nil, :actions => [] } # { :action => "index", :method => "GET" }, { :action => "create", :method => "POST" }
10
+ end
17
11
 
18
- def extract_options_information(env)
19
- [204, {"Allow" => extract_allowed_methods(env), "Link" => build_help_link}, ""]
12
+ def call(env)
13
+ unless env["REQUEST_METHOD"] == "OPTIONS"
14
+ @app.call(env)
15
+ else
16
+ extract_options_information(env)
20
17
  end
18
+ end
21
19
 
22
- def extract_allowed_methods(env)
23
- # do routing introspection:
24
- routes = do_routing_introspection
25
- # do request path investigation
26
- route_guess = guess_route(routes, env["PATH_INFO"])
27
- allows = ""
28
- # do the matches:
29
- routes.each do |route|
30
- if route.first == route_guess
31
- allows += route[1].to_s.upcase! + "|"
32
- end
33
- end
34
- allows = allows.split("|").join(", ")
35
- end
20
+ private
36
21
 
37
- def build_help_link
38
- #PENDING
39
- "<http://baurets.net/api/resources>; type=text/html; rel=help"
40
- end
22
+ def extract_options_information(env)
23
+ [204, {"Allow" => extract_allowed_methods(env), "Link" => build_help_link(env)}, ""]
24
+ end
41
25
 
42
- def guess_route(routes, path)
43
- parts = prepare_request_path(path)
44
- guess = []
45
- index = 0
46
- parts.each do |part|
47
- if is_part_static?(routes, index, part)
48
- guess << part
49
- else
50
- guess << :dynamic
51
- end
52
- index += 1
53
- end
54
- guess
55
- end
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
56
34
 
57
- def is_part_static?(routes, index, value)
58
- routes.each do |route|
59
- if route[0][index] == value
60
- return true
61
- end
62
- end
63
- return false
64
- end
35
+ private
65
36
 
66
- def do_routing_introspection
67
- routes = []
68
- ActionController::Routing::Routes.routes.each do |route|
69
- static_path = []
70
- route.segments.each do |segment|
71
- if segment.kind_of?(ActionController::Routing::StaticSegment)
72
- static_path << segment.value if (segment.respond_to?(:value) && segment.value != "/")
73
- elsif segment.kind_of?(ActionController::Routing::DynamicSegment)
74
- static_path << :dynamic unless (segment.respond_to?(:key) && segment.key == :format)
75
- end
76
- end
77
- routes << [static_path, route.conditions[:method]] unless route.conditions[:method].nil?
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!) }
78
44
  end
79
- routes
80
- end
81
-
82
- def prepare_request_path(path)
83
- path = path[0..(path.rindex('.')-1)] if path.include?('.')
84
- path_parts = path.split("/")
85
- path_parts.delete("")
86
- path_parts
87
45
  end
46
+ allow = allow.split("|").join(", ")
47
+ end
88
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"
89
53
  end
90
54
 
55
+
56
+
57
+ end
@@ -0,0 +1,129 @@
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
data/rails/init.rb CHANGED
@@ -1,2 +1,15 @@
1
1
  # Adding Optionsful to the Rack middleware stack:
2
- ActionController::Dispatcher.middleware.use Optionsful
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)
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+
4
+ describe "OptionsfulDocs" do
5
+
6
+ include Rack::Test::Methods
7
+
8
+
9
+ describe "as a Rack middleware" do
10
+
11
+ it "is a Ruby object that responds to call;" do
12
+ assert OptionsfulDocs.new(app).respond_to? :call
13
+ end
14
+
15
+ 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/"}))
17
+ assert response.kind_of?(Array)
18
+ end
19
+
20
+ 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/"}))
22
+ assert response.size.should == 3
23
+ assert response[0].kind_of? Fixnum
24
+ assert response[1].kind_of? Hash
25
+ assert response[2].kind_of? String
26
+ end
27
+
28
+ 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/"}))
30
+ assert response.size.should == 3
31
+ assert response[0].kind_of? Fixnum
32
+ assert response[0].should == 200
33
+ assert response[1].kind_of? Hash
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -32,7 +32,7 @@ describe "Optionsful," do
32
32
  end
33
33
 
34
34
  it "must be nice, acting somewhere on a Rack middleware stack." do
35
- response = fake_app.call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
35
+ response = fake_opts_app.call(mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => "/posts"}))
36
36
  assert response.size.should == 3
37
37
  assert response[0].kind_of? Fixnum
38
38
  assert response[0].should == 204
@@ -101,7 +101,7 @@ describe "Optionsful," do
101
101
 
102
102
  it " method GET example" do
103
103
  complex_env = mock_env({"REQUEST_METHOD" => "GET", "PATH_INFO" => "/lobster" })
104
- response = fake_app.call(complex_env)
104
+ response = fake_opts_app.call(complex_env)
105
105
  assert response.kind_of?(Array)
106
106
  assert response.size.should == 3
107
107
  end
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require File.join(File.dirname(__FILE__), '..', 'lib','optionsful.rb')
2
+ require File.join(File.dirname(__FILE__), '..', 'lib','optionsful_docs.rb')
2
3
 
3
4
  require 'rubygems'
4
5
  require 'sinatra'
@@ -9,8 +10,7 @@ require 'spec'
9
10
  require 'spec/autorun'
10
11
  require 'spec/interop/test'
11
12
  require 'action_controller'
12
- require 'ruby-debug'
13
- # require 'baurets/optionsful'
13
+
14
14
 
15
15
  # set test environment set :environment, :test set :run, false set :raise_errors, true set :logging, false
16
16
 
@@ -37,7 +37,7 @@ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "
37
37
  end
38
38
 
39
39
 
40
- def fake_app
40
+ def fake_opts_app
41
41
  app = Rack::Builder.new {
42
42
  use Rack::CommonLogger
43
43
  use Rack::ShowExceptions
@@ -48,6 +48,18 @@ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "
48
48
  end
49
49
  }
50
50
  end
51
+
52
+ def fake_docs_app
53
+ app = Rack::Builder.new {
54
+ use Rack::CommonLogger
55
+ use Rack::ShowExceptions
56
+ use OptionsfulDocs
57
+ map "/lobster" do
58
+ use Rack::Lint
59
+ run Rack::Lobster.new
60
+ end
61
+ }
62
+ end
51
63
 
52
64
  def mock_env(options = {})
53
65
  FAKE_ENV.merge!(options)
@@ -56,7 +68,6 @@ DEFAULT_ENV = { "rack.version" => Rack::VERSION, "rack.input" => StringIO.new, "
56
68
  def http_options_request(path)
57
69
  complex_env = mock_env({"REQUEST_METHOD" => "OPTIONS", "PATH_INFO" => path })
58
70
  response = Optionsful.new(app).call(complex_env)
59
- response
60
71
  end
61
72
 
62
73
  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: 31
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 2
10
- version: 0.1.2
9
+ - 5
10
+ version: 0.1.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Marco Antonio Gonzalez Junior
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-19 00:00:00 -03:00
18
+ date: 2010-07-22 00:00:00 -03:00
19
19
  default_executable:
20
20
  dependencies: []
21
21
 
@@ -37,6 +37,10 @@ files:
37
37
  - spec/optionsful_spec.rb
38
38
  - spec/spec.opts
39
39
  - spec/spec_helper.rb
40
+ - lib/optionsful_docs.rb
41
+ - lib/introspections.rb
42
+ - lib/baurets/optionsful/version.rb
43
+ - spec/optionsful_docs_spec.rb
40
44
  has_rdoc: true
41
45
  homepage: http://optionsful.rubyforge.org
42
46
  licenses: []