webink 1.1.4

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