lunetas 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.md
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Iván Valdés (@ivanvc)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ Lunetas
2
+ -------
3
+
4
+ A mini framework based on top of Rack for APIs.
5
+
6
+ ![Lunetas](http://dulcemexico.com/productos/lunetasgrand.jpg)
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "lunetas"
8
+ gem.summary = %Q{Rack micro framework intended for APIs}
9
+ gem.description = %Q{This is a micro framework based in Rack, inspired in Sinatra, camping and many others. The intention is to have fast responses for simple API calls. The base unit of this framework are the classes, which registers a regular expression that will match this resource.}
10
+ gem.email = "iv@nvald.es"
11
+ gem.homepage = "http://github.com/ivanvc/lunetas"
12
+ gem.authors = ["Iván Valdés (@ivanvc)"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.add_development_dependency "yard", ">= 0"
15
+ gem.add_development_dependency "shotgun", ">= 0"
16
+ gem.add_dependency "rack", ">= 0"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'spec/rake/spectask'
25
+ Spec::Rake::SpecTask.new(:spec) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rcov = true
34
+ end
35
+
36
+ task :spec => :check_dependencies
37
+
38
+ task :default => :spec
39
+
40
+ begin
41
+ require 'yard'
42
+ YARD::Rake::YardocTask.new
43
+ rescue LoadError
44
+ task :yardoc do
45
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
46
+ end
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,3 @@
1
+ require 'testing'
2
+
3
+ run Lunetas::Bag
@@ -0,0 +1,29 @@
1
+ require '../lib/lunetas'
2
+
3
+ class Testing
4
+ include Lunetas::Candy
5
+ matches '/hello/(\w+)', :name
6
+
7
+ def get
8
+ "Hello #{@name}! #{params[:chunky]}"
9
+ end
10
+
11
+ def post
12
+ "Hey #{@name}, I see you're testing the POST method :)"
13
+ end
14
+ end
15
+
16
+ class AnotherTest
17
+ include Lunetas::Candy
18
+ matches '^/(\d+)$', :number
19
+
20
+ def get
21
+ "Is #{@number} your lucky number?"
22
+ end
23
+
24
+ def other_verb(verb)
25
+ if verb == 'TEAPOT'
26
+ "I ain't a teapot!"
27
+ end
28
+ end
29
+ end
data/lib/lunetas.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rubygems'
2
+ gem 'rack'
3
+ require 'rack'
4
+
5
+ module Lunetas
6
+ base_dir = File.dirname(__FILE__) + '/lunetas/'
7
+ autoload :Candy, base_dir + 'candy.rb'
8
+ autoload :Bag, base_dir + 'bag.rb'
9
+ autoload :Error, base_dir + 'error.rb'
10
+ end
11
+
@@ -0,0 +1,33 @@
1
+ # This is the module that keeps the register of all the Lunetas. It will add the
2
+ # classes to a register, and whenever rack receives a call, it delegates it.
3
+ # If Lunetas is not run as a stand alone rack application, then this module
4
+ # lacks of responsability, ie. Running as a Rails Metal.
5
+ module Lunetas::Bag
6
+ # Registers new classes to a given regular expression. It should be called from
7
+ # a Luneta.
8
+ # @param [Regexp] regex the regular expression for this Luneta Class.
9
+ # @param [Luneta] candy the class that owns the regular expresion.
10
+ def self.register(regex, candy)
11
+ # TODO: Log: "Registering #{regex.inspect} #{candy.inspect}"
12
+ @@candies ||= {}
13
+ @@candies[regex] = candy
14
+ end
15
+
16
+ # Rack's call method. Will be called with the env, from rack. If it matches
17
+ # a regular expression, it will start a new instance of the propiertary class.
18
+ # If there's no matching class, it will return a 404.
19
+ #
20
+ # @param [Hash] env the rack's env.
21
+ # @return [Array] the rack response.
22
+ def self.call(env)
23
+ @url_match = nil
24
+ match_regex = @@candies.keys.find do |regex|
25
+ @url_match = env['PATH_INFO'].match(regex)
26
+ end
27
+ unless match_regex
28
+ return [404, {"Content-Type" => "text/html", "X-Cascade" => "pass"}, ["Not Found"]]
29
+ end
30
+ candy = @@candies[match_regex].new(env, @url_match.to_a)
31
+ candy.bite
32
+ end
33
+ end
@@ -0,0 +1,186 @@
1
+ # This module should be included in any resource that should be exposed with
2
+ # an API. Then the method matches should be called in order to register the
3
+ # path for this resource.
4
+ # @example Simple example
5
+ # class Testing
6
+ # include Lunetas::Candy
7
+ # matches '/test'
8
+ #
9
+ # def get
10
+ # "This is what I answer then calling GET '/test'"
11
+ # end
12
+ # end
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
+ response(e, e.code)
59
+ end
60
+
61
+ private
62
+ def handle_call
63
+ case @req.request_method
64
+ when 'GET'
65
+ get
66
+ when 'POST'
67
+ post
68
+ when 'PUT'
69
+ put
70
+ when 'DELETE'
71
+ delete
72
+ when 'HEAD'
73
+ head
74
+ when 'TRACE'
75
+ trace
76
+ when 'OPTIONS'
77
+ options
78
+ else
79
+ response = other_verb(@req.request_method)
80
+ raise Lunetas::Error::APIError unless response
81
+ response
82
+ end
83
+ end
84
+
85
+ def xhr?
86
+ @req.xhr?
87
+ end
88
+
89
+ def params
90
+ @req.params
91
+ end
92
+
93
+ # TODO: Polish this
94
+ # def authenticate!
95
+ # @current_user = User.where(:single_access_token => token).first
96
+ # raise Lunetas::Error::AuthenticationError unless @current_user
97
+ # end
98
+
99
+ def response(object, code = 200)
100
+ [code, {'Content-Type' => "application/json"}, [object.to_s]]
101
+ end
102
+
103
+ # The following methods should be overwritten by the including class
104
+ def before
105
+ nil
106
+ end
107
+
108
+ def get
109
+ raise Lunetas::Error::APIError
110
+ end
111
+
112
+ def post
113
+ raise Lunetas::Error::APIError
114
+ end
115
+
116
+ def put
117
+ raise Lunetas::Error::APIError
118
+ end
119
+
120
+ def delete
121
+ raise Lunetas::Error::APIError
122
+ end
123
+
124
+ def head
125
+ raise Lunetas::Error::APIError
126
+ end
127
+
128
+ def trace
129
+ raise Lunetas::Error::APIError
130
+ end
131
+
132
+ def options
133
+ raise Lunetas::Error::APIError
134
+ end
135
+
136
+ def other_verb(verb)
137
+ raise Lunetas::Error::APIError
138
+ end
139
+ end
140
+
141
+ module ClassMethods
142
+ attr_reader :_url_params
143
+
144
+ # Support to be runned as a Rails Metal.
145
+ # @param [Hash] env the Rack env.
146
+ # @return [Array] a standard Rack response.
147
+ def call(env)
148
+ url_match = env['PATH_INFO'].match(@_regex)
149
+ if url_match
150
+ call = new(env, url_match.to_a)
151
+ call.process
152
+ else
153
+ [404, {"Content-Type" => "text/html", "X-Cascade" => "pass"}, ["Not Found"]]
154
+ end
155
+ end
156
+
157
+ private
158
+ # Registers a new regular expression. Will add it to the Lunetas::Bag.
159
+ # Will convert the regex to a Regular Expression, if passing a String. It
160
+ # also receives the instance variables for the matches of the regex.
161
+ # @example Regular expression as a Regexp
162
+ # matches /\/test\/\w+/
163
+ # @example Regular expression as a double quoted string
164
+ # matches "\\/test\\/\\w+"
165
+ # @example Regular expression as a single quoted string (my favorite)
166
+ # matches '/test/\w+'
167
+ # @example Passing instance variables that will be set for the captures
168
+ # matches '/test/(\d+)\.(\w+)', :id, :format
169
+ # @param [String, Regexp] regex the regular expression for this resource.
170
+ # @param [Array<Symbol, String>] url_params the instance variables that will
171
+ # be set for the captures from the regex.
172
+ def matches(regex, *url_params)
173
+ @_regex = regex
174
+ unless Regexp === @_regex
175
+ @_regex = Regexp.new(@_regex)
176
+ end
177
+ @_url_params = url_params
178
+ Lunetas::Bag.register(@_regex, self)
179
+ end
180
+ end
181
+
182
+ def self.included(receiver)
183
+ receiver.send :include, InstanceMethods
184
+ receiver.send :extend, ClassMethods
185
+ end
186
+ end
@@ -0,0 +1,42 @@
1
+ module Lunetas::Error
2
+ class BaseError < StandardError
3
+ def to_json
4
+ {'error' => message}.to_json
5
+ end
6
+ def code
7
+ 400
8
+ end
9
+ def message
10
+ "Error"
11
+ end
12
+ def to_s
13
+ message
14
+ end
15
+ end
16
+ class AuthenticationError < BaseError
17
+ def code
18
+ 401
19
+ end
20
+ def message
21
+ "Permission denied"
22
+ end
23
+ end
24
+
25
+ class BuildError < BaseError
26
+ def code
27
+ 405
28
+ end
29
+ def message
30
+ "Build error"
31
+ end
32
+ end
33
+
34
+ class APIError < BaseError
35
+ def code
36
+ 404
37
+ end
38
+ def message
39
+ "API route error"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Lunetas::Bag do
4
+
5
+ describe "register" do
6
+ before(:each) do
7
+ @luneta_class, @luneta_object = mock_candy
8
+ end
9
+
10
+ it 'should register a new class' do
11
+ Lunetas::Bag.register(/test/, @luneta_class)
12
+ @luneta_object.should_receive(:bite)
13
+ Lunetas::Bag.call(mock_env('/test'))
14
+ end
15
+ end
16
+
17
+ describe "call" do
18
+ before(:each) do
19
+ @luneta_class, @luneta_object = mock_candy
20
+ end
21
+
22
+ it 'should return a 404 if no matching path' do
23
+ Lunetas::Bag.call(mock_env('/123')).first.should == 404
24
+ end
25
+
26
+ it 'should return the bite of the called' do
27
+ Lunetas::Bag.register(/chunky/, @luneta_class)
28
+ @luneta_class.should_receive(:new).and_return(@luneta_object)
29
+ @luneta_object.should_receive(:bite).once
30
+ Lunetas::Bag.call(mock_env('/chunky')).first.should == 200
31
+ end
32
+
33
+ it 'should pass the matched regex and matches' do
34
+ Lunetas::Bag.register(/bacon\/(bbacon)/, @luneta_class)
35
+ env = mock_env('/bacon/bbacon')
36
+ @luneta_class.should_receive(:new).with(env, ['bacon/bbacon', 'bbacon'])
37
+ Lunetas::Bag.call(env)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,106 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Lunetas::Candy::InstanceMethods do
4
+ describe '.initialize' do
5
+ before(:each) do
6
+ @instance = TestClass.new(mock_env('/just_a_test'), ['/just_a_test', 'a', 'b'])
7
+ end
8
+
9
+ it 'should set the url variable' do
10
+ @instance.url.should == '/just_a_test'
11
+ end
12
+
13
+ it 'should set the url params' do
14
+ @instance.url_param(0).should == 'a'
15
+ @instance.url_param(1).should == 'b'
16
+ end
17
+ end
18
+
19
+ describe '.url_param' do
20
+ it 'should be accasible through its index' do
21
+ instance = TestClass.new(mock_env('/just_a_test'), ['/just_a_test', 'a'])
22
+ instance.url_param(0).should == 'a'
23
+ end
24
+
25
+ it 'should have none if no matches' do
26
+ instance = TestClass.new(mock_env('/just_a_test'), ['/just_a_test'])
27
+ instance.url_param(0).should be_nil
28
+ end
29
+ end
30
+
31
+ describe '.bite' do
32
+ before(:each) do
33
+ @instance = TestClass.new(mock_env('/just_a_test'), ['/just_a_test', 'a'])
34
+ end
35
+
36
+ it 'should call the before method' do
37
+ @instance.should_receive(:before).once
38
+ @instance.bite
39
+ end
40
+
41
+ it 'should call to the response' do
42
+ @instance.should_receive(:response).once.and_return('')
43
+ @instance.bite
44
+ end
45
+
46
+ it 'should answer with the raised error' do
47
+ test_exception = TestException.new
48
+ @instance.should_receive(:before).and_raise(test_exception)
49
+ @instance.should_receive(:response).with(test_exception, 400)
50
+ @instance.bite
51
+ end
52
+
53
+ it 'should call to the get method' do
54
+ @instance.should_receive(:get)
55
+ @instance.bite
56
+ end
57
+
58
+ it 'should render the given response' do
59
+ @instance.bite.last.should == ['Chunky Bacon']
60
+ end
61
+
62
+ %w{post put delete head trace options}.each do |verb|
63
+ it 'should call to the #{verb} method if called with #{verb.upcase}' do
64
+ mock_env = mock_env('/just_a_test')
65
+ mock_env['REQUEST_METHOD'] = verb.upcase
66
+ @instance = TestClass.new(mock_env, ['/just_a_test'])
67
+ @instance.should_receive(verb)
68
+ @instance.bite
69
+ end
70
+
71
+ it 'should return an API Error to #{verb.upcase} if no defined' do
72
+ mock_env = mock_env('/just_a_test')
73
+ mock_env['REQUEST_METHOD'] = verb.upcase
74
+ @instance = TestClass.new(mock_env, ['/just_a_test'])
75
+ @instance.should_receive(verb).and_raise(Lunetas::Error::APIError)
76
+ @instance.bite.last.should == ["API route error"]
77
+ end
78
+ end
79
+
80
+ it 'should call to other_verb with the passed method' do
81
+ mock_env = mock_env('/just_a_test')
82
+ mock_env['REQUEST_METHOD'] = 'TEAPOT'
83
+ @instance = TestClass.new(mock_env, ['/just_a_test'])
84
+ @instance.should_receive(:other_verb).with('TEAPOT')
85
+ @instance.bite
86
+ end
87
+
88
+ it 'should call to other_verb with the passed method and return the response if method handled' do
89
+ mock_env = mock_env('/just_a_test')
90
+ mock_env['REQUEST_METHOD'] = 'TEAPOT'
91
+ @instance = TestClass.new(mock_env, ['/just_a_test'])
92
+ @instance.should_receive(:other_verb).with('TEAPOT').and_return('TEAPOT YEAH')
93
+ @instance.bite.last.should == ['TEAPOT YEAH']
94
+ end
95
+
96
+ it 'should call to other_verb with the passed method and return error if method not handled' do
97
+ mock_env = mock_env('/just_a_test')
98
+ mock_env['REQUEST_METHOD'] = 'TEAPOT'
99
+ @instance = TestClass.new(mock_env, ['/just_a_test'])
100
+ @instance.should_receive(:other_verb).with('TEAPOT')
101
+ @instance.bite.last.should == ['API route error']
102
+ end
103
+
104
+ end
105
+
106
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,38 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'lunetas'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
10
+
11
+ def mock_candy
12
+ body = [200, {'Content-Type' => 'text/html'}, ["body"]]
13
+ luneta_object = mock(:Luneta, :bite => body)
14
+ [mock(:Luneta, :new => luneta_object), luneta_object]
15
+ end
16
+
17
+ def mock_env(path)
18
+ Rack::MockRequest.env_for(path)
19
+ end
20
+
21
+ class TestClass
22
+ include Lunetas::Candy
23
+ matches '/just_a_test'
24
+ def get
25
+ 'Chunky Bacon'
26
+ end
27
+ end
28
+
29
+ class TestException < StandardError
30
+ def code
31
+ 400
32
+ end
33
+ def message
34
+ 'a'
35
+ end
36
+ end
37
+
38
+
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lunetas
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - "Iv\xC3\xA1n Vald\xC3\xA9s (@ivanvc)"
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-21 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
34
+ version: 1.2.9
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: yard
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :development
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ name: shotgun
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :development
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: rack
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :runtime
78
+ version_requirements: *id004
79
+ description: This is a micro framework based in Rack, inspired in Sinatra, camping and many others. The intention is to have fast responses for simple API calls. The base unit of this framework are the classes, which registers a regular expression that will match this resource.
80
+ email: iv@nvald.es
81
+ executables: []
82
+
83
+ extensions: []
84
+
85
+ extra_rdoc_files:
86
+ - LICENSE
87
+ - README.md
88
+ files:
89
+ - .document
90
+ - .gitignore
91
+ - LICENSE
92
+ - README.md
93
+ - Rakefile
94
+ - VERSION
95
+ - examples/config.ru
96
+ - examples/testing.rb
97
+ - lib/lunetas.rb
98
+ - lib/lunetas/bag.rb
99
+ - lib/lunetas/candy.rb
100
+ - lib/lunetas/error.rb
101
+ - spec/lunetas/bag_spec.rb
102
+ - spec/lunetas/candy_spec.rb
103
+ - spec/spec.opts
104
+ - spec/spec_helper.rb
105
+ has_rdoc: true
106
+ homepage: http://github.com/ivanvc/lunetas
107
+ licenses: []
108
+
109
+ post_install_message:
110
+ rdoc_options:
111
+ - --charset=UTF-8
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ hash: 3
120
+ segments:
121
+ - 0
122
+ version: "0"
123
+ required_rubygems_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ requirements: []
133
+
134
+ rubyforge_project:
135
+ rubygems_version: 1.3.7
136
+ signing_key:
137
+ specification_version: 3
138
+ summary: Rack micro framework intended for APIs
139
+ test_files:
140
+ - spec/lunetas/bag_spec.rb
141
+ - spec/lunetas/candy_spec.rb
142
+ - spec/spec_helper.rb
143
+ - examples/testing.rb