lydia 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bf7ff0ab21d348d83cab7edbf2fc60dc0d2742fa
4
+ data.tar.gz: 9160af15569f5469cb8f02c30b44cbbe489e196e
5
+ SHA512:
6
+ metadata.gz: 9a5fcbdeddb34ef8ab94d6408af99f37ddb2bd453196748e578612bb4e5cf821be42d1c074e7a65cc581dec33c8e46315ceeeee00cadb24bf3bf4c638700d9af
7
+ data.tar.gz: 2415bbd84f8af49363d86877ab5750984594b3581303d32f21a14699ba865e3331fc57bed6690c84db3900f25e991b24a2662ba0e499a5d70ec684523309ab1c
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1
7
+ - 2.2
8
+ - rbx-2
9
+ - jruby
10
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lydia.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Mirko Mignini
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ [![Build Status](https://travis-ci.org/MirkoMignini/lydia.svg)](https://travis-ci.org/MirkoMignini/lydia)
2
+ [![Coverage Status](https://coveralls.io/repos/MirkoMignini/lydia/badge.svg?branch=master&service=github)](https://coveralls.io/github/MirkoMignini/lydia?branch=master)
3
+ [![Code Climate](https://codeclimate.com/github/MirkoMignini/lydia/badges/gpa.svg)](https://codeclimate.com/github/MirkoMignini/lydia)
4
+
5
+ # Lydia
6
+
7
+ Lightweight, fast and easy to use small ruby web framework.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'lydia'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install lydia
24
+
25
+ ## Another ruby web framework? WTF?
26
+
27
+ This project is not intended to become a top notch framework or the new rails, it's just an experiment.
28
+ The goals of this project are:
29
+
30
+ * [Rack](https://github.com/rack/rack/) based.
31
+ * Modular (Router, Application...).
32
+ * A powerful router that works stand alone too.
33
+ * Easy templates support using [Tilt](https://github.com/rtomayko/tilt).
34
+ * Well written, easy to read and to understand code.
35
+ * Less lines of code as possible (but no [code golf](https://en.wikipedia.org/wiki/Code_golf)).
36
+ * [Don't repeat yourself](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself).
37
+ * [100% test code coverage](https://coveralls.io/github/MirkoMignini/lydia?branch=master).
38
+ * [Continuos integration](https://travis-ci.org/MirkoMignini/lydia).
39
+ * Highest codeclimate score and [0 issues](https://codeclimate.com/github/MirkoMignini/lydia/issues).
40
+
41
+ ## Usage
42
+
43
+ Usage instructions soon available!
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
52
+
53
+ ## License
54
+
55
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
56
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "lydia/application"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'lydia'
3
+
4
+ get '/' do
5
+ 'Hello world!'
6
+ end
data/lib/lydia.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rack'
2
+ require 'lydia/application'
3
+ require 'lydia/delegator'
4
+ require 'lydia/version'
5
+
6
+ module Lydia
7
+ at_exit { Rack::Handler.default.run(Application) }
8
+ end
9
+
10
+ extend Lydia::Delegator
@@ -0,0 +1,53 @@
1
+ require 'forwardable'
2
+ require 'rack/builder'
3
+ require 'lydia/router'
4
+ require 'lydia/templates'
5
+ require 'lydia/filters'
6
+ require 'lydia/helpers'
7
+ require 'lydia/request'
8
+ require 'lydia/response'
9
+
10
+ module Lydia
11
+ class Application < Router
12
+ include Templates
13
+ include Filters
14
+ include Helpers
15
+
16
+ def process
17
+ result = super
18
+ if result.nil?
19
+ @response.build(200)
20
+ elsif result.class <= Rack::Response
21
+ result.finish
22
+ else
23
+ @response.build(result)
24
+ end
25
+ end
26
+
27
+ def new_request(env)
28
+ Lydia::Request.new(env)
29
+ end
30
+
31
+ def new_response(body = [], status = 200, header = {})
32
+ Lydia::Response.new(body, status, header)
33
+ end
34
+
35
+ class << self
36
+ extend Forwardable
37
+
38
+ def_delegators :builder, :map, :use, :run
39
+
40
+ def builder
41
+ @builder ||= Rack::Builder.new
42
+ end
43
+
44
+ alias new! new
45
+
46
+ def new(*args, &bk)
47
+ app = new!(*args, &bk)
48
+ builder.run(app)
49
+ builder.to_app
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,12 @@
1
+ require 'lydia/application'
2
+ require 'forwardable'
3
+
4
+ module Lydia
5
+ module Delegator
6
+ extend Forwardable
7
+ def_delegators Lydia::Application,
8
+ :head, :get, :patch, :put, :post, :delete, :options,
9
+ :namespace, :before, :after,
10
+ :map, :use, :run
11
+ end
12
+ end
@@ -0,0 +1,52 @@
1
+ require 'lydia/route'
2
+
3
+ module Lydia
4
+ module Filters
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def filters
11
+ @filters ||= Hash.new { |h, k| h[k] = [] }
12
+ end
13
+
14
+ %w(before after).each do |filter|
15
+ define_method(filter) do |pattern = '/*', options = {}, &block|
16
+ filters[filter.to_sym] << Route.new(@namespace || '', pattern, options, &block)
17
+ end
18
+ end
19
+
20
+ def redirect(pattern, options = {})
21
+ return ArgumentError.new('Options must contains :to') unless options.include?(:to)
22
+ filters[:redirect] << Route.new(@namespace || '', pattern, options)
23
+ end
24
+ end
25
+
26
+ def dispatch(env)
27
+ process_redirects(env)
28
+ process_before_filters(env)
29
+ result = super(env)
30
+ process_after_filters(env)
31
+ result
32
+ end
33
+
34
+ %w(before after).each do |filter_type|
35
+ define_method("process_#{filter_type}_filters") do |env|
36
+ self.class.filters[filter_type.to_sym].each do |filter|
37
+ instance_eval(&filter.block) if filter.match?(env)
38
+ end
39
+ end
40
+ end
41
+
42
+ def process_redirects(env)
43
+ self.class.filters[:redirect].each do |redirect|
44
+ if redirect.match?(env)
45
+ env['PATH_INFO'] = redirect.namespace + redirect.options[:to]
46
+ @request = new_request(env)
47
+ break
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,9 @@
1
+ module Lydia
2
+ class Halted < StandardError
3
+ attr_reader :content
4
+ def initialize(content)
5
+ @content = content
6
+ super
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Lydia
2
+ module Helpers
3
+ def content_type(content)
4
+ @response.header['Content-Type'] = content
5
+ end
6
+
7
+ def redirect(target, status = 302)
8
+ [status, target]
9
+ end
10
+
11
+ def params
12
+ @request.params
13
+ end
14
+
15
+ def send_file(path, mime_type = nil)
16
+ raise NotImplementedError.new
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module Lydia
2
+ class NotFound < StandardError
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ require 'rack/request'
2
+
3
+ module Lydia
4
+ class Request < Rack::Request
5
+ end
6
+ end
@@ -0,0 +1,33 @@
1
+ require 'rack/response'
2
+
3
+ module Lydia
4
+ class Response < Rack::Response
5
+ def initialize(*)
6
+ super
7
+ headers['Content-Type'] = 'text/html' if headers['Content-Type'].nil?
8
+ end
9
+
10
+ def build(input)
11
+ case input.class.to_s
12
+ when 'String'
13
+ write input
14
+ when 'Array'
15
+ @status = input.first
16
+ write input.last.is_a?(Array) ? input.last[0] : input.last
17
+ headers.merge!(input[1]) if input.count == 3
18
+ when 'Fixnum'
19
+ @status = input
20
+ when 'Hash'
21
+ headers['Content-Type'] = 'application/json'
22
+ write input.to_json
23
+ else
24
+ if input.respond_to?(:each)
25
+ write input
26
+ else
27
+ raise ArgumentError.new("#{input.class} is not a valid allowed return type")
28
+ end
29
+ end
30
+ finish
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,36 @@
1
+ module Lydia
2
+ class Route
3
+ attr_reader :regexp, :params, :namespace, :pattern, :options, :block
4
+
5
+ WILDCARD_REGEX = /\/\*(.*)/.freeze
6
+ NAMED_SEGMENTS_REGEX = /\/([^\/]*):([^:$\/]+)/.freeze
7
+
8
+ def initialize(namespace, pattern, options = {}, &block)
9
+ @namespace = namespace
10
+ @pattern = pattern
11
+ @options = options
12
+ @block = block
13
+ if pattern.is_a? String
14
+ path = (namespace || '') + pattern
15
+ if path.match(WILDCARD_REGEX)
16
+ result = path.gsub(WILDCARD_REGEX, '(?:/(.*)|)')
17
+ elsif path.match(NAMED_SEGMENTS_REGEX)
18
+ result = path.gsub(NAMED_SEGMENTS_REGEX, '/\1(?<\2>[^.$/]+)')
19
+ else
20
+ result = path
21
+ end
22
+ @regexp = Regexp.new("\\A#{result}\\z")
23
+ elsif pattern.is_a?(Regexp)
24
+ @regexp = pattern
25
+ else
26
+ raise ArgumentError.new('Pattern must be a string or a regex')
27
+ end
28
+ end
29
+
30
+ def match?(env)
31
+ match = @regexp.match("#{env['PATH_INFO']}")
32
+ @params = Hash[match.names.map(&:to_sym).zip(match.captures)] if match && match.names.size
33
+ match
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,86 @@
1
+ require 'lydia/route'
2
+ require 'lydia/standard_pages'
3
+ require 'lydia/not_found'
4
+ require 'lydia/halted'
5
+ require 'rack/request'
6
+ require 'rack/response'
7
+
8
+ module Lydia
9
+ class Router
10
+ include StandardPages
11
+
12
+ attr_reader :request, :response, :env
13
+
14
+ class << self
15
+ def routes
16
+ @routes ||= Hash.new { |h, k| h[k] = [] }
17
+ end
18
+
19
+ %w(HEAD GET PATCH PUT POST DELETE OPTIONS).each do |request_method|
20
+ define_method(request_method.downcase) do |pattern, options = {}, &block|
21
+ routes[request_method] << Route.new(@namespace, pattern, options, &block)
22
+ end
23
+ end
24
+
25
+ def namespace(pattern, options = {})
26
+ prev_namespace = @namespace ||= ''
27
+ @namespace += pattern
28
+ yield
29
+ @namespace = prev_namespace
30
+ end
31
+
32
+ def call(env)
33
+ new.call(env)
34
+ end
35
+ end
36
+
37
+ def next_route
38
+ throw :next_route
39
+ end
40
+
41
+ def halt(input = nil)
42
+ raise Halted.new(input || halted)
43
+ end
44
+
45
+ def call(env)
46
+ dup._call(env)
47
+ end
48
+
49
+ def _call(env)
50
+ @env = env
51
+ @request = new_request(env)
52
+ @response = new_response
53
+ process
54
+ end
55
+
56
+ def new_request(env)
57
+ Rack::Request.new(env)
58
+ end
59
+
60
+ def new_response(body = [], status = 200, header = {})
61
+ Rack::Response.new(body, status, header)
62
+ end
63
+
64
+ def process
65
+ dispatch(env)
66
+ rescue NotFound
67
+ not_found(env)
68
+ rescue Halted => exception
69
+ exception.content
70
+ rescue StandardError => exception
71
+ internal_server_error(env, exception)
72
+ end
73
+
74
+ def dispatch(env)
75
+ self.class.routes[env['REQUEST_METHOD']].each do |route|
76
+ if route.match?(env)
77
+ @request.params.merge!(route.params) if route.params
78
+ catch(:next_route) do
79
+ return instance_eval(&route.block)
80
+ end
81
+ end
82
+ end
83
+ raise NotFound
84
+ end
85
+ end
86
+ end