lunetas 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,80 @@
1
+ ![Lunetas](http://dulcemexico.com/productos/lunetasgrand.jpg)
2
+
1
3
  Lunetas
2
- -------
4
+ =======
3
5
 
4
- A mini framework based on top of Rack for APIs.
6
+ A Rack based micro framework.
5
7
 
6
- ![Lunetas](http://dulcemexico.com/productos/lunetasgrand.jpg)
8
+ Structure
9
+ ---------
10
+
11
+ It is a class-url based framework. It means, that every class describes a route using
12
+ a Regular Expression. It may respond to one or many HTTP methods. These responses are
13
+ defined overwritting the methods get, put, post, delete, trace, head, etc.. It can
14
+ also handle other non-native HTTP methods, using overwritting other_verb.
15
+
16
+ What does all these means? Checkout an example. (Also you may want to take a look at
17
+ the examples folder).
18
+
19
+ Usage
20
+ -----
21
+
22
+ If you are going to use Lunetas as a stand alone Rack application. In order to get it
23
+ running, you just need to add `run Lunetas::Bag` in your config.ru file.
24
+
25
+ If you are going to use Lunetas behind a framework like Rails. You just need to add
26
+ the gem, `require 'lunetas'` in your metal, and you are ready to go.
27
+
28
+ Examples
29
+ --------
30
+
31
+ ### Simple example
32
+
33
+ require 'lunetas'
34
+
35
+ class Testing
36
+ include Lunetas::Candy
37
+ matches '/hello/(\w+)', :name
38
+
39
+ def before
40
+ @name = @name.capitalize
41
+ end
42
+
43
+ def get
44
+ "Hello #{@name}! #{params[:chunky]}"
45
+ end
46
+
47
+ def post
48
+ "Hey #{@name}, I see you're testing the POST method :)"
49
+ end
50
+ end
51
+
52
+ class AnotherTest
53
+ include Lunetas::Candy
54
+ matches '^/(\d+)$', :number
55
+
56
+ def get
57
+ "Is #{@number} your lucky number?"
58
+ end
59
+
60
+ def other_verb(verb)
61
+ if verb == 'TEAPOT'
62
+ "I ain't a teapot!"
63
+ end
64
+ end
65
+ end
66
+
67
+ ### Defining a custom ContentType
68
+
69
+ require 'lunetas'
70
+ require 'json'
71
+
72
+ class JaySon
73
+ include Lunetas::Candy
74
+ matches '^/something\.json$'
75
+ set_content_type 'application/json'
76
+
77
+ def get
78
+ { :test => true, 'json' => "Yes, JSON", :amount => 1}.to_json
79
+ end
80
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.1.0
@@ -4,8 +4,8 @@ require 'rack'
4
4
 
5
5
  module Lunetas
6
6
  base_dir = File.dirname(__FILE__) + '/lunetas/'
7
+ autoload :Bag, base_dir + 'bag.rb'
7
8
  autoload :Candy, base_dir + 'candy.rb'
8
- autoload :Bag, base_dir + 'bag.rb'
9
9
  autoload :Error, base_dir + 'error.rb'
10
10
  end
11
11
 
@@ -11,202 +11,19 @@
11
11
  # end
12
12
  # end
13
13
  module Lunetas::Candy
14
- module InstanceMethods
15
- # The matched url-regex for this resource.
16
- attr_reader :url
17
- attr_accessor :url_params
18
- # The initialization of a class that includes a Candy, should be done
19
- # with rack environment and the url matches from its regular expression.
20
- # It will register all the passed url parameters from #matches as new
21
- # instance variables.
22
- # @param [Hash] env the Rack env.
23
- # @param [Array] url_matches the matches from its regex. In most cases
24
- # (MatchData instance).to_a
25
- def initialize(env, url_matches)
26
- @req = Rack::Request.new(env)
27
- @_url_params = url_matches
28
- self.url_params = url_matches
29
- @url = @_url_params.shift
30
- begin
31
- self.class._url_params.each_with_index do |option, index|
32
- instance_variable_set "@#{option}", url_param(index)
33
- end
34
- # Provide an authentication method. Probably a method
35
- # from the Lunetas::Bag.
36
- # authenticate!
37
- # rescue Lunetas::Error::AuthenticationError
38
- # @_error = Lunetas::Error::AuthenticationError
39
- end
40
- end
41
-
42
- # Returns the url parameter from the regular expresion. If a captured
43
- # block is given, then they will be added in order of appearance.
44
- # @param [Fixnum] index the index of the captured block.
45
- # @return [String] the captured block.
46
- def url_param(index)
47
- @_url_params[index]
48
- end
49
-
50
- # Bites the Candy (a.k.a process this resource).
51
- #
52
- # @return [Array] a standard rack response.
53
- def bite
54
- raise @_error if @_error
55
- before
56
- response(handle_call)
57
- rescue Exception => e
58
- code, error = 500, e
59
- if Lunetas::Error::BaseError === e
60
- code = e.code
61
- elsif development?
62
- error = "Error: #{e.message}\nBacktrace: #{e.backtrace.join('\n')}"
63
- end
64
- response(error, code)
65
- end
66
-
67
- private
68
- def handle_call
69
- case @req.request_method
70
- when 'GET'
71
- get
72
- when 'POST'
73
- post
74
- when 'PUT'
75
- put
76
- when 'DELETE'
77
- delete
78
- when 'HEAD'
79
- head
80
- when 'TRACE'
81
- trace
82
- when 'OPTIONS'
83
- options
84
- else
85
- response = other_verb(@req.request_method)
86
- raise Lunetas::Error::APIError unless response
87
- response
88
- end
89
- end
90
-
91
- def xhr?
92
- @req.xhr?
93
- end
94
-
95
- def params
96
- @req.params
97
- end
98
-
99
- def request
100
- @req
101
- end
102
-
103
- def session
104
- @req.session
105
- end
106
-
107
- def development?
108
- ENV['RAILS_ENV'] == 'development' || ENV['RACK_ENV'].nil? || ENV['RACK_ENV'] == 'development'
109
- end
110
-
111
- # TODO: Polish this
112
- # def authenticate!
113
- # @current_user = User.where(:single_access_token => token).first
114
- # raise Lunetas::Error::AuthenticationError unless @current_user
115
- # end
116
-
117
- def response(object, code = 200)
118
- [code, {'Content-Type' => self.class.content_type}, [object.to_s]]
119
- end
120
-
121
- # The following methods should be overwritten by the including class
122
- def before
123
- nil
124
- end
125
-
126
- def get
127
- raise Lunetas::Error::APIError
128
- end
129
-
130
- def post
131
- raise Lunetas::Error::APIError
132
- end
133
-
134
- def put
135
- raise Lunetas::Error::APIError
136
- end
137
-
138
- def delete
139
- raise Lunetas::Error::APIError
140
- end
141
-
142
- def head
143
- raise Lunetas::Error::APIError
144
- end
145
-
146
- def trace
147
- raise Lunetas::Error::APIError
148
- end
149
-
150
- def options
151
- raise Lunetas::Error::APIError
152
- end
153
-
154
- def other_verb(verb)
155
- raise Lunetas::Error::APIError
156
- end
157
- end
158
-
159
- module ClassMethods
160
- attr_reader :_url_params
161
- attr_reader :content_type
162
-
163
- # Support to be runned as a Rails Metal.
164
- # @param [Hash] env the Rack env.
165
- # @return [Array] a standard Rack response.
166
- def call(env)
167
- url_match = env['PATH_INFO'].match(@_regex)
168
- if url_match
169
- candy = new(env, url_match.to_a)
170
- candy.bite
171
- else
172
- [404, {"Content-Type" => "text/html", "X-Cascade" => "pass"}, ["Not Found"]]
173
- end
174
- end
175
-
176
- private
177
- # Registers a new regular expression. Will add it to the Lunetas::Bag.
178
- # Will convert the regex to a Regular Expression, if passing a String. It
179
- # also receives the instance variables for the matches of the regex.
180
- # @example Regular expression as a Regexp
181
- # matches /\/test\/\w+/
182
- # @example Regular expression as a double quoted string
183
- # matches "\\/test\\/\\w+"
184
- # @example Regular expression as a single quoted string (my favorite)
185
- # matches '/test/\w+'
186
- # @example Passing instance variables that will be set for the captures
187
- # matches '/test/(\d+)\.(\w+)', :id, :format
188
- # @param [String, Regexp] regex the regular expression for this resource.
189
- # @param [Array<Symbol, String>] url_params the instance variables that will
190
- # be set for the captures from the regex.
191
- def matches(regex, *url_params)
192
- @content_type = 'text/html'
193
- @_regex = regex
194
- unless Regexp === @_regex
195
- @_regex = Regexp.new(@_regex)
196
- end
197
- @_url_params = url_params
198
- Lunetas::Bag.register(@_regex, self)
199
- end
200
-
201
- # Sets the Content Type for this URL. Defaults to text/html.
202
- # @param [String] content_type the ContentType for the response.
203
- def set_content_type(content_type)
204
- @content_type = content_type
205
- end
206
- end
14
+ base_dir = File.dirname(__FILE__) + '/candy/'
15
+ autoload :Initialization, base_dir + 'initialization.rb'
16
+ autoload :MethodStrategy, base_dir + 'method_strategy.rb'
17
+ autoload :RequestWrapper, base_dir + 'request_wrapper.rb'
18
+ autoload :ResponseHandler, base_dir + 'response_handler.rb'
207
19
 
20
+ # @private
208
21
  def self.included(receiver)
209
- receiver.send :include, InstanceMethods
210
- receiver.send :extend, ClassMethods
22
+ receiver.send :include, Initialization::InstanceMethods
23
+ receiver.send :extend, Initialization::ClassMethods
24
+ receiver.send :include, MethodStrategy::InstanceMethods
25
+ receiver.send :include, RequestWrapper::InstanceMethods
26
+ receiver.send :include, ResponseHandler::InstanceMethods
27
+ receiver.send :extend, ResponseHandler::ClassMethods
211
28
  end
212
29
  end
@@ -0,0 +1,71 @@
1
+ module Lunetas::Candy::Initialization
2
+ module InstanceMethods
3
+ # The matched url-regex for this resource.
4
+ attr_reader :url
5
+ # The url parameters hold by the instance.
6
+ attr_reader :lunetas_url_instance_params
7
+
8
+ # The initialization of a class that includes a Candy, should be done
9
+ # with rack environment and the url matches from its regular expression.
10
+ # It will register all the passed url parameters from #matches as new
11
+ # instance variables.
12
+ # @param [Hash] env the Rack env.
13
+ # @param [Array] url_matches the matches from its regex. In most cases
14
+ # (MatchData instance).to_a
15
+ def initialize(env, url_matches)
16
+ @req = Rack::Request.new(env)
17
+ @lunetas_url_instance_params = url_matches
18
+ @url = @lunetas_url_instance_params.shift
19
+ begin
20
+ self.class.lunetas_url_params.each_with_index do |option, index|
21
+ instance_variable_set "@#{option}", url_param(index)
22
+ end
23
+ # Provide an authentication method. Probably a method
24
+ # from the Lunetas::Bag.
25
+ # authenticate!
26
+ # rescue Lunetas::Error::AuthenticationError
27
+ # @_error = Lunetas::Error::AuthenticationError
28
+ end
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ # Holds the URL params, this will be created as instance
34
+ # variables when the class is initialized.
35
+ attr_reader :lunetas_url_params
36
+ # Holds the Content Type for this request.
37
+ attr_reader :lunetas_content_type
38
+ # Holds the Regular Expression for this class.
39
+ attr_reader :lunetas_regex
40
+
41
+ # Registers a new regular expression. Will add it to the Lunetas::Bag.
42
+ # Will convert the regex to a Regular Expression, if passing a String. It
43
+ # also receives the instance variables for the matches of the regex.
44
+ # @example Regular expression as a Regexp
45
+ # matches /\/test\/\w+/
46
+ # @example Regular expression as a double quoted string
47
+ # matches "\\/test\\/\\w+"
48
+ # @example Regular expression as a single quoted string (my favorite)
49
+ # matches '/test/\w+'
50
+ # @example Passing instance variables that will be set for the captures
51
+ # matches '/test/(\d+)\.(\w+)', :id, :format
52
+ # @param [String, Regexp] regex the regular expression for this resource.
53
+ # @param [Array<Symbol, String>] url_params the instance variables that will
54
+ # be set for the captures from the regex.
55
+ def matches(regex, *url_params)
56
+ @lunetas_content_type = 'text/html'
57
+ @lunetas_regex = regex
58
+ unless Regexp === lunetas_regex
59
+ @lunetas_regex = Regexp.new(regex)
60
+ end
61
+ @lunetas_url_params = url_params
62
+ Lunetas::Bag.register(lunetas_regex, self)
63
+ end
64
+
65
+ # Sets the Content Type for this URL. Defaults to text/html.
66
+ # @param [String] content_type the ContentType for the response.
67
+ def set_content_type(content_type)
68
+ @lunetas_content_type = content_type
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,80 @@
1
+ # This is an 'abstract' class for the response of the call.
2
+ # The methods here can and should be overwritten. If they're
3
+ # not (the http methods) will raise an exception, telling that
4
+ # this method is not allowed for this route.
5
+ module Lunetas::Candy::MethodStrategy
6
+ module InstanceMethods
7
+ # Called before getting the response. Useful to set instance
8
+ # variables that will be used in the methods. Analog to a
9
+ # before filter in Rails.
10
+ # @return [nil]
11
+ def before
12
+ nil
13
+ end
14
+
15
+ # The response of the GET HTTP method.
16
+ # @raise [Lunetas::Error::APIError] if the method is not
17
+ # overwritten.
18
+ # @return [nil]
19
+ def get
20
+ raise Lunetas::Error::APIError
21
+ end
22
+
23
+ # The response of the POST HTTP method.
24
+ # @raise [Lunetas::Error::APIError] if the method is not
25
+ # overwritten.
26
+ # @return [nil]
27
+ def post
28
+ raise Lunetas::Error::APIError
29
+ end
30
+
31
+ # The response of the PUT HTTP method.
32
+ # @raise [Lunetas::Error::APIError] if the method is not
33
+ # overwritten.
34
+ # @return [nil]
35
+ def put
36
+ raise Lunetas::Error::APIError
37
+ end
38
+
39
+ # The response of the DELETE HTTP method.
40
+ # @raise [Lunetas::Error::APIError] if the method is not
41
+ # overwritten.
42
+ # @return [nil]
43
+ def delete
44
+ raise Lunetas::Error::APIError
45
+ end
46
+
47
+ # The response of the HEAD HTTP method.
48
+ # @raise [Lunetas::Error::APIError] if the method is not
49
+ # overwritten.
50
+ # @return [nil]
51
+ def head
52
+ raise Lunetas::Error::APIError
53
+ end
54
+
55
+ # The response of the TRACE HTTP method.
56
+ # @raise [Lunetas::Error::APIError] if the method is not
57
+ # overwritten.
58
+ # @return [nil]
59
+ def trace
60
+ raise Lunetas::Error::APIError
61
+ end
62
+
63
+ # The response of the OPTIONS HTTP method.
64
+ # @raise [Lunetas::Error::APIError] if the method is not
65
+ # overwritten.
66
+ # @return [nil]
67
+ def options
68
+ raise Lunetas::Error::APIError
69
+ end
70
+
71
+ # The response of the any other HTTP method.
72
+ # @raise [Lunetas::Error::APIError] if the method is not
73
+ # overwritten.
74
+ # @param [String] verb the HTTP method that was called from.
75
+ # @return [nil]
76
+ def other_verb(verb)
77
+ raise Lunetas::Error::APIError
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,40 @@
1
+ # This module is a Wrapper for the request object. It exposes
2
+ # several methods in order to get the session, params, etc.
3
+ module Lunetas::Candy::RequestWrapper
4
+ module InstanceMethods
5
+ # Called from an XML Http Request?
6
+ # @return [true, false]
7
+ def xhr?
8
+ @req.xhr?
9
+ end
10
+
11
+ # Gets the request parameters.
12
+ # @return [Hash]
13
+ def params
14
+ @req.params
15
+ end
16
+
17
+ # Gets the current request object.
18
+ # @return [Rack::Request]
19
+ def request
20
+ @req
21
+ end
22
+
23
+ # Gets the current session.
24
+ # @return [Hash]
25
+ def session
26
+ @req.session
27
+ end
28
+
29
+ # Is lunetas running in development?
30
+ # @return [true, false]
31
+ def development?
32
+ if ENV['RAILS_ENV']
33
+ ENV['RAILS_ENV'] == 'development'
34
+ else
35
+ ENV['RACK_ENV'].nil? || ENV['RACK_ENV'] == 'development'
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,81 @@
1
+ # Handles the response to the client. Has the call method to
2
+ # be called from Rack.
3
+ module Lunetas::Candy::ResponseHandler
4
+ module InstanceMethods
5
+ # Handles the rack call. Delegates it to the method
6
+ # that matches the request method.
7
+ # @return [Array] a Rack::Request response.
8
+ def handle_call
9
+ case @req.request_method
10
+ when 'GET'
11
+ get
12
+ when 'POST'
13
+ post
14
+ when 'PUT'
15
+ put
16
+ when 'DELETE'
17
+ delete
18
+ when 'HEAD'
19
+ head
20
+ when 'TRACE'
21
+ trace
22
+ when 'OPTIONS'
23
+ options
24
+ else
25
+ response = other_verb(@req.request_method)
26
+ raise Lunetas::Error::APIError unless response
27
+ response
28
+ end
29
+ end
30
+
31
+ # Bites the Candy (a.k.a process this resource).
32
+ #
33
+ # @return [Array] a standard rack response.
34
+ def bite
35
+ raise @_error if @_error
36
+ before
37
+ response(handle_call)
38
+ rescue Exception => e
39
+ code, error = 500, e
40
+ if Lunetas::Error::BaseError === e
41
+ code = e.code
42
+ elsif development?
43
+ error = "Error: #{e.message}\nBacktrace: #{e.backtrace.join("\n")}"
44
+ end
45
+ response(error, code)
46
+ end
47
+
48
+ # Returns the url parameter from the regular expresion. If a captured
49
+ # block is given, then they will be added in order of appearance.
50
+ # @param [Fixnum] index the index of the captured block.
51
+ # @return [String] the captured block.
52
+ def url_param(index)
53
+ @lunetas_url_instance_params[index]
54
+ end
55
+
56
+ private
57
+ # A Rack::Request response with the specified content type.
58
+ # @param [Object#to_s] object the object that will be the
59
+ # response.
60
+ # @param [Fixnum] code the response code.
61
+ # @return [Array] a Rack::Request response.
62
+ def response(object, code = 200)
63
+ [code, {'Content-Type' => self.class.lunetas_content_type}, [object.to_s]]
64
+ end
65
+ end
66
+
67
+ module ClassMethods
68
+ # Support to be runned as a Rails Metal.
69
+ # @param [Hash] env the Rack env.
70
+ # @return [Array] a standard Rack response.
71
+ def call(env)
72
+ url_match = env['PATH_INFO'].match(lunetas_regex)
73
+ if url_match
74
+ candy = new(env, url_match.to_a)
75
+ candy.bite
76
+ else
77
+ [404, {"Content-Type" => "text/html", "X-Cascade" => "pass"}, ["Not Found"]]
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
- describe Lunetas::Candy::InstanceMethods do
3
+ describe Lunetas::Candy do
4
4
  describe '.initialize' do
5
5
  before(:each) do
6
6
  @instance = TestClass.new(mock_env('/just_a_test'), ['/just_a_test', 'a', 'b'])
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lunetas
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 4
10
- version: 0.0.4
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - "Iv\xC3\xA1n Vald\xC3\xA9s (@ivanvc)"
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-08-24 00:00:00 -05:00
18
+ date: 2010-08-26 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -98,6 +98,10 @@ files:
98
98
  - lib/lunetas.rb
99
99
  - lib/lunetas/bag.rb
100
100
  - lib/lunetas/candy.rb
101
+ - lib/lunetas/candy/initialization.rb
102
+ - lib/lunetas/candy/method_strategy.rb
103
+ - lib/lunetas/candy/request_wrapper.rb
104
+ - lib/lunetas/candy/response_handler.rb
101
105
  - lib/lunetas/error.rb
102
106
  - spec/lunetas/bag_spec.rb
103
107
  - spec/lunetas/candy_spec.rb