zorglub 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Changelog ADDED
@@ -0,0 +1,2 @@
1
+ FIME-date Jérémy Zurcher <jeremy@asynk.ch>
2
+ * Project creation
data/MIT-LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2011-2012 Jérémy Zurcher
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to
5
+ deal in the Software without restriction, including without limitation the
6
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
+ sell copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16
+ THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,55 @@
1
+ zorglub
2
+ by Jérémy Zurcher
3
+ http://cgit.asynk.ch/cgi-bin/cgit/zorglub
4
+
5
+ == DESCRIPTION:
6
+
7
+ a nano web application framework based on rack[http://rack.rubyforge.org/]
8
+
9
+ == FEATURES:
10
+
11
+ * class#method mapping scheme (/class_mapping/method_name/*args)
12
+ * class level layout and engine specification
13
+ * method level layout, engine and view specification
14
+ * before_all and after_all methods callbacks
15
+ * redirection
16
+ * partial
17
+ * class level inherited variables
18
+ * session
19
+
20
+ == SYNOPSIS:
21
+
22
+ For a simple test application run:
23
+ * rackup ./example/sample.ru
24
+
25
+ Don't forget to look at
26
+ * the spec/ folder
27
+
28
+ == REQUIREMENTS:
29
+
30
+ * rack
31
+
32
+ == DOWNLOAD/INSTALL:
33
+
34
+ From rubygems:
35
+
36
+ [sudo] gem install zorglub
37
+
38
+ or from the git repository on github:
39
+
40
+ git clone git://github.com/jeremyz/zorglub.git
41
+ cd zorglub
42
+ rake gem:install
43
+
44
+ == RESOURCES:
45
+
46
+ You can find this project in a few places:
47
+
48
+ Online repositories:
49
+
50
+ * https://github.com/jeremyz/zorglub
51
+ * http://cgit.asynk.ch/cgi-bin/cgit/zorglub/
52
+
53
+ == LICENSE:
54
+
55
+ See MIT-LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ require './lib/zorglub.rb'
4
+ load './tasks/setup.rb'
5
+ #
6
+ # Project general information
7
+ PROJ.name = 'zorglub'
8
+ PROJ.authors = 'Jérémy Zurcher'
9
+ PROJ.email = 'jeremy@asynk.ch'
10
+ PROJ.url = 'http://cgit.asynk.ch/cgi-bin/cgit/zorglub'
11
+ PROJ.version = Zorglub::VERSION
12
+ PROJ.rubyforge.name = 'FIXME'
13
+ PROJ.readme_file = 'README.rdoc'
14
+ #
15
+ # Annoucement
16
+ PROJ.ann.paragraphs << 'FEATURES' << 'SYNOPSIS' << 'REQUIREMENTS' << 'DOWNLOAD/INSTALL' << 'CREDITS' << 'LICENSE'
17
+ PROJ.ann.email[:from] = 'jeremy@asynk.ch'
18
+ PROJ.ann.email[:to] = ['FIXME']
19
+ PROJ.ann.email[:server] = 'FIXME'
20
+ PROJ.ann.email[:tls] = false
21
+ # Gem specifications
22
+ PROJ.gem.need_tar = false
23
+ PROJ.gem.files = %w(Changelog MIT-LICENSE README.rdoc Rakefile) + Dir.glob("{ext,lib,spec,tasks}/**/*[^~]").reject { |fn| test ?d, fn }
24
+ PROJ.gem.platform = Gem::Platform::RUBY
25
+ PROJ.gem.required_ruby_version = ">= 1.9.2"
26
+ #
27
+ # Override Mr. Bones autogenerated extensions and force ours in
28
+ #PROJ.gem.extras['extensions'] = %w(ext/extconf.rb)
29
+ #PROJ.gem.extras['required_ruby_version'] = ">= 1.9.2"
30
+ #
31
+ # RDoc
32
+ PROJ.rdoc.exclude << '^ext\/'
33
+ PROJ.rdoc.opts << '-x' << 'ext'
34
+ #
35
+ # Ruby
36
+ PROJ.ruby_opts = []
37
+ PROJ.ruby_opts << '-I' << 'lib'
38
+ #
39
+ # RSpec
40
+ PROJ.spec.files.exclude /rbx/
41
+ PROJ.spec.opts << '--color'
42
+ #
43
+ # Rcov
44
+ PROJ.rcov.opts << '-I lib'
45
+ #
46
+ # Dependencies
47
+ depend_on 'rack'
48
+ depend_on 'rake', '>=0.8.0'
49
+ #
50
+ task :default => [:spec]
51
+ #
52
+ desc "Build all packages"
53
+ task :package => 'gem:package'
54
+ #
55
+ desc "Install the gem locally"
56
+ task :install => 'gem:install'
57
+ #
58
+ # EOF
@@ -0,0 +1,45 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ require 'rack'
4
+ #
5
+ module Zorglub
6
+ #
7
+ class App < Rack::URLMap
8
+ #
9
+ def initialize map={}, &block
10
+ super
11
+ @map = map
12
+ instance_eval &block if block_given?
13
+ remap @map
14
+ end
15
+ #
16
+ def map location, object
17
+ return unless location and object
18
+ raise Exception.new "#{@map[location]} already mapped to #{location}" if @map.has_key? location
19
+ object.app = self
20
+ @map.merge! location.to_s=>object
21
+ remap @map
22
+ end
23
+ #
24
+ def delete location
25
+ @map.delete location
26
+ remap @map
27
+ end
28
+ #
29
+ def at location
30
+ @map[location]
31
+ end
32
+ #
33
+ def to object
34
+ @map.invert[object]
35
+ end
36
+ #
37
+ def to_hash
38
+ @map.dup
39
+ end
40
+ #
41
+ end
42
+ #
43
+ end
44
+ #
45
+ # EOF
@@ -0,0 +1,66 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ module Zorglub
4
+ #
5
+ class Config
6
+ @options = {
7
+ :root => '.',
8
+ :engine => nil,
9
+ :layout => 'default',
10
+ :view_dir => 'view',
11
+ :layout_dir => 'layout',
12
+ :session_on => false
13
+ }
14
+ @engines = { }
15
+ class << self
16
+ #
17
+ def [] k
18
+ @options[k]
19
+ end
20
+ #
21
+ def []= k, v
22
+ @options[k]=v
23
+ end
24
+ #
25
+ def view_base_path
26
+ p = @options[:view_path]
27
+ ( p.nil? ? File.join(@options[:root], @options[:view_dir]) : p )
28
+ end
29
+ #
30
+ def layout_base_path
31
+ p = @options[:layout_path]
32
+ ( p.nil? ? File.join(@options[:root], @options[:layout_dir]) : p )
33
+ end
34
+ #
35
+ def register_engine name, ext, proc
36
+ return unless name
37
+ @engines[name]=[ ext, proc ]
38
+ end
39
+ #
40
+ def engine_ext engine
41
+ e = @engines[engine]
42
+ return '' if e.nil?
43
+ x=e[0]
44
+ ( x.nil? ? '' : '.'+x )
45
+ end
46
+ #
47
+ def engine_proc engine
48
+ e = @engines[engine]
49
+ ( e.nil? ? nil : e[1] )
50
+ end
51
+ #
52
+ end
53
+ #
54
+ def self.method_missing m, *args, &block
55
+ if m=~/(.*)=$/
56
+ @options[$1.to_sym]=args[0]
57
+ else
58
+ @options[m.to_sym]
59
+ end
60
+ end
61
+ #
62
+ end
63
+ #
64
+ end
65
+ #
66
+ # EOF
@@ -0,0 +1,196 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ module Zorglub
4
+ #
5
+ class Node
6
+ #
7
+ @hooks = {
8
+ :before_all => [],
9
+ :after_all => [],
10
+ }
11
+ #
12
+ @inherited_vars = { }
13
+ #
14
+ class << self
15
+ #
16
+ attr_reader :hooks, :inherited_vars
17
+ #
18
+ def inherited sub
19
+ sub.layout layout
20
+ sub.engine engine
21
+ sub.instance_variable_set :@inherited_vars, {}
22
+ @inherited_vars.each do |s,v| sub.inherited_var s, *v end
23
+ end
24
+ #
25
+ def engine engine=nil
26
+ @engine = engine unless engine.nil? or engine.empty?
27
+ @engine ||= Config.engine
28
+ end
29
+ #
30
+ def layout layout=nil
31
+ @layout = layout unless layout.nil? or layout.empty?
32
+ @layout ||= Config.layout
33
+ end
34
+ #
35
+ def inherited_var sym, *args
36
+ var = @inherited_vars[sym] ||=[]
37
+ unless args.empty?
38
+ var.concat args
39
+ var.uniq!
40
+ end
41
+ var
42
+ end
43
+ #
44
+ attr_writer :app
45
+ def map app, location
46
+ @app = app
47
+ @app.map location, self
48
+ end
49
+ #
50
+ def r *args
51
+ @r ||= @app.to self
52
+ (args.empty? ? @r : File.join( @r, args.map { |x| x.to_s } ) )
53
+ end
54
+ #
55
+ def call env
56
+ meth, *args = env['PATH_INFO'].sub(/^\//,'').split '/'
57
+ meth||= 'index'
58
+ node = self.new env, {:engine=>engine,:layout=>layout,:view=>r(meth),:method=>meth,:args=>args}
59
+ return error_404 node if not node.respond_to? meth
60
+ node.realize!
61
+ end
62
+ #
63
+ def partial meth, *args
64
+ node = self.new nil, {:engine=>engine,:layout=>nil,:view=>r(meth),:method=>meth.to_s,:args=>args}
65
+ return error_404 node if not node.respond_to? meth
66
+ node.feed!
67
+ node.content
68
+ end
69
+ #
70
+ def call_before_hooks obj
71
+ Node.hooks[:before_all].each do |blk| blk.call obj end
72
+ end
73
+ #
74
+ def before_all &blk
75
+ Node.hooks[:before_all]<< blk
76
+ Node.hooks[:before_all].uniq!
77
+ end
78
+ #
79
+ def call_after_hooks obj
80
+ Node.hooks[:after_all].each do |blk| blk.call obj end
81
+ end
82
+ #
83
+ def after_all &blk
84
+ Node.hooks[:after_all]<< blk
85
+ Node.hooks[:after_all].uniq!
86
+ end
87
+ #
88
+ def error_404 node
89
+ resp = node.response
90
+ resp.status = 404
91
+ resp['Content-Type'] = 'text/plain'
92
+ resp.write "%s mapped at %p can't respond to : %p" % [ node.class.name, node.r, node.request.env['PATH_INFO'] ]
93
+ resp
94
+ end
95
+ #
96
+ end
97
+ #
98
+ attr_reader :action, :request, :response, :content
99
+ #
100
+ def initialize env, action
101
+ @env = env
102
+ @action = action
103
+ @request = Rack::Request.new env
104
+ @response = Rack::Response.new
105
+ end
106
+ #
107
+ def realize!
108
+ catch(:stop_realize) {
109
+ feed!
110
+ response.write @content
111
+ response.finish
112
+ response
113
+ }
114
+ end
115
+ #
116
+ def feed!
117
+ Node.call_before_hooks self
118
+ state :meth
119
+ @content = self.send @action[:method], *@action[:args]
120
+ v, l, e = view, layout, Config.engine_proc(@action[:engine])
121
+ # TODO compile and cache
122
+ state (@action[:layout].nil? ? :partial : :view)
123
+ @content = e.call v, self if e and File.exists? v
124
+ state :layout
125
+ @content = e.call l, self if e and File.exists? l
126
+ Node.call_after_hooks self
127
+ @content
128
+ end
129
+ #
130
+ def redirect target, options={}, &block
131
+ status = options[:status] || 302
132
+ body = options[:body] || redirect_body(target)
133
+ header = response.header.merge('Location' => target.to_s)
134
+ throw :stop_realize, Rack::Response.new(body, status, header, &block)
135
+ end
136
+ #
137
+ def redirect_body target
138
+ "You are being redirected, please follow this link to: <a href='#{target}'>#{target}</a>!"
139
+ end
140
+ #
141
+ def state state=nil
142
+ @action[:state] = state unless state.nil?
143
+ @action[:state]
144
+ end
145
+ #
146
+ def engine engine=nil
147
+ @action[:engine] = engine unless engine.nil? or engine.empty?
148
+ @action[:engine]
149
+ end
150
+ #
151
+ def layout layout=nil
152
+ @action[:layout] = layout unless layout.nil? or layout.empty?
153
+ return '' if @action[:layout].nil?
154
+ File.join(Config.layout_base_path, @action[:layout])+ Config.engine_ext(@action[:engine])
155
+ end
156
+ #
157
+ def no_layout
158
+ @action[:layout] = nil
159
+ end
160
+ #
161
+ def view view=nil
162
+ @action[:view] = view unless view.nil? or view.empty?
163
+ return '' if @action[:view].nil?
164
+ File.join(Config.view_base_path, @action[:view])+Config.engine_ext(@action[:engine])
165
+ end
166
+ #
167
+ def inherited_var sym, *args
168
+ d = self.class.inherited_vars[sym].clone || []
169
+ unless args.empty?
170
+ d.concat args
171
+ d.uniq!
172
+ end
173
+ d
174
+ end
175
+ #
176
+ def args
177
+ @action[:args]
178
+ end
179
+ #
180
+ def map
181
+ self.class.r
182
+ end
183
+ #
184
+ def r *args
185
+ File.join map, (args.empty? ? @action[:method] : args.map { |x| x.to_s } )
186
+ end
187
+ #
188
+ def html
189
+ [ :map, :r, :args, :engine, :layout, :view ].inject('') { |s,sym| s+="<p>#{sym} => #{self.send sym}</p>"; s }
190
+ end
191
+ #
192
+ end
193
+ #
194
+ end
195
+ #
196
+ # EOF
@@ -0,0 +1,81 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ require 'securerandom'
4
+ #
5
+ module Zorglub
6
+ #
7
+ class Node
8
+ #
9
+ def session
10
+ @session ||= Session.new @request
11
+ end
12
+ end
13
+ #
14
+ class SessionHash
15
+ #
16
+ @data = {}
17
+ class << self
18
+ attr_reader :data
19
+ end
20
+ #
21
+ def initialize sid
22
+ @sid = sid
23
+ # TODO if sid is nil, one should be created
24
+ @session_data = SessionHash.data[sid]||={}
25
+ end
26
+ #
27
+ def exists?
28
+ not @sid.nil?
29
+ end
30
+ #
31
+ def [] idx
32
+ @session_data[idx]
33
+ end
34
+ #
35
+ def []= idx, v
36
+ @session_data[idx] = v
37
+ end
38
+ end
39
+ #
40
+ class Session
41
+ #
42
+ @session_key = 'zorglub.sid'
43
+ @session_kls = Zorglub::SessionHash
44
+ class << self
45
+ attr_accessor :session_key, :session_kls
46
+ end
47
+ #
48
+ def initialize req
49
+ @request = req
50
+ @instance = nil
51
+ end
52
+ #
53
+ def setup!
54
+ if Config.session_on
55
+ @instance = Session.session_kls.new @request.cookies[Session.session_key]
56
+ else
57
+ @instance = {}
58
+ end
59
+ end
60
+ private :setup!
61
+ #
62
+ def exists?
63
+ setup! if @instance.nil?
64
+ @instance.exists?
65
+ end
66
+ #
67
+ def [] idx
68
+ setup! if @instance.nil?
69
+ @instance[idx]
70
+ end
71
+ #
72
+ def []= idx, v
73
+ setup! if @instance.nil?
74
+ @instance[idx] = v
75
+ end
76
+ #
77
+ end
78
+ #
79
+ end
80
+ #
81
+ # EOF
data/lib/zorglub.rb ADDED
@@ -0,0 +1,14 @@
1
+ #! /usr/bin/env ruby
2
+ # -*- coding: UTF-8 -*-
3
+ #
4
+ require './lib/zorglub/config'
5
+ require './lib/zorglub/node'
6
+ require './lib/zorglub/app'
7
+ #
8
+ module Zorglub
9
+ #
10
+ VERSION = '0.0.1'
11
+ #
12
+ end
13
+ #
14
+ # EOF
data/spec/app_spec.rb ADDED
@@ -0,0 +1,43 @@
1
+ # -*- coding: UTF-8 -*-
2
+ #
3
+ require 'spec_helper'
4
+ #
5
+ describe Zorglub do
6
+ #
7
+ describe Zorglub::App do
8
+ #
9
+ it "map should add a mapped node" do
10
+ APP.at("/temp").should be_nil
11
+ APP.map "/temp", Temp
12
+ APP.at("/temp").should be Temp
13
+ end
14
+ #
15
+ it "delete should delete a mapped node" do
16
+ APP.at("/temp").should be Temp
17
+ APP.delete "/temp"
18
+ APP.at("/temp").should be_nil
19
+ end
20
+ #
21
+ it "at should return mapped node" do
22
+ APP.at("/node1").should be Node1
23
+ end
24
+ #
25
+ it "at should return nil if no Node mapped" do
26
+ APP.at("/none").should be_nil
27
+ end
28
+ #
29
+ it "to should return path to node" do
30
+ APP.to(Node1).should == "/node1"
31
+ end
32
+ #
33
+ it "to should return nil if not an existing Node" do
34
+ APP.to(nil).should be_nil
35
+ end
36
+ #
37
+ it "to_hash should return a correct hash" do
38
+ APP.to_hash["/node1"].should be Node1
39
+ end
40
+ #
41
+ end
42
+ #
43
+ end
File without changes
File without changes
File without changes
File without changes