track 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1,6 @@
1
+ # Changelog
2
+
3
+ ## v 0.4.0
4
+
5
+ - complete rewrite
1
6
 
data/README.md CHANGED
@@ -1,17 +1,17 @@
1
1
  # Track
2
2
 
3
- A nano framework for server applications based on rack and ruby 1.9 named capture groups.
3
+ A nano framework for server applications based on rack.
4
4
 
5
5
  ## What Track does
6
6
 
7
- - modularize your rack app through controllers
8
- - routes paths to methods inside of your controllers via the `route` method
9
- - define before filters via the `pre` method
10
- - ActiveRecord initializer through `require 'track/orm/active_record'`
7
+ - modularize your rack app with controllers
8
+ - routes paths to methods of controllers via `Track::Routes.define` in `config/routes.rb`
9
+ - lets you define before- and after-filters in your controllers
11
10
 
12
11
  ## What Track does *not*
13
12
 
14
13
  - support any template engines, your actions have to return low level rack responses
14
+ - no security layer
15
15
 
16
16
  ## Install
17
17
 
@@ -23,7 +23,11 @@ or install it via rubygems
23
23
 
24
24
  `gem install track`
25
25
 
26
- ## Install example application
26
+ ## Usage
27
+
28
+ Have a look at the example application. I will provide documentation and more examples in the future.
29
+
30
+ ### Install example application
27
31
 
28
32
  Clone the [http://github.com/larskuhnt/track-example](track-example) project:
29
33
 
@@ -37,44 +41,6 @@ or install it via the gem executable
37
41
  track new my_track_project
38
42
  ```
39
43
 
40
- ## Usage
41
-
42
- Subclass the `Track::Controller` class to define controllers:
43
-
44
- ### Example
45
-
46
- ```ruby
47
- class UsersController < Track::Controller
48
-
49
- route '/', :index, :get
50
- route '/show/:id', :show, :get
51
- route '/update/(?<id> [^\/]+)', :show, [:post, :put]
52
-
53
- pre :find_user, :show
54
-
55
- def index
56
- [200, { "Content-Type" => 'text/plain' }, StringIO.new('hello from index')]
57
- end
58
-
59
- def show
60
- [200, { "Content-Type" => 'text/plain' }, StringIO.new(@user.name)]
61
- end
62
-
63
- private
64
-
65
- def find_user
66
- @user = User.find(params['id'])
67
- fail [404, { "Content-Type" => 'text/plain' }, StringIO.new('user not found')] unless @user
68
- end
69
- end
70
- ```
71
-
72
- The `route` method maps a route to an action in your controller. You can define named parameters by appending a : or by using a named capture group.
73
-
74
- You can build arbitrary path match patterns by using regaular expressions.
75
-
76
- The `pre` method calls a method prior to the action. If `fail` is called in before filter method the action will not get called and the given response will be returned.
77
-
78
44
  ## Plugins
79
45
 
80
46
  Currently available:
data/ROADMAP.md CHANGED
@@ -0,0 +1,8 @@
1
+ # Track
2
+
3
+ - add logging
4
+
5
+ # FilterMap
6
+
7
+ - add controller/action cache
8
+
@@ -1,8 +1,14 @@
1
1
  require 'rack'
2
+ require 'track/application'
3
+ require 'track/routes'
2
4
  require 'track/controller'
3
5
 
4
6
  module Track
5
7
 
8
+ @@responses = {
9
+ :routing_error => [404, { 'Content-Type' => 'text/plain'}, ['Route not found']],
10
+ :not_found => [404, { 'Content-Type' => 'text/plain'}, ['Resource not found']]
11
+ }
6
12
  @@env = ENV['RACK_ENV'].to_sym.freeze
7
13
  @@config = nil
8
14
  @@root = nil
@@ -13,6 +19,7 @@ module Track
13
19
  @@root = root
14
20
  @@config = load_config_file!(:config)
15
21
  boot_plugins!
22
+ Routes.load! File.join(root, 'config', 'routes')
16
23
  end
17
24
 
18
25
  def [](key)
@@ -39,17 +46,16 @@ module Track
39
46
  VERSION
40
47
  end
41
48
 
42
- def use(plugin)
49
+ def plugin(plugin)
43
50
  @@plugins << plugin
44
51
  end
45
52
 
53
+ def responses
54
+ @@responses
55
+ end
56
+
46
57
  def load_config_file!(filename)
47
- begin
48
- YAML.load(File.open(File.join(root, 'config', "#{filename}.yml")))[env.to_s]
49
- rescue
50
- raise "Config file config/config.yml is missing!" if env?(:production)
51
- YAML.load(File.open(File.join(root, 'config', "#{filename}.example.yml")))[env.to_s]
52
- end
58
+ YAML.load(File.open(File.join(root, 'config', "#{filename}.yml")))[env.to_s]
53
59
  end
54
60
 
55
61
  def boot_plugins!
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module Track
4
+ class Application
5
+
6
+ def call(env)
7
+ req = Rack::Request.new(env)
8
+ if route = Routes.find(req.request_method, req.path_info)
9
+ controller = route[:class].new(env, req.params.merge(route[:matches]))
10
+ if response = controller.run_filters(:before, route[:action])
11
+ return response
12
+ else
13
+ response = controller.send(route[:action])
14
+ controller.run_filters(:after, route[:action])
15
+ response
16
+ end
17
+ else
18
+ Track.responses[:routing_error]
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -1,57 +1,45 @@
1
1
  # encoding: utf-8
2
- require_relative 'route_map'
3
2
  require_relative 'filter_map'
4
3
 
5
4
  module Track
6
5
  class Controller
7
6
 
8
- @@route_map = RouteMap.new
9
- @@filter_map = FilterMap.new
7
+ @@filters = FilterMap.new
10
8
 
11
- attr_accessor :params
9
+ attr_reader :env, :params
12
10
 
13
- def initialize
14
- @params = {}
11
+ def initialize(env, params)
12
+ @env = env
13
+ @params = params
15
14
  end
16
15
 
17
- def call(env)
18
- req = Rack::Request.new(env)
19
- @params.merge!(req.params)
20
- if route = @@route_map.scan(self.class.name, req)
21
- @params.merge!(route[:matches])
22
- response_for(route)
23
- else
24
- routing_error
16
+ def run_filters(kind, action)
17
+ @@filters.scan(self.class, kind, action).each do |filter_method|
18
+ self.send(filter_method)
19
+ return @_response if @_response
25
20
  end
21
+ nil
26
22
  end
27
23
 
28
- protected
29
-
30
- def routing_error
31
- [404, { 'Content-Type' => 'text/plain' }, ['route not found']]
32
- end
33
-
34
- def response_for(route)
35
- if filters = @@filter_map.scan(self.class.name, route[:action])
36
- filters.each do |m|
37
- send(m)
38
- return @_response if @_response
39
- end
40
- end
41
- send(route[:action])
42
- end
43
-
44
- def fail(response = [404, { 'Content-Type' => 'text/plain' }, ['']])
24
+ def respond(response)
45
25
  @_response = response
46
26
  end
47
27
 
48
- def self.route(pattern, action, methods = nil)
49
- @@route_map.add self.name, pattern, action, methods
28
+ class << self
29
+
30
+ def filters
31
+ @@filters
32
+ end
33
+
34
+ def before_filter(method, options = {})
35
+ @@filters.add(self, :before, method, options)
36
+ end
37
+
38
+ def after_filter(method, options = {})
39
+ @@filters.add(self, :after, method, options)
40
+ end
41
+
50
42
  end
51
43
 
52
- def self.pre(method, actions)
53
- @@filter_map.add self.name, method, actions
54
- end
55
-
56
44
  end
57
45
  end
@@ -1,23 +1,36 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Track
4
- class FilterMap < Hash
4
+ class FilterMap
5
5
 
6
- def add(key, method, actions)
7
- self[key] ||= {}
8
- actions = actions.is_a?(Array) ? actions.map(&:to_sym) : [actions.to_sym]
9
- actions.each do |action|
10
- self[key][action.to_sym] ||= []
11
- self[key][action.to_sym] << method.to_sym
12
- end
6
+ def initialize
7
+ @filters = {}
8
+ end
9
+
10
+ def add(klass, kind, method, options = {})
11
+ @filters[kind] ||= []
12
+ @filters[kind] << [method, options, klass]
13
13
  end
14
14
 
15
- def scan(key, action)
16
- if self[key]
17
- self[key][action.to_sym] ? self[key][action.to_sym] : []
18
- else
19
- []
15
+ def scan(klass, kind, action)
16
+ filters = []
17
+ if @filters[kind]
18
+ @filters[kind].each do |method, options, k|
19
+ next if contains?(options[:except], action) || !(klass <= k)
20
+ filters << method if contains?(options[:only], action) || blank?(options[:only])
21
+ end
20
22
  end
23
+ filters
24
+ end
25
+
26
+ private
27
+
28
+ def contains?(a, k)
29
+ a == k || (Array === a && a.include?(k)) ? true : false
30
+ end
31
+
32
+ def blank?(a)
33
+ a.nil? || a.size == 0
21
34
  end
22
35
 
23
36
  end
@@ -1,25 +1,36 @@
1
- # encoding: utf-8
2
1
 
3
2
  module Track
4
- class RouteMap < Hash
3
+ class RouteMap
5
4
 
6
- def add(clazz, pattern, action, methods)
5
+ def initialize
6
+ @routes = []
7
+ end
8
+
9
+ def add(pattern, klass, action, methods = nil)
10
+ raise "#{klass} is not a subclass of Track::Controller" unless klass <= Track::Controller
7
11
  methods = methods.is_a?(Array) ? methods : [methods] if methods
8
- self[clazz] ||= []
9
- self[clazz].push(
10
- :pattern => compile_regexp(pattern),
12
+ regex, keys = compile_regexp(pattern)
13
+ @routes.push(
14
+ :pattern => regex,
15
+ :keys => keys,
16
+ :class => klass,
11
17
  :action => action.to_sym,
12
18
  :methods => methods
13
19
  )
14
20
  end
21
+ alias_method :route, :add
15
22
 
16
- def scan(clazz, req)
17
- method = req.request_method.downcase.to_sym
18
- self[clazz].each do |route|
19
- md = req.path_info.match(route[:pattern])
20
- return route.merge(:matches => match_hash(md)) if md && allowed_method?(route, method)
23
+ def scan(path, method)
24
+ @routes.each do |route|
25
+ md = path.match(route[:pattern])
26
+ if md && allowed_method?(route, method)
27
+ keys = route[:keys]
28
+ matches = {}
29
+ (1..md.size-1).each { |i| matches[keys[i-1]] = md[i] }
30
+ return route.merge(:matches => matches)
31
+ end
21
32
  end
22
- false
33
+ nil
23
34
  end
24
35
 
25
36
  private
@@ -28,18 +39,14 @@ module Track
28
39
  route[:methods].nil? ? true : route[:methods].include?(method)
29
40
  end
30
41
 
31
- def match_hash(md)
32
- params = {}
33
- md.names.each do |key|
34
- params[key.to_s] = md[key]
35
- end
36
- params
37
- end
38
-
39
42
  def compile_regexp(pattern)
40
43
  pattern = (pattern[-1,1] == '/' ? pattern.chop : pattern) << '/?'
41
- pattern.gsub!(/:([^\/]+)/i, '(?<\1> [^/]+)')
42
- Regexp.new('\A'+pattern+'\z', Regexp::EXTENDED)
44
+ keys = []
45
+ pattern.gsub!(/:(\w+)/) do |m|
46
+ keys << $1
47
+ '([^\/#\?]+)'
48
+ end
49
+ [Regexp.new("^#{pattern}$"), keys]
43
50
  end
44
51
  end
45
52
  end
@@ -0,0 +1,27 @@
1
+ require_relative 'route_map'
2
+
3
+ module Track
4
+
5
+ class Routes
6
+
7
+ @@route_map = RouteMap.new
8
+
9
+ class << self
10
+
11
+ def load!(route_file)
12
+ require route_file
13
+ end
14
+
15
+ def define(&block)
16
+ @@route_map.instance_eval &block
17
+ end
18
+
19
+ def find(method, path)
20
+ @@route_map.scan(path, method.downcase.to_sym)
21
+ end
22
+
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Track
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: track
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-10 00:00:00.000000000Z
12
+ date: 2012-01-03 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
16
- requirement: &70223625727700 !ruby/object:Gem::Requirement
16
+ requirement: &70210397017080 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70223625727700
24
+ version_requirements: *70210397017080
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70223625725260 !ruby/object:Gem::Requirement
27
+ requirement: &70210397015520 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70223625725260
35
+ version_requirements: *70210397015520
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rack-test
38
- requirement: &70223625719580 !ruby/object:Gem::Requirement
38
+ requirement: &70210397014500 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,31 +43,8 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70223625719580
47
- - !ruby/object:Gem::Dependency
48
- name: active_record
49
- requirement: &70223625717640 !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: *70223625717640
58
- - !ruby/object:Gem::Dependency
59
- name: thor
60
- requirement: &70223625715720 !ruby/object:Gem::Requirement
61
- none: false
62
- requirements:
63
- - - ! '>='
64
- - !ruby/object:Gem::Version
65
- version: '0'
66
- type: :development
67
- prerelease: false
68
- version_requirements: *70223625715720
69
- description: Nano framework to build small server applications based on rack and ruby
70
- 1.9
46
+ version_requirements: *70210397014500
47
+ description: Nano framework to build small and fast server applications based on rack
71
48
  email:
72
49
  - lars.kuhnt@gmail.com
73
50
  executables:
@@ -75,9 +52,11 @@ executables:
75
52
  extensions: []
76
53
  extra_rdoc_files: []
77
54
  files:
55
+ - lib/track/application.rb
78
56
  - lib/track/controller.rb
79
57
  - lib/track/filter_map.rb
80
58
  - lib/track/route_map.rb
59
+ - lib/track/routes.rb
81
60
  - lib/track/version.rb
82
61
  - lib/track.rb
83
62
  - LICENSE
@@ -96,7 +75,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
96
75
  requirements:
97
76
  - - ! '>='
98
77
  - !ruby/object:Gem::Version
99
- version: '1.9'
78
+ version: '0'
100
79
  required_rubygems_version: !ruby/object:Gem::Requirement
101
80
  none: false
102
81
  requirements:
@@ -108,6 +87,5 @@ rubyforge_project:
108
87
  rubygems_version: 1.8.10
109
88
  signing_key:
110
89
  specification_version: 3
111
- summary: Nano framework to build small server applications based on rack and ruby
112
- 1.9
90
+ summary: Nano framework to build small server applications based on rack
113
91
  test_files: []