lydia 0.1.0

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.
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