optionsful 0.1.2 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []