kawaii-api 0.0.4

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ea79f9e987e739ff81727f6ce6da51b26a8392e6
4
+ data.tar.gz: 5e12c43e07da2808b134be8f30fe88d55d7e550c
5
+ SHA512:
6
+ metadata.gz: 653d5961c1922ca474197408b18bb612b6c559008a6b0114001af1a70e9421614d19fb1d0b81b54b828077f5be9d501a56fc6a35b1d3b97f419dbe7a29551cb6
7
+ data.tar.gz: 09aee41c9a1ff002e3e2db408004c974e6ded3542bd600bc7a187f580f3bc9211b4ebfae5563f1b6624600bd7b690a6c4e828bd09649a4e2742a4a476ead5419
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 rybex
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,215 @@
1
+ [![Build Status](https://travis-ci.org/hanami/hanami.svg?branch=master)](https://travis-ci.org/hanami/hanami)
2
+ [![Gem Version](https://badge.fury.io/rb/kawaii-api.svg)](https://badge.fury.io/rb/kawaii-api)
3
+ [![CodeClimate](https://codeclimate.com/github/rybex/kawaii/badges/gpa.svg)](https://codeclimate.com/github/rybex/kawaii)
4
+
5
+ # Kawaii
6
+
7
+ I would like to share new web API framework based on Rack.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'kawaii-api'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install kawaii-api
24
+
25
+ ## Creating new project
26
+
27
+ To create new project structure execute:
28
+
29
+ $ kawaii-api new app_name
30
+
31
+ And then execute:
32
+
33
+ $ cd app_name
34
+ $ bundle install
35
+ $ rackup
36
+
37
+ ## Defining routes
38
+ You can define routes in many ways. All routes have to be defined in class which interhits from `Kawaii::Routing::Routes` class. This is why you can easily split your routes into many files.
39
+
40
+ ### Support HTTP methods
41
+
42
+ ```ruby
43
+ class Routes < Kawaii::Routing::Routes
44
+
45
+ #you can specify mapping to controller class
46
+ get '/kawaii', "controller#method"
47
+ post '/kawaii', "controller#method"
48
+ put '/kawaii', "controller#method"
49
+ delete '/kawaii', "controller#method"
50
+
51
+ #or define block as a route handler
52
+ get '/kawaii' do |params, request|
53
+ {message: 'Hello'}
54
+ end
55
+ post '/kawaii' do |params, request|
56
+ {message: 'Hello'}
57
+ end
58
+ put '/kawaii' do |params, request|
59
+ {message: 'Hello'}
60
+ end
61
+ delete '/kawaii' do |params, request|
62
+ {message: 'Hello'}
63
+ end
64
+ end
65
+ ```
66
+
67
+ ### REST Resources
68
+
69
+ ```ruby
70
+ class Routes < Kawaii::Routing::Routes
71
+
72
+ resource :car
73
+ resources :house
74
+ end
75
+ ```
76
+
77
+ | HTTP_METHOD | PATH | MAPPING |
78
+ |------------:|-----------|-------------|
79
+ | :GET | /car | car#index |
80
+ | :GET | /car/new | car#new |
81
+ | :POST | /car | car#create |
82
+ | :GET | /car/edit | car#edit |
83
+ | :PUT | /car | car#update |
84
+ | :DELETE | /car | car#destroy |
85
+
86
+ ```ruby
87
+ class Routes < Kawaii::Routing::Routes
88
+
89
+ resources :car
90
+ end
91
+ ```
92
+
93
+ | HTTP_METHOD | PATH | MAPPING |
94
+ |------------:|---------------|-------------|
95
+ | :GET | /car | car#index |
96
+ | :GET | /car/new | car#new |
97
+ | :GET | /car/:id | car#show |
98
+ | :POST | /car | car#create |
99
+ | :GET | /car/:id/edit | car#edit |
100
+ | :PUT | /car/:id | car#update |
101
+ | :DELETE | /car/:id | car#destroy |
102
+
103
+ ####Nested resources
104
+
105
+ ```ruby
106
+ class Routes < Kawaii::Routing::Routes
107
+
108
+ resources :cars do
109
+ resource :wheel
110
+ end
111
+
112
+ resource :human do
113
+ resources :legs
114
+ end
115
+ end
116
+ ```
117
+
118
+ ####Additional options
119
+ You can specify which methods do you want to generate.
120
+
121
+ ```ruby
122
+ class Routes < Kawaii::Routing::Routes
123
+
124
+ resources :cars, [:index, :edit]
125
+ resource :wheel, [:index]
126
+ end
127
+ ```
128
+
129
+ ### Namespaced routes
130
+
131
+ ```ruby
132
+ class Routes < Kawaii::Routing::Routes
133
+
134
+ namespace :car do
135
+ resources :wheel
136
+
137
+ get :wheel, "controller#method"
138
+ end
139
+ end
140
+ ```
141
+
142
+ ## Creating Controllers
143
+ App Controllers should be placed in app/ directory. Every controller class must interhits from `Kawaii:Controller` class.
144
+
145
+ Let create resource routes.
146
+
147
+ ```ruby
148
+ class Routes < Kawaii::Routing::Routes
149
+
150
+ resource :car
151
+ end
152
+ ```
153
+
154
+ Then in `app/car_controller.rb` :
155
+
156
+ ```ruby
157
+ class CarController < Kawaii::Controller
158
+ def index
159
+ end
160
+
161
+ def new
162
+ end
163
+
164
+ def create
165
+ end
166
+
167
+ def edit
168
+ end
169
+
170
+ def update
171
+ end
172
+
173
+ def destroy
174
+ end
175
+ end
176
+ ```
177
+
178
+ ## Using Rack middlewares
179
+
180
+ ```ruby
181
+ class ModifyResponse
182
+ def initialize(app)
183
+ @app = app
184
+ end
185
+ attr_reader :app
186
+
187
+ def call(env)
188
+ status, headers, response = app.call(env)
189
+ response[0] = {message: 'Modified hello'}.to_json
190
+ [status, headers, response]
191
+ end
192
+ end
193
+
194
+ class Application < Kawaii::App
195
+ use ModifyResponse
196
+ end
197
+ ```
198
+
199
+ ## Defining custom 404 handler
200
+
201
+ ```ruby
202
+ class Application < Kawaii::App
203
+ route_not_found do
204
+ [404, {Rack::CONTENT_TYPE => "application/json"}, [{ message: 'Route not exists'}.to_json]]
205
+ end
206
+ end
207
+ ```
208
+
209
+ ## Contributing
210
+
211
+ 1. Fork it ( https://github.com/[my-github-username]/kawaii/fork )
212
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
213
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
214
+ 4. Push to the branch (`git push origin my-new-feature`)
215
+ 5. Create a new Pull Request
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler'
4
+ require 'kawaii/cli'
5
+ require 'kawaii/generators/new_project'
6
+ Kawaii::CLI.start
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'kawaii/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'kawaii-api'
8
+ spec.version = Kawaii::VERSION
9
+ spec.authors = ['rybex']
10
+ spec.email = ['tomek.rybka@gmail.com']
11
+ spec.summary = 'Micro API framework'
12
+ spec.description = 'Micro API framework to develop web applications in ruby'
13
+ spec.homepage = 'https://github.com/rybex/kawaii'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z -- lib/* bin/* LICENSE.txt README.md kawaii.gemspec`.split("\x0")
17
+ spec.executables = ['kawaii-api']
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec', '~> 3.4.0'
24
+ spec.add_development_dependency 'pry', '~> 0.10.3'
25
+ spec.add_development_dependency 'rubocop', '~> 0.40'
26
+ spec.add_development_dependency 'rack-test', '~> 0.6.3'
27
+
28
+ spec.add_runtime_dependency 'rack', '~> 1.6.4'
29
+ spec.add_runtime_dependency 'thor', '~> 0.19.1'
30
+ end
@@ -0,0 +1,20 @@
1
+ require 'rack'
2
+ require 'kawaii/routing/routes'
3
+ require 'kawaii/routing/handler'
4
+ require 'kawaii/routing/route'
5
+ require 'kawaii/routing/namespace'
6
+ require 'kawaii/routing/nested_path'
7
+ require 'kawaii/routing/resource'
8
+ require 'kawaii/routing/resources'
9
+ require 'kawaii/routing/member'
10
+ require 'kawaii/routing/collection'
11
+ require 'kawaii/routing/action'
12
+ require 'kawaii/routing/services/generate_resource_actions'
13
+ require 'kawaii/routing/services/generate_resources_actions'
14
+ require 'kawaii/routing/services/extract_params_from_url'
15
+ require 'kawaii/routing/services/find_controller'
16
+ require 'kawaii/app'
17
+ require 'kawaii/controller'
18
+ require 'kawaii/version'
19
+ require 'kawaii/errors'
20
+ require 'kawaii/router'
@@ -0,0 +1,47 @@
1
+ module Kawaii
2
+ class App
3
+
4
+ def call(env)
5
+ self.class.process_request(env)
6
+ rescue Kawaii::RouteNotFound => error
7
+ self.class.handle_route_not_found(error)
8
+ end
9
+
10
+ class << self
11
+
12
+ def use(middleware, *args, &block)
13
+ rack_builder.use(middleware, *args, &block)
14
+ end
15
+
16
+ def route_not_found(&block)
17
+ @exception_handler = block
18
+ end
19
+
20
+ def process_request(env)
21
+ route = router.match(env)
22
+ rack_builder.run route.handler
23
+ handler = rack_builder.to_app
24
+ handler.call(env)
25
+ end
26
+
27
+ def rack_builder
28
+ @rack_builder ||= Rack::Builder.new
29
+ end
30
+
31
+ def router
32
+ @router ||= Router.new
33
+ end
34
+
35
+ def handle_route_not_found(error)
36
+ handler = @exception_handler || error_handler(error)
37
+ handler.call(error)
38
+ end
39
+
40
+ private
41
+
42
+ def error_handler(error)
43
+ Proc.new { raise error }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require 'thor'
2
+
3
+ module Kawaii
4
+ class CLI < Thor
5
+
6
+ desc 'new', 'Create new project structure'
7
+ def new(name)
8
+ new_project_generator(name).invoke_all
9
+ end
10
+
11
+ private
12
+
13
+ def new_project_generator(name)
14
+ Generators::NewProject.new([name])
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Kawaii
2
+ class Controller
3
+ def initialize(params, request)
4
+ @params = params
5
+ @request = request
6
+ end
7
+ attr_reader :request, :params
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ module Kawaii
2
+ class MappingAndBlockProvided < StandardError
3
+ def message
4
+ 'You cant provide mapping and block for route'
5
+ end
6
+ end
7
+
8
+ class MappingOrBlockMissing < StandardError
9
+ def message
10
+ 'You must provice block on mapping'
11
+ end
12
+ end
13
+
14
+ class MissingBlockForNamespace < StandardError
15
+ def message
16
+ 'You must provide block for namespace'
17
+ end
18
+ end
19
+
20
+ class ControllerDoesntExist < StandardError
21
+ def message
22
+ 'Create controller endpoint for specified route'
23
+ end
24
+ end
25
+
26
+ class RouteNotFound < StandardError
27
+ def message
28
+ 'Please define route in routes file'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,75 @@
1
+ require 'thor'
2
+
3
+ module Kawaii
4
+ module Generators
5
+ class NewProject < Thor::Group
6
+ include Thor::Actions
7
+
8
+ TEMPLATES_DIR = 'templates/new_project'.freeze
9
+
10
+ argument :name
11
+
12
+ def self.source_root
13
+ File.dirname(__FILE__)
14
+ end
15
+
16
+ def create_gemfile
17
+ template(gemfile_template_location, gemfile_file_name)
18
+ end
19
+
20
+ def create_application_rb
21
+ template(application_template_location, application_file_name)
22
+ end
23
+
24
+ def create_config_ru
25
+ template(config_ru_template_location, config_ru_file_name)
26
+ end
27
+
28
+ def create_routes_ru
29
+ template(routes_template_location, routes_file_name)
30
+ end
31
+
32
+ def create_app_folder
33
+ empty_directory app_file_name
34
+ end
35
+
36
+ private
37
+
38
+ def gemfile_template_location
39
+ "#{TEMPLATES_DIR}/Gemfile.erb"
40
+ end
41
+
42
+ def application_template_location
43
+ "#{TEMPLATES_DIR}/application.rb.erb"
44
+ end
45
+
46
+ def config_ru_template_location
47
+ "#{TEMPLATES_DIR}/config.ru.erb"
48
+ end
49
+
50
+ def routes_template_location
51
+ "#{TEMPLATES_DIR}/routes.rb.erb"
52
+ end
53
+
54
+ def gemfile_file_name
55
+ "#{name}/Gemfile"
56
+ end
57
+
58
+ def application_file_name
59
+ "#{name}/application.rb"
60
+ end
61
+
62
+ def config_ru_file_name
63
+ "#{name}/config.ru"
64
+ end
65
+
66
+ def routes_file_name
67
+ "#{name}/config/routes.rb"
68
+ end
69
+
70
+ def app_file_name
71
+ "#{name}/app"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'kawaii-api'
@@ -0,0 +1,10 @@
1
+ require 'kawaii'
2
+
3
+ ['/app/**/*.rb', '/config/*.rb'].each do |dir|
4
+ Dir["#{File.dirname(__FILE__)}#{dir}"].each {|file| require file }
5
+ end
6
+
7
+ module <%= name.capitalize %>
8
+ class Application < Kawaii::App
9
+ end
10
+ end
@@ -0,0 +1,2 @@
1
+ require './application.rb'
2
+ run <%= name.capitalize %>::Application.new
@@ -0,0 +1,3 @@
1
+ class Routes < Kawaii::Routing::Routes
2
+
3
+ end
@@ -0,0 +1,34 @@
1
+ module Kawaii
2
+ class Router
3
+ def initialize
4
+ @routes = Routing::Routes.routes
5
+ end
6
+
7
+ def match(env)
8
+ searched_route = create_route(env)
9
+ route = find_route(searched_route)
10
+ raise RouteNotFound unless route
11
+ route
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :routes
17
+
18
+ def find_route(searched_route)
19
+ routes.detect { |route| route == searched_route }
20
+ end
21
+
22
+ def create_route(env)
23
+ Routing::Route.new(request_path(env), request_method(env))
24
+ end
25
+
26
+ def request_path(env)
27
+ env['PATH_INFO']
28
+ end
29
+
30
+ def request_method(env)
31
+ env['REQUEST_METHOD'].to_sym
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Action
4
+ def initialize(name, http_method, path_pattern, controller_method)
5
+ @http_method = http_method
6
+ @path = path_pattern
7
+ @mapping = generate_mapping(name, controller_method)
8
+ end
9
+ attr_reader :http_method, :path, :mapping
10
+
11
+ private
12
+
13
+ def generate_path(path_pattern, name)
14
+ path_pattern % [name]
15
+ end
16
+
17
+ def generate_mapping(name, controller_method)
18
+ "#{name}##{controller_method}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Collection < Member
4
+ def initialize(routes, resource, &block)
5
+ @routes = routes
6
+ @path = resource_path(resource)
7
+ @resource = resource
8
+ instance_eval(&block) if block_given?
9
+ end
10
+
11
+ private
12
+
13
+ attr_reader :routes, :path, :parent_name
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,54 @@
1
+ require 'json'
2
+
3
+ module Kawaii
4
+ module Routing
5
+ class Handler
6
+ def initialize(path, mapping, block = nil)
7
+ @path = path
8
+ @mapping = mapping
9
+ @handler = block || controller_handler(mapping)
10
+ end
11
+
12
+ def call(env)
13
+ request = rack_request(env)
14
+ params = get_url_params(request_path(env))
15
+ response = call_handler(params, request)
16
+ format_response(response)
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :path, :mapping, :handler
22
+
23
+ def controller_handler(mapping)
24
+ Kawaii::Routing::Services::FindController.new.call(mapping)
25
+ end
26
+
27
+ def get_url_params(request_url)
28
+ Kawaii::Routing::Services::ExtractParamsFromUrl.new.call(path, request_url)
29
+ end
30
+
31
+ def rack_request(env)
32
+ Rack::Request.new(env)
33
+ end
34
+
35
+ def format_response(response)
36
+ [
37
+ 200,
38
+ {
39
+ Rack::CONTENT_TYPE => 'application/json'
40
+ },
41
+ [response.to_json]
42
+ ]
43
+ end
44
+
45
+ def call_handler(params, request)
46
+ handler.call(params, request)
47
+ end
48
+
49
+ def request_path(env)
50
+ env['PATH_INFO']
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,50 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Member
4
+ def initialize(routes, resource, &block)
5
+ @routes = routes
6
+ @path = resource_path(resource)
7
+ @resource = resource
8
+ instance_eval(&block) if block_given?
9
+ end
10
+
11
+ def get(action_name)
12
+ routes.get(member_path(action_name), member_mapping(action_name))
13
+ end
14
+
15
+ def post(action_name)
16
+ routes.post(member_path(action_name), member_mapping(action_name))
17
+ end
18
+
19
+ def put(action_name)
20
+ routes.put(member_path(action_name), member_mapping(action_name))
21
+ end
22
+
23
+ def delete(action_name)
24
+ routes.delete(member_path(action_name), member_mapping(action_name))
25
+ end
26
+
27
+ protected
28
+
29
+ def resource_path(resource)
30
+ NestedPath.new(resource, self.class).path
31
+ end
32
+
33
+ def resource_name
34
+ resource.resource_name
35
+ end
36
+
37
+ def member_path(action_name)
38
+ "#{path}/#{action_name}"
39
+ end
40
+
41
+ def member_mapping(action_name)
42
+ "#{resource_name}##{action_name}"
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :routes, :path, :resource
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Namespace
4
+ def initialize(routes, namespace_name, &block)
5
+ @routes = routes
6
+ @namespace_name = namespace_name
7
+ instance_eval(&block) if block_given?
8
+ end
9
+
10
+ def get(path, mapping)
11
+ routes.get(namespace_path(path), wrap_in_namespace(mapping))
12
+ end
13
+
14
+ def post(path, mapping)
15
+ routes.post(namespace_path(path), wrap_in_namespace(mapping))
16
+ end
17
+
18
+ def put(path, mapping)
19
+ routes.put(namespace_path(path), wrap_in_namespace(mapping))
20
+ end
21
+
22
+ def delete(path, mapping)
23
+ routes.delete(namespace_path(path), wrap_in_namespace(mapping))
24
+ end
25
+
26
+ def resource(resource_name, methods = nil, &block)
27
+ Resource.new(routes, resource_name, methods, namespace_name, &block)
28
+ end
29
+
30
+ def resources(resources_name, methods = nil, &block)
31
+ Resources.new(routes, resources_name, methods, namespace_name, &block)
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :routes, :namespace_name
37
+
38
+ def namespace_path(path)
39
+ "/#{namespace_name}/#{path}"
40
+ end
41
+
42
+ def wrap_in_namespace(mapping)
43
+ "#{namespace_name}/#{mapping}"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ module Kawaii
2
+ module Routing
3
+ class NestedPath
4
+ def initialize(resource, action_class = nil)
5
+ @resource = resource
6
+ @action_class = action_class
7
+ @path = wrap_namespace(resource, calculate(resource))
8
+ end
9
+ attr_reader :resource, :path, :action_class
10
+
11
+ private
12
+
13
+ def calculate(nested = nil, path = nil)
14
+ unless path
15
+ path = init_path
16
+ else
17
+ path.prepend nested.resource_path
18
+ end
19
+ calculate(nested.parent, path) if nested.parent
20
+ path
21
+ end
22
+
23
+ def init_path
24
+ if resource.class == Resources && action_class == Member
25
+ resource_path
26
+ else
27
+ resource_name
28
+ end
29
+ end
30
+
31
+ def resource_name
32
+ "/#{resource.name}"
33
+ end
34
+
35
+ def resource_path
36
+ resource.resource_path
37
+ end
38
+
39
+ def wrap_namespace(nested, path)
40
+ if nested.namespace
41
+ path.prepend "/#{nested.namespace}"
42
+ end
43
+ path
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,58 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Resource
4
+ METHODS = [:index, :new, :create, :edit, :update, :destroy].freeze
5
+
6
+ def initialize(routes, name, methods, namespace = nil, parent = nil, &block)
7
+ @routes = routes
8
+ @name = name
9
+ @parent = parent
10
+ @methods = methods || METHODS
11
+ @namespace = namespace
12
+ @actions_generator = Services::GenerateResourceActions
13
+ generate_actions
14
+ instance_eval(&block) if block_given?
15
+ end
16
+ attr_reader :routes, :name, :parent, :namespace, :actions_generator
17
+
18
+ def member(&block)
19
+ Member.new(routes, self, &block)
20
+ end
21
+
22
+ def collection(&block)
23
+ Collection.new(routes, self, &block)
24
+ end
25
+
26
+ def resource(resource_name, methods = nil, &block)
27
+ Resource.new(routes, resource_name, methods, namespace, self, &block)
28
+ end
29
+
30
+ def resources(resources_name, methods = nil, &block)
31
+ Resources.new(routes, resources_name, methods, namespace, self, &block)
32
+ end
33
+
34
+ def resource_path
35
+ "/#{name}"
36
+ end
37
+
38
+ def resource_name
39
+ resource_name = name.to_s
40
+ resource_name.prepend "#{namespace}/" if namespace
41
+ resource_name
42
+ end
43
+
44
+ protected
45
+
46
+ def resource_full_path
47
+ NestedPath.new(self).path
48
+ end
49
+
50
+ def generate_actions
51
+ actions = actions_generator.new(resource_name, resource_full_path, @methods).call
52
+ actions.each do |action|
53
+ routes.send(action.http_method, action.path, action.mapping)
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,23 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Resources < Resource
4
+ METHODS = [:index, :new, :show, :create, :edit, :update, :destroy].freeze
5
+
6
+ def initialize(routes, name, methods, namespace = nil, parent = nil, &block)
7
+ @routes = routes
8
+ @name = name
9
+ @parent = parent
10
+ @methods = methods || METHODS
11
+ @namespace = namespace
12
+ @actions_generator = Services::GenerateResourcesActions
13
+ generate_actions
14
+ instance_eval(&block) if block_given?
15
+ end
16
+ attr_reader :routes, :name, :parent, :namespace, :actions_generator
17
+
18
+ def resource_path
19
+ "/#{name}/:#{name.to_s.split('/').last}_id"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Route
4
+ def initialize(path, http_method, mapping = nil, block = nil)
5
+ @path = path
6
+ @http_method = http_method
7
+ @mapping = mapping
8
+ @handler = route_handler(path, mapping, block)
9
+ end
10
+ attr_reader :path, :mapping, :handler, :http_method
11
+
12
+ def ==(other)
13
+ path == other.path && http_method == other.http_method
14
+ end
15
+
16
+ private
17
+
18
+ def route_handler(path, mapping, block)
19
+ Handler.new(path, mapping, block) if mapping || block
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,65 @@
1
+ module Kawaii
2
+ module Routing
3
+ class Routes
4
+ @@routes ||= []
5
+
6
+ class << self
7
+ def get(path, mapping = nil, &block)
8
+ create_route(path, mapping, :GET, block)
9
+ end
10
+
11
+ def post(path, mapping = nil, &block)
12
+ create_route(path, mapping, :POST, block)
13
+ end
14
+
15
+ def put(path, mapping = nil, &block)
16
+ create_route(path, mapping, :PUT, block)
17
+ end
18
+
19
+ def delete(path, mapping = nil, &block)
20
+ create_route(path, mapping, :DELETE, block)
21
+ end
22
+
23
+ def resources(name, methods = nil, &block)
24
+ Resources.new(self, name, methods, &block)
25
+ end
26
+
27
+ def resource(name, methods = nil, &block)
28
+ Resource.new(self, name, methods, &block)
29
+ end
30
+
31
+ def namespace(name, &block)
32
+ Namespace.new(self, name, &block)
33
+ end
34
+
35
+ def routes
36
+ @@routes
37
+ end
38
+
39
+ def reset
40
+ @@routes = []
41
+ end
42
+
43
+ private
44
+
45
+ def create_route(path, mapping, http_method, block)
46
+ validate_arguments(mapping, block)
47
+ add_route_to_list(route(path, mapping, http_method, block))
48
+ end
49
+
50
+ def validate_arguments(mapping, block)
51
+ raise MappingAndBlockProvided if mapping && block
52
+ raise MappingOrBlockMissing unless mapping || block
53
+ end
54
+
55
+ def add_route_to_list(route)
56
+ @@routes.push(route)
57
+ end
58
+
59
+ def route(path, mapping, http_method, block)
60
+ Route.new(path, http_method, mapping, block)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,23 @@
1
+ module Kawaii
2
+ module Routing
3
+ module Services
4
+ class ExtractParamsFromUrl
5
+ def call(path_pattern, url)
6
+ regexp = pattern_to_regexp(path_pattern)
7
+ matches = regexp.match(url)
8
+ format_to_hash(matches)
9
+ end
10
+
11
+ private
12
+
13
+ def format_to_hash(matches)
14
+ Hash[matches.names.zip(matches[1..-1])]
15
+ end
16
+
17
+ def pattern_to_regexp(pattern)
18
+ Regexp.new(Regexp.escape(pattern).gsub(/:(\w+)/, '(?<\1>.+?)'))
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,48 @@
1
+ module Kawaii
2
+ module Routing
3
+ module Services
4
+ class FindController
5
+ def call(mapping)
6
+ controller_name, method = parse_mapping(mapping)
7
+ controller_class = find_controller(controller_name)
8
+ proc do |params, request|
9
+ raise ControllerDoesntExist if controller_class.nil?
10
+ controller = controller_class.new(params, request)
11
+ controller.send(method)
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def find_controller(controller_name)
18
+ Object.const_get(controller_name)
19
+ rescue
20
+ nil
21
+ end
22
+
23
+ def parse_mapping(mapping)
24
+ mapping_with_namespace = mapping.split('/')
25
+ namespace = mapping_with_namespace[0] if mapping_with_namespace.length > 1
26
+ controller, method = mapping_with_namespace.last.split('#')
27
+ [get_controller(namespace, controller), method.to_sym]
28
+ end
29
+
30
+ def get_controller(namespace, controller)
31
+ controller_name = controller_name(controller)
32
+ if namespace
33
+ controller_name = wrap_namespace(namespace, controller_name)
34
+ end
35
+ controller_name
36
+ end
37
+
38
+ def controller_name(controller)
39
+ "#{controller.capitalize}Controller"
40
+ end
41
+
42
+ def wrap_namespace(namespace, controller_name)
43
+ "#{namespace.capitalize}::#{controller_name}"
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,53 @@
1
+ module Kawaii
2
+ module Routing
3
+ module Services
4
+ class GenerateResourceActions
5
+ def initialize(name, path, methods)
6
+ @name = name.to_s
7
+ @methods = methods
8
+ @path = path
9
+ end
10
+
11
+ def call
12
+ methods.map do |method|
13
+ send(method)
14
+ end
15
+ end
16
+
17
+ protected
18
+
19
+ def index
20
+ get_action(name, :get, path.to_s, 'index')
21
+ end
22
+
23
+ def new
24
+ get_action(name, :get, "#{path}/new", 'new')
25
+ end
26
+
27
+ def edit
28
+ get_action(name, :get, "#{path}/edit", 'edit')
29
+ end
30
+
31
+ def create
32
+ get_action(name, :post, path.to_s, 'create')
33
+ end
34
+
35
+ def update
36
+ get_action(name, :put, path.to_s, 'update')
37
+ end
38
+
39
+ def destroy
40
+ get_action(name, :delete, path.to_s, 'destroy')
41
+ end
42
+
43
+ def get_action(name, http_method, path_pattern, controller_method)
44
+ Routing::Action.new(name, http_method, path_pattern, controller_method)
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :methods, :name, :path
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,35 @@
1
+ module Kawaii
2
+ module Routing
3
+ module Services
4
+ class GenerateResourcesActions < GenerateResourceActions
5
+ def initialize(name, path, methods)
6
+ @name = name.to_s
7
+ @methods = methods
8
+ @path = path
9
+ end
10
+
11
+ protected
12
+
13
+ def show
14
+ get_action(name, :get, "#{path}/:id", 'show')
15
+ end
16
+
17
+ def edit
18
+ get_action(name, :get, "#{path}/:id/edit", 'edit')
19
+ end
20
+
21
+ def update
22
+ get_action(name, :put, "#{path}/:id", 'update')
23
+ end
24
+
25
+ def destroy
26
+ get_action(name, :delete, "#{path}/:id", 'destroy')
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :methods, :name, :path
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module Kawaii
2
+ VERSION = '0.0.4'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kawaii-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - rybex
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.4.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.4.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.10.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.10.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.40'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.40'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.6.3
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.6.3
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.6.4
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 1.6.4
111
+ - !ruby/object:Gem::Dependency
112
+ name: thor
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.19.1
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.19.1
125
+ description: Micro API framework to develop web applications in ruby
126
+ email:
127
+ - tomek.rybka@gmail.com
128
+ executables:
129
+ - kawaii-api
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - LICENSE.txt
134
+ - README.md
135
+ - bin/kawaii-api
136
+ - kawaii.gemspec
137
+ - lib/kawaii.rb
138
+ - lib/kawaii/app.rb
139
+ - lib/kawaii/cli.rb
140
+ - lib/kawaii/controller.rb
141
+ - lib/kawaii/errors.rb
142
+ - lib/kawaii/generators/new_project.rb
143
+ - lib/kawaii/generators/templates/new_project/Gemfile.erb
144
+ - lib/kawaii/generators/templates/new_project/application.rb.erb
145
+ - lib/kawaii/generators/templates/new_project/config.ru.erb
146
+ - lib/kawaii/generators/templates/new_project/routes.rb.erb
147
+ - lib/kawaii/router.rb
148
+ - lib/kawaii/routing/action.rb
149
+ - lib/kawaii/routing/collection.rb
150
+ - lib/kawaii/routing/handler.rb
151
+ - lib/kawaii/routing/member.rb
152
+ - lib/kawaii/routing/namespace.rb
153
+ - lib/kawaii/routing/nested_path.rb
154
+ - lib/kawaii/routing/resource.rb
155
+ - lib/kawaii/routing/resources.rb
156
+ - lib/kawaii/routing/route.rb
157
+ - lib/kawaii/routing/routes.rb
158
+ - lib/kawaii/routing/services/extract_params_from_url.rb
159
+ - lib/kawaii/routing/services/find_controller.rb
160
+ - lib/kawaii/routing/services/generate_resource_actions.rb
161
+ - lib/kawaii/routing/services/generate_resources_actions.rb
162
+ - lib/kawaii/version.rb
163
+ homepage: https://github.com/rybex/kawaii
164
+ licenses:
165
+ - MIT
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.4.3
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Micro API framework
187
+ test_files: []