lego-core 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ coverage
2
+ doc
3
+ tmp/**/*
data/README.rdoc ADDED
@@ -0,0 +1,95 @@
1
+ = Lego-core
2
+
3
+ It's all about the bits an pieces!
4
+
5
+ == Installation
6
+
7
+ sudo gem install lego-core
8
+
9
+ == TODO
10
+
11
+ - Write a text that describe what we are trying to do, and why :)
12
+ - Write a roadmap for lego-core
13
+ - Add before and after filter handling.
14
+
15
+ == Example plugin (plugin.rb)
16
+
17
+ # This is a sample plugin that extends a few different Lego contexts.
18
+ #
19
+ # - View plugin
20
+ # - Controller plugin
21
+ # - Route matcher plugin
22
+ #
23
+ # This code will prob. be held in a different gem
24
+
25
+ module SamplePlugin
26
+
27
+ # Register is called by lego when plugin is loaded
28
+
29
+ def self.register(lego)
30
+ lego.add_plugin :view, View
31
+ lego.add_plugin :router, Matcher
32
+ lego.add_plugin :controller, Routes
33
+ end
34
+
35
+ # A plugin module we load as a view helper
36
+
37
+ module View
38
+ def h1(content)
39
+ "<h1>#{content}</h1>"
40
+ end
41
+ end
42
+
43
+ # A very simply route helper
44
+
45
+ module Routes
46
+ def get(path, &block)
47
+ add_route(:get, {:path => path, :action_block => block})
48
+ end
49
+ end
50
+
51
+ # A very simple route matcher
52
+
53
+ module Matcher
54
+ def self.match_route(route, env)
55
+ (route[:path] == env['PATH_INFO']) ? true : false
56
+ end
57
+ end
58
+ end
59
+
60
+ == Example Lego Application (my_app.rb)
61
+
62
+ # This is how our Lego app would look with this plugin.
63
+
64
+ Lego.plugin SamplePlugin
65
+
66
+ class MyApp < Lego::Controller
67
+ get '/hello' do
68
+ h1 'Hello world'
69
+ end
70
+ end
71
+
72
+ == Example Rackup/Shotgun file (config.ru)
73
+
74
+ # This is how you would use it with rack.
75
+
76
+ require 'rubygems'
77
+ require 'lego-core'
78
+ require 'plugin'
79
+ require 'my_app'
80
+
81
+ run MyApp
82
+
83
+ == Example run
84
+
85
+ # Start this app by running shotgun or rackup (these gems nees to be installed)
86
+
87
+ shotgun config.ru
88
+
89
+ == Thoughts
90
+
91
+ - Move ActionContext and RouteHandler into different module that controller?
92
+ - Add a plugin Context for before and after filters to make them pluggable?
93
+ - Make the whole framework run inside an instance instead of as class methods?
94
+ - When route is matchen with a different method than the one requested you should not respond 404, support for this?
95
+
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'rcov/rcovtask'
4
+
5
+
6
+ begin
7
+ require 'spec/rake/spectask'
8
+ rescue LoadError
9
+ puts 'To use rspec for testing you must install rspec gem:'
10
+ puts '$ sudo gem install rspec'
11
+ exit
12
+ end
13
+
14
+ desc "Default task is to run specs"
15
+ task :default => :show_all_tasks
16
+
17
+ desc "Run all examples with RCov"
18
+ Spec::Rake::SpecTask.new('rcov') do |t|
19
+ t.spec_files = FileList['spec/**/*.rb']
20
+ t.spec_opts = ['--options', '"spec/spec.opts"']
21
+ # rcov
22
+ t.rcov = true
23
+ t.rcov_dir = 'doc/coverage'
24
+ t.rcov_opts = ['-p', '-T', '--exclude', 'spec']
25
+ end
26
+
27
+ desc "Run the specs under spec"
28
+ Spec::Rake::SpecTask.new do |t|
29
+ t.spec_opts = ['--diff', '--options', "spec/spec.opts"]
30
+ t.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ desc "Print Specdoc for all specs"
34
+ Spec::Rake::SpecTask.new('specdoc') do |t|
35
+ t.spec_opts = ["--format", "specdoc", "--dry-run", "--options", 'spec/spec.opts']
36
+ t.spec_files = FileList['spec/**/*_spec.rb']
37
+ end
38
+
39
+ desc "Generate RDoc documentation"
40
+ Rake::RDocTask.new(:rdoc) do |rdoc|
41
+ rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc' << '--charset' << 'utf-8'
42
+ rdoc.rdoc_dir = "doc"
43
+ rdoc.rdoc_files.include 'README.rdoc'
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
46
+
47
+ begin
48
+ require 'jeweler'
49
+ Jeweler::Tasks.new do |gemspec|
50
+ gemspec.name = "lego-core"
51
+ gemspec.summary = "It's all about the bits and peices"
52
+ gemspec.description = "It's all about the bits and peices"
53
+ gemspec.email = "mathias@globalinn.com"
54
+ gemspec.homepage = "http://github.com/stjernstrom/lego-core"
55
+ gemspec.authors = ["Mathias Stjernström", "Patrik Hedman"]
56
+ end
57
+ rescue LoadError
58
+ puts "Jeweler not available. Install it with: gem install jeweler"
59
+ end
60
+
61
+ task :show_all_tasks do
62
+ system "rake -T"
63
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,137 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), '..', 'lib', 'lego')
2
+
3
+ module BasicRoutes
4
+ def self.register(lego)
5
+ lego.add_plugin :controller, ControllerPlugin
6
+ lego.add_plugin :router, RouteMatcher
7
+ end
8
+
9
+ module ControllerPlugin
10
+ def get(path, &block)
11
+ add_route(:get, { :path => path, :action_block => block })
12
+ end
13
+ end
14
+
15
+ module RouteMatcher
16
+ def self.match_route(route, env)
17
+ (route[:path] == env['PATH_INFO']) ? true : false
18
+ end
19
+ end
20
+ end
21
+
22
+ module RegexpMatcher
23
+ def self.register(lego)
24
+ lego.add_plugin :router, self
25
+ end
26
+
27
+ def self.match_route(route, env)
28
+ if route[:path].is_a?(Regexp) && match = route[:path].match(env['PATH_INFO'])
29
+ route[:instance_vars] = { :caps => match.captures }
30
+ true
31
+ else
32
+ false
33
+ end
34
+ end
35
+ end
36
+
37
+ module SymbolExtractor
38
+ def self.register(lego)
39
+ lego.add_plugin :router, self
40
+ end
41
+
42
+ def self.match_route(route, env)
43
+ return false if not route[:path].kind_of?(String)
44
+ matches = route[:path].scan(/:([\w]+)/)
45
+ if matches.size > 0
46
+ exp = Regexp.escape( route[:path] ).gsub(/:([\w]+)/, "([\\w]+)")
47
+ if match = Regexp.new("^#{exp}$").match(env["PATH_INFO"])
48
+ route[:instance_vars] = {} if route[:instance_vars].nil?
49
+ 1.upto(matches.size) do |i|
50
+ route[:instance_vars][matches[i-1]] = match.captures[i-1]
51
+ end
52
+ return true
53
+ end
54
+ end
55
+ false
56
+ end
57
+ end
58
+
59
+ module HtmlPlug
60
+ def self.register(lego)
61
+ lego.add_plugin :view, Mod
62
+ end
63
+ module Mod
64
+ def h1(content)
65
+ "<h1>#{content}</h1>"
66
+ end
67
+ def b(content)
68
+ "<b>#{content}</b>"
69
+ end
70
+ end
71
+ end
72
+
73
+ class MyBlog < Lego::Controller
74
+ set :foo => "bar"
75
+
76
+ plugin BasicRoutes
77
+ plugin RegexpMatcher
78
+ plugin SymbolExtractor
79
+
80
+ # Sample ActionContext plugin giving h1() and b() methods to views.
81
+ plugin HtmlPlug
82
+
83
+ #
84
+ # Ex: http://localhost:9393/extract/something.jpg
85
+ # Sets up an instance variable with the matches
86
+ # @caps[0] => 'something'
87
+ # @caps[1] => 'jpg'
88
+ #
89
+ get /extract\/(\w+)\.(\w+)/ do
90
+ "We are extracting....#{@caps.inspect}"
91
+ end
92
+
93
+ #
94
+ # Ex: http://localhost:9393/
95
+ #
96
+ get '/' do
97
+ "This is /"
98
+ end
99
+
100
+ #
101
+ # ex: http://localhost:9393/show/10
102
+ # Sets up an instance variable with the id
103
+ # @id => 10
104
+ #
105
+ get '/show/:id' do
106
+ "This is show with id = #{@id}"
107
+ end
108
+
109
+ get '/options' do
110
+ options :foo # renders bar to the browser
111
+ end
112
+
113
+ #
114
+ # Demonstrating a simple ActionContext plugin (HtmlPlug)
115
+ #
116
+ # Ex: http://localhost:9393/viewplugins
117
+ #
118
+ get '/viewplugins' do
119
+ h1("Hello, i'm a header.") + b("And im some stupid bold text.....")
120
+ end
121
+
122
+ #
123
+ # This action will be called if no other route matches.
124
+ #
125
+ # Ex: http://localhost:9393/you_will_not_find_me/
126
+ #
127
+ not_found do
128
+ h1("404 - File not found") + "Lego could not find what you are looking for, sorry."
129
+ end
130
+
131
+
132
+ end
133
+
134
+ run MyBlog
135
+
136
+ # Save this stuff to config.ru and fire it up with 'rackup'
137
+
data/lego-core.gemspec ADDED
@@ -0,0 +1,70 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{lego-core}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Mathias Stjernstr\303\266m", "Patrik Hedman"]
12
+ s.date = %q{2010-01-12}
13
+ s.description = %q{It's all about the bits and peices}
14
+ s.email = %q{mathias@globalinn.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "examples/config.ru",
24
+ "lego-core.gemspec",
25
+ "lib/lego.rb",
26
+ "lib/lego/controller.rb",
27
+ "lib/lego/controller/action_context.rb",
28
+ "lib/lego/controller/config.rb",
29
+ "lib/lego/controller/route_handler.rb",
30
+ "lib/lego/plugin.rb",
31
+ "lib/lego/plugin/controller/not_found.rb",
32
+ "spec/integration/test_full_stack_spec.rb",
33
+ "spec/lego/controller/action_context_spec.rb",
34
+ "spec/lego/controller/config_spec.rb",
35
+ "spec/lego/controller/route_handler_spec.rb",
36
+ "spec/lego/controller_spec.rb",
37
+ "spec/lego/plugin/controller/not_found_spec.rb",
38
+ "spec/lego/plugin_spec.rb",
39
+ "spec/lego_spec.rb",
40
+ "spec/spec.opts",
41
+ "spec/spec_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/stjernstrom/lego-core}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.5}
47
+ s.summary = %q{It's all about the bits and peices}
48
+ s.test_files = [
49
+ "spec/integration/test_full_stack_spec.rb",
50
+ "spec/lego/controller/action_context_spec.rb",
51
+ "spec/lego/controller/config_spec.rb",
52
+ "spec/lego/controller/route_handler_spec.rb",
53
+ "spec/lego/controller_spec.rb",
54
+ "spec/lego/plugin/controller/not_found_spec.rb",
55
+ "spec/lego/plugin_spec.rb",
56
+ "spec/lego_spec.rb",
57
+ "spec/spec_helper.rb"
58
+ ]
59
+
60
+ if s.respond_to? :specification_version then
61
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
62
+ s.specification_version = 3
63
+
64
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
65
+ else
66
+ end
67
+ else
68
+ end
69
+ end
70
+
data/lib/lego.rb ADDED
@@ -0,0 +1,38 @@
1
+ #
2
+ #:title:Lego-Core
3
+ #
4
+
5
+ module Lego
6
+
7
+ file_path = File.expand_path( File.dirname(__FILE__) )
8
+ $LOAD_PATH.unshift( file_path ) unless $LOAD_PATH.include?( file_path )
9
+
10
+ VERSION = [0,0,1]
11
+
12
+ #
13
+ # Return the current Lego version.
14
+ #
15
+
16
+ def self.version
17
+ VERSION.join(".")
18
+ end
19
+
20
+ autoload :Controller, 'lego/controller'
21
+ autoload :Plugin, 'lego/plugin'
22
+
23
+ def self.config(&block)
24
+ class_eval &block
25
+ end
26
+
27
+ #
28
+ # plugin lets a plugin register itself globally
29
+ #
30
+
31
+ def self.plugin(plugin_module)
32
+ plugin_module.register(Lego::Controller)
33
+ end
34
+
35
+ def self.set(options={})
36
+ Lego::Controller.set options
37
+ end
38
+ end
@@ -0,0 +1,162 @@
1
+ #
2
+ # = Lego::Controller
3
+ # Lego::Controller is the context where you setup routes and stuff.
4
+ #
5
+
6
+ class Lego::Controller
7
+
8
+ autoload :ActionContext, 'lego/controller/action_context'
9
+ autoload :RouteHandler, 'lego/controller/route_handler'
10
+ autoload :Config, 'lego/controller/config'
11
+
12
+ class << self
13
+
14
+ #
15
+ # When Lego::Controller is inherited it will create a new Lego::Controller::ActionContext for the class thats inheriting
16
+ # and it will also create a new Lego::Controller::RouteHandler module for the class thats inheriting.
17
+ #
18
+
19
+ def inherited(class_inheriting)
20
+ class_inheriting.const_set(:ActionContext, Class.new(Lego::Controller::ActionContext) do
21
+ const_set :ApplicationClass, class_inheriting
22
+ end)
23
+
24
+ class_inheriting.const_set(:RouteHandler, Module.new { extend Lego::Controller::RouteHandler })
25
+ class_inheriting.const_set(:Config, Module.new { extend Lego::Controller::Config })
26
+ end
27
+
28
+ #
29
+ # Use register inside your plugin to inject your code into the right place.
30
+ #
31
+ # Context available are:
32
+ #
33
+ # - :controller
34
+ # - :router
35
+ # - :view
36
+ #
37
+ # and on the way
38
+ #
39
+ # - :helper ?
40
+ #
41
+
42
+ def add_plugin(context, plugin_module)
43
+
44
+ base = (self == Lego::Controller) ? Lego::Controller : self
45
+
46
+ case context
47
+ when :controller
48
+ base.extend plugin_module
49
+ when :router
50
+ base::RouteHandler.add_matcher plugin_module
51
+ when :view
52
+ base::ActionContext.instance_eval do
53
+ include plugin_module
54
+ end
55
+ end
56
+ end
57
+
58
+ #
59
+ # add_route <method> <route> is exposed to your plugin as a shortcut for adding routes to the application they are plugged in to.
60
+ #
61
+ # <method> is a symbol for the request method it should match
62
+ #
63
+ # Valid options are:
64
+ #
65
+ # - :get
66
+ # - :post
67
+ # - :put
68
+ # - :head
69
+ # - :options
70
+ # - :not_found
71
+ #
72
+ # <route> is a hash with keys to be handled by the route matchers and also the ActionContext
73
+ #
74
+ # Valid options are anything your route matcher can handle. But there's some keys that's special for Lego and they are.
75
+ #
76
+ # - :action_block => A block thats going to be executed inside ActionContext.
77
+ # - :instance_vars => A hash where the keys beeing converted to ActionContext vars ex: { :var1 => "value1", :var2 => "value2" }
78
+ # - :set_response_code => An integer representing the response code.
79
+ #
80
+
81
+ def add_route(method, route)
82
+ self::RouteHandler.add_route(method, route)
83
+ end
84
+
85
+ #
86
+ # Use plugin in your controllers to choose which extensions you want to use in this controller.
87
+ #
88
+ # Extensions then inject themself into the right context.
89
+ #
90
+
91
+ def plugin(plugin_module)
92
+ plugin_module.register(self)
93
+ end
94
+
95
+ #
96
+ # Provides acces to the current Config class
97
+ #
98
+
99
+ def current_config
100
+ self::Config
101
+ end
102
+
103
+ #
104
+ # Use set to define environment agnostic configuration options
105
+ #
106
+ # Usage:
107
+ # Lego::Controller.set :foo => "bar"
108
+ #
109
+
110
+ def set(options={})
111
+ current_config.set(options)
112
+ end
113
+
114
+ #
115
+ # Use environment to define environment specific configuration options
116
+ #
117
+ # Usage:
118
+ # Lego::Controller.environment :development do
119
+ # set :foo => "bar"
120
+ # end
121
+ #
122
+ # or set environment agnostic configuration options by leaving out the environment parameter
123
+ #
124
+ # Usage:
125
+ # Lego::Controller.environment do
126
+ # set :foo => "bar"
127
+ # end
128
+ #
129
+
130
+ def environment(env=nil, &block)
131
+ raise ArgumentError, "No block provided" unless block_given?
132
+ current_config.environment(env, &block)
133
+ end
134
+
135
+ #
136
+ # call is used to handle an incomming Rack request.
137
+ #
138
+ # If no matching route is found we check for a defined :not_found route
139
+ # and if no :not_found defined we send a simple 404 - not found.
140
+ #
141
+
142
+ def call(env)
143
+ if route = self::RouteHandler.match_all_routes(env)
144
+ self::ActionContext.new.run(route, env)
145
+ else
146
+ if route = self::RouteHandler.routes[:not_found]
147
+ self::ActionContext.new.run(route, env)
148
+ else
149
+ [404, {'Content-Type' => 'text/html'}, '404 - Not found']
150
+ end
151
+ end
152
+ end
153
+
154
+ end
155
+
156
+ #
157
+ # Core plugins
158
+ #
159
+
160
+ plugin Lego::Plugin::Controller::NotFound
161
+
162
+ end