webink 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ require "fcgi"
2
+ require "mmap"
3
+ require "webink/beauty"
4
+
5
+ def getBinding(cgi,env,params)
6
+ return binding
7
+ end
8
+
9
+ script = nil
10
+ fhandle = nil
11
+ control = nil
12
+ pram = nil
13
+ FCGI.each_cgi do |cgi|
14
+ time = Time.now
15
+ script = cgi.env_table['SCRIPT_FILENAME']
16
+ begin
17
+ pram = CGI::parse(cgi.env_table['QUERY_STRING'].gsub(/\?/, "&"))
18
+ control = pram['controller'][0]
19
+ routes = Ink::Beauty.load_routes script
20
+ fhandle = Mmap.new(routes, "r")
21
+ Dir.chdir( File.dirname(routes) )
22
+ control = eval fhandle, getBinding(cgi,cgi.env_table,control) if fhandle
23
+ fhandle.munmap
24
+ raise StandardError.new("Routes not matched.") if control.keys.length <= 1
25
+
26
+ control[:get] = pram
27
+ control[:config] = Ink::Beauty.load_config script
28
+ control[:time] = time
29
+ control[:cgi] = cgi
30
+ if cgi.env_table['REQUEST_METHOD'] == 'POST'
31
+ control[:post] = Hash.new
32
+ cgi.params.each do |k,v|
33
+ if v.is_a? Array
34
+ control[:post][k] = Array.new
35
+ v.each do |a|
36
+ control[:post][k].push (control[:config]["escape_post_data"]) ? CGI::escapeHTML(a) : a
37
+ end
38
+ control[:post][k] = control[:post][k].to_s
39
+ else
40
+ control[:post][k] = (control[:config]["escape_post_data"]) ? CGI::escapeHTML(v) : v
41
+ end
42
+ end
43
+ pram.each do |k,v|
44
+ cgi.params[k] = v if not cgi.params.has_key? k
45
+ end
46
+ else
47
+ cgi.params = pram
48
+ end
49
+
50
+ script = Ink::Beauty.load_init script
51
+ fhandle = Mmap.new(script, "r")
52
+ Dir.chdir( File.dirname(script) )
53
+ eval fhandle, getBinding(cgi,cgi.env_table,control) if fhandle
54
+ fhandle.munmap
55
+
56
+
57
+ rescue LoadError => bang
58
+ if control and control[:config] and not control[:config]["production"]
59
+ puts cgi.header({"Location" => "/status-404.html"});
60
+ else
61
+ puts cgi.header
62
+ puts "<hr><b>LoadError:</b>", "<em><pre>", CGI::escapeHTML("#{bang}"), "</pre></em>\n", "<pre>", bang.backtrace.join("\n"), "</pre>"
63
+ end
64
+
65
+ rescue NotImplementedError => bang
66
+ if control and control[:config] and not control[:config]["production"]
67
+ puts cgi.header({"Location" => "/status-500.html"});
68
+ else
69
+ puts cgi.header
70
+ puts "<hr><b>NotImplementedError:</b>", "<em><pre>", CGI::escapeHTML("#{bang}"), "</pre></em>\n", "<pre>", bang.backtrace.join("\n"), "</pre>"
71
+ end
72
+
73
+ rescue ArgumentError => bang
74
+ if control and control[:config] and not control[:config]["production"]
75
+ puts cgi.header({"Location" => "/status-500.html"});
76
+ else
77
+ puts cgi.header
78
+ puts "<hr><b>ArgumentError:</b>", "<em><pre>", CGI::escapeHTML("#{bang}"), "</pre></em>\n", "<pre>", bang.backtrace.join("\n"), "</pre>"
79
+ end
80
+
81
+ rescue RuntimeError => bang
82
+ if control and control[:config] and not control[:config]["production"]
83
+ puts cgi.header({"Location" => "/status-500.html"});
84
+ else
85
+ puts cgi.header
86
+ puts "<hr><b>RuntimeError:</b>", "<em><pre>", CGI::escapeHTML("#{bang}"), "</pre></em>\n", "<pre>", bang.backtrace.join("\n"), "</pre>"
87
+ end
88
+
89
+ rescue NameError => bang
90
+ if control and control[:config] and not control[:config]["production"]
91
+ puts cgi.header({"Location" => "/status-500.html"});
92
+ else
93
+ puts cgi.header
94
+ puts "<hr><b>NameError:</b>", "<em><pre>", CGI::escapeHTML("#{bang}"), "</pre></em>\n", "<pre>", bang.backtrace.join("\n"), "</pre>"
95
+ end
96
+
97
+ rescue Exception, StandardError => bang
98
+ if control and control[:config] and not control[:config]["production"]
99
+ puts cgi.header({"Location" => "/status-500.html"});
100
+ else
101
+ puts cgi.header
102
+ puts "<hr><b>Exception:</b>", "<em><pre>", CGI::escapeHTML("#{bang}"), "</pre></em>\n", "<pre>", bang.backtrace.join("\n"), "</pre>"
103
+ end
104
+ end
105
+
106
+ script = nil
107
+ fhandle = nil
108
+ control = nil
109
+ pram = nil
110
+ GC.start
111
+ end
112
+
@@ -0,0 +1,50 @@
1
+ require 'mmap'
2
+ require 'webink/beauty'
3
+
4
+ config = nil
5
+ model_classes = Array.new
6
+
7
+ begin
8
+ config = Ink::Beauty.load_config "./"
9
+ rescue Exception => bang
10
+ puts "Error while loading config: #{bang}."
11
+ end
12
+
13
+ require "#{config["db_type"]}"
14
+ require 'webink'
15
+
16
+ models = Dir.new "./models"
17
+ models.each do |model|
18
+ load "#{models.path}/#{model}" if model =~ /\.rb$/
19
+ model_classes.push Ink::Model.classname($1) if model =~ /^(.*)\.rb$/
20
+ end
21
+
22
+ begin
23
+ Ink::Database.create config
24
+ db = Ink::Database.database
25
+ db_tables = db.tables
26
+
27
+ if ARGV[0] == "drop"
28
+ puts "Dropping old tables..."
29
+ db_tables.each do |t|
30
+ puts "...#{t}"
31
+ db.query "DROP TABLE #{t};"
32
+ end
33
+ end
34
+
35
+ puts "Creating new tables..."
36
+ model_classes.each do |m|
37
+ puts "...for class: #{m.name}:"
38
+ c = m.create
39
+ puts c
40
+ c.each do |exec|
41
+ begin
42
+ db.query exec
43
+ rescue => ex
44
+ puts ex
45
+ end
46
+ end
47
+ end
48
+ rescue Exception => bang
49
+ puts "SQLError: #{bang}."
50
+ end
@@ -0,0 +1,4 @@
1
+ require 'webink/model'
2
+ require 'webink/controller'
3
+ require 'webink/database'
4
+
@@ -0,0 +1,80 @@
1
+ module Ink
2
+
3
+ # = Beauty class
4
+ #
5
+ # This class provides a set of tools for loading config and init scripts
6
+ # as well as the route-matching. It makes the dispatcher code much more
7
+ # beautiful, hence the name.
8
+ #
9
+ #
10
+ #
11
+ class Beauty
12
+
13
+ # Class method
14
+ #
15
+ # Attempts to load the init file of the project or raises a LoadError.
16
+ # [param script:] Project folder path
17
+ # [returns:] valid init file path
18
+ def self.load_init(script)
19
+ script = "#{File.dirname(script)}/init"
20
+ script = "#{script}.rb" if not File.exist? script
21
+ raise LoadError.new("Init not found.") if not File.exist? script
22
+ script
23
+ end
24
+
25
+ # Class method
26
+ #
27
+ # Attempts to load the config file of the project or raises a LoadError.
28
+ # Once loaded, the config is evaluated and can raise a RuntimeError, or
29
+ # it is returned
30
+ # [param script:] Project folder path
31
+ # [returns:] a valid config
32
+ def self.load_config(script)
33
+ config = "#{File.dirname(script)}/config"
34
+ config = "#{config}.rb" if not File.exist? config
35
+ raise LoadError.new("Config not found.") if not File.exist? config
36
+ fhandle = Mmap.new(config, "r")
37
+ config = eval fhandle
38
+ fhandle.munmap
39
+ raise RuntimeError.new("Config error.") if not config.instance_of? Hash
40
+ config
41
+ end
42
+
43
+ # Class method
44
+ #
45
+ # Attempts to load the routes file of the project or raises a LoadError
46
+ # [param script:] Project folder path
47
+ # [returns:] valid routes file path
48
+ def self.load_routes(script)
49
+ routes = "#{File.dirname(script)}/routes"
50
+ routes = "#{routes}.rb" if not File.exist? routes
51
+ raise LoadError.new("Routes not found.") if not File.exist? routes
52
+ routes
53
+ end
54
+
55
+ # Class method
56
+ #
57
+ # Attempts to match the params onto the routes and return the results in
58
+ # form of a Hash.
59
+ # [param root:] Relative project folder path to the server document path
60
+ # [param routes:] Array of Arrays: regex, Hash(Symbol => String)
61
+ # [param params:] Requested string
62
+ # [returns:] Hash of Symbol => String
63
+ def self.routing(root, routes, params)
64
+ match = { :root => root }
65
+ routes.each do |entry|
66
+ k = entry[0]
67
+ v = entry[1]
68
+ if params =~ k
69
+ v.each do |sys,e|
70
+ match[sys] = (e =~ /^\$\d+$/ and params =~ k and eval e) ? (eval e) : e
71
+ end
72
+ break
73
+ end
74
+ end
75
+ match
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,285 @@
1
+ module Ink
2
+
3
+ # = Controller class
4
+ #
5
+ # == Usage
6
+ #
7
+ # Controllers handle incoming requests and decide what to do
8
+ # with them. A controller has access to all incoming data, like
9
+ # POST and GET as well as the config.
10
+ #
11
+ # class App < Ink::Controller
12
+ #
13
+ # def index
14
+ # redirect_to :controller => "app", :module => "feed", :id => 29382374
15
+ # end
16
+ #
17
+ # def feed
18
+ # @arg = @params[:id]
19
+ # @users = []
20
+ # render :template => "index"
21
+ # end
22
+ #
23
+ # def partial
24
+ # @arg = "foo bar"
25
+ # render :partial => "part", :standalone => true
26
+ # end
27
+ #
28
+ # end
29
+ #
30
+ # A controller named App should have the filename app.rb and be
31
+ # placed inside the project controller folder. It can have instance
32
+ # methods that are usually refered to as modules.
33
+ # So a route should contain at least a :controller and a :module.
34
+ #
35
+ # In the sample above there are three modules, index redirects
36
+ # to feed, feed renders a template and partial renders a partial.
37
+ # When using a partial inside a template, you do not need the
38
+ # standalone argument, but when you plan to return just the partial,
39
+ # you should add it to the argument string. Rendering returns an
40
+ # interpreted erb instance, which is returned to the calling part of
41
+ # the script, which runs the erb instance. Redirecting forwards
42
+ # an erb instance and passes on the existing @params, after overwriting
43
+ # and setting its hash arguments, so multiple redirecting works.
44
+ #
45
+ # The template is called inside the binding on the controller, which
46
+ # means that all instance variables and methods are available as such.
47
+ #
48
+ # == Templates
49
+ #
50
+ # All templates are to be put into the project views folder, and are
51
+ # to be named name.html.erb, while partials are to be named _anothername.html.erb.
52
+ # It is possible to create subfolders and call the templates via
53
+ # :template => "subfolder/template.html.erb".
54
+ #
55
+ # The index.html.erb template in the controller above looks like this:
56
+ #
57
+ # <%= render :partial => "part" %>
58
+ #
59
+ # And the _part.html.erb, that is called from the index, is this:
60
+ #
61
+ # WTF <%= @arg %>!!
62
+ # <br>
63
+ #
64
+ # When the index module is requested, it will set :id to 29382374.
65
+ # However requesting the feed module, it requires :id to be set as
66
+ # a parameter through the routes.
67
+ #
68
+ # == Routing
69
+ #
70
+ # The routes being evaluated by the dispatcher are simple regular expressions.
71
+ # This example fits the controller from earlier.
72
+ #
73
+ # root = "/folder"
74
+ #
75
+ # routes = [
76
+ # [ /^\/([^\/]+)\/([^\/]+)(\/([0-9]+))?$/, {:controller => "$1", :module => "$2", :id => "$4"} ],
77
+ # [ /^\/([^\/]+)\/?$/, {:controller => "$1", :module => "index"} ],
78
+ # [ /^\/?$/, {:controller => "app", :module => "index"} ],
79
+ # ]
80
+ #
81
+ # Routes are built as a priority list, the first match will be the route taken. All
82
+ # route configurations must include a variable named root, as it will help to create
83
+ # dynamic links within websites. Root is relative to the webserver root. Assume your
84
+ # webserver points to public_html, then routes can be "" for the public_html folder,
85
+ # or "/anysubfolder" which points to anysubfolder. Deeper structures work too.
86
+ #
87
+ # == Linking
88
+ #
89
+ # A controller has the ability to create hyperlinks for you, that will dynamically match
90
+ # the project path etc. (and is also using the root variable from the routes.rb)
91
+ #
92
+ # link_to "name", "controller", "module", "param1", "param2", {:class => "h1", :id => "14"}
93
+ #
94
+ # The link above will produce this:
95
+ #
96
+ # <a class="h1" id="14" href="/controller/module/param1/param2/">name</a>
97
+ #
98
+ # So the first entry of the argument array is the hyperlink name, then all
99
+ # following entries are connected as the href string, prefixed by the root variable
100
+ # that is set in the routes.rb. The hash includes the attributes for the <a>-tag.
101
+ #
102
+ # == Pathing
103
+ #
104
+ # A controller also constructs paths for you (and is also using the root variable
105
+ # from the routes.rb), which you can use to dynamically reroute forms, just to
106
+ # mention one example.
107
+ #
108
+ # @my = "no"
109
+ # path_to "this", "is", @my, "path"
110
+ #
111
+ # The constructed path will look like this:
112
+ #
113
+ # /this/is/no/path
114
+ #
115
+ # It just puts together the arguments given to it.
116
+ #
117
+ # == Sessions
118
+ #
119
+ # Usually very important in the development of websites are sessions. There is some
120
+ # basic support for it in webink, that will make certain bits easier. For more information
121
+ # have a look at the blog-demo.
122
+ #
123
+ # Each controller usually expects cookies to work, if you handle sessions via cgi.
124
+ # Whenever cookies do not work, or you do not intend to use them in the first place,
125
+ # path_to and link_to watch out for the instance variables @session_key and @session_id
126
+ # which are added via GET to the path/hyperlink, that they construct. Therefore the
127
+ # tools should not be used for external linking. The dispatcher automatically filters
128
+ # the GET session_id when using POST and adds it to the cgi.params, so sessions can
129
+ # be used confortably.
130
+ #
131
+ # Per default, the @session_key should be "_session_id" as specified in the cgi/session
132
+ # library from ruby core, but the controller offers some freedom, in case you may want
133
+ # a different key name for some reason.
134
+ #
135
+ #
136
+ #
137
+ class Controller
138
+
139
+ # Constructor
140
+ #
141
+ # Creates a new controller of the derived class. params are the globals set
142
+ # by the dispatcher, and include keys like: :get, :post and :config. Also the
143
+ # matched routes are put there, :controller, :module and any parameters like
144
+ # :id or :page that were retrieved from the routes.
145
+ # [param params:] Hash of Symbol => Objects
146
+ def initialize(params)
147
+ @params = params
148
+ end
149
+
150
+ # Instance method
151
+ #
152
+ # Render a template or a partial that is located in the ./views/ folder of the
153
+ # project. Subfolders are allowed to be specified. All instance variables are
154
+ # accessible from within the template, as are any instance methods.
155
+ # [param args:] Hash of arguments
156
+ # [returns:] interpreted erb instance or nil
157
+ def render(args)
158
+ args[:locals] = Array.new if not args[:locals]
159
+ template = nil
160
+ if args[:template]
161
+ template = File.open "./views/#{args[:template]}.html.erb", "r"
162
+ erb = ERB.new template.readlines * "\n"
163
+ puts @params[:cgi].header
164
+ erb.run self.getBinding(args[:locals])
165
+ elsif args[:partial]
166
+ template = File.open "./views/#{(File.dirname(args[:partial]) != ".") ? "#{File.dirname(args[:partial])}/" : ""}_#{File.basename(args[:partial])}.html.erb", "r"
167
+ erb = ERB.new template.readlines * "\n"
168
+ if not args[:standalone]
169
+ erb.result self.getBinding(args[:locals])
170
+ else
171
+ puts @params[:cgi].header
172
+ erb.run self.getBinding(args[:locals])
173
+ end
174
+ else
175
+ nil
176
+ end
177
+ end
178
+
179
+ # Instance method
180
+ #
181
+ # Redirect to a controller and module. The new controller will
182
+ # be instanciated with a copy of the current params.
183
+ # [param args:] Hash of arguments
184
+ # [returns:] interpreted erb instance or nil
185
+ def redirect_to(args)
186
+ p = Hash.new
187
+ @params.each do |k,v|
188
+ p[k] = v
189
+ end
190
+ args.each do |k,v|
191
+ p[k] = v
192
+ end
193
+ if p[:controller] and p[:module]
194
+ controller = (Ink::Controller.verify p[:controller]).new p
195
+ (controller.verify p[:module]).call
196
+ else
197
+ nil
198
+ end
199
+ end
200
+
201
+ # Instance method
202
+ #
203
+ # Creates a dynamic hyperlink
204
+ # First argument is the name, then follow the pieces that are imploded by
205
+ # a /, followed by hashes, that become attributes. It adds the @session_key
206
+ # and @session_id if they are set.
207
+ # Convenience method
208
+ # [param args:] Array of Strings and Hashes
209
+ # [returns:] Hyperlink
210
+ def link_to(*args)
211
+ raise ArgumentError.new("Expects an array.") if not args.instance_of? Array and args.length < 2
212
+ href = "#{@params[:root]}#{(@params[:root][@params[:root].length-1].chr == "/") ? "" : "/"}" if @params[:root].length > 0
213
+ href = "/" if @params[:root].length == 0
214
+ a = "<a "
215
+ name = args[0]
216
+ for i in 1...args.length
217
+ arg = args[i]
218
+ if not arg.instance_of? Hash
219
+ href += "#{arg}/"
220
+ else
221
+ arg.each do |k,v|
222
+ a += "#{k}=\"#{v}\" "
223
+ end
224
+ end
225
+ end
226
+ href += "?#{@session_key}=#{@session_id}" if @session_id and @session_key
227
+ "#{a}href=\"#{href}\">#{name}</a>"
228
+ end
229
+
230
+ # Instance method
231
+ #
232
+ # Creates a dynamic path
233
+ # The array pieces are imploded by a /. It adds the @session_key
234
+ # and @session_id if they are set.
235
+ # Convenience method
236
+ # [param args:] Array of Strings or String
237
+ # [returns:] path
238
+ def path_to(*args)
239
+ href = "#{@params[:root]}#{(@params[:root][@params[:root].length-1].chr == "/") ? "" : "/"}" if @params[:root].length > 0
240
+ href = "/" if @params[:root].length == 0
241
+
242
+ if args.is_a? Array
243
+ for i in 0...args.length
244
+ arg = args[i]
245
+ href += "#{arg}/"
246
+ end
247
+ else
248
+ href += "#{args}/"
249
+ end
250
+ href += "?#{@session_key}=#{@session_id}" if @session_id and @session_key
251
+ href
252
+ end
253
+
254
+ # Instance method
255
+ #
256
+ # Retrieve the current binding of the instance.
257
+ # [param locals:] an Array of data for the template
258
+ # [returns:] current binding
259
+ def getBinding(*locals)
260
+ binding
261
+ end
262
+
263
+ # Class method
264
+ #
265
+ # Retrieve the class of the name controller, that can be instanciated
266
+ # by using new. Raises a NameError.
267
+ # [param controller:] Controller name string
268
+ # [returns:] class or nil
269
+ def self.verify(controller)
270
+ ((Module.const_get controller.capitalize).is_a? Class) ? (Module.const_get controller.capitalize) : (raise NameError.new("Controller not found."))
271
+ end
272
+
273
+ # Instance method
274
+ #
275
+ # Retrieve the method of name mod, that can be called by using the
276
+ # call method. Raises a NameError.
277
+ # [param mod:] Method name string
278
+ # [returns:] method or nil
279
+ def verify(mod)
280
+ self.method(mod)
281
+ end
282
+
283
+ end
284
+
285
+ end