mentawai 0.5.0 → 0.6.0

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.
@@ -5,17 +5,19 @@ module Mentawai
5
5
 
6
6
  class ApplicationManager
7
7
 
8
- def initialize
8
+ def initialize(app)
9
9
 
10
10
  puts "Initializing application manager..."
11
11
 
12
+ @app = app
13
+
12
14
  @actions = Hash.new
13
15
  @innerActions = Hash.new
14
16
 
15
17
  @globalFilters = Array.new
16
18
  @globalConsequences = Hash.new
17
19
 
18
- @loader = Loader.new('./actions')
20
+ @loader = Loader.new(".#{app.contextPathDir}/WEB-INF")
19
21
  @loader.reloadFiles
20
22
 
21
23
  @pageMethods = initPageMethods
@@ -26,6 +28,10 @@ module Mentawai
26
28
  # should be overriden...
27
29
  end
28
30
 
31
+ def app
32
+ @app
33
+ end
34
+
29
35
  def pageMethods
30
36
  @pageMethods
31
37
  end
@@ -44,9 +50,20 @@ module Mentawai
44
50
  @customPageMethods
45
51
  end
46
52
 
47
- def action(className, actionName = nil, innerAction = nil)
48
- ac = ActionConfig.new(className, actionName, innerAction)
49
- addActionConfig(ac)
53
+ def action(params)
54
+ if params.is_a?(Hash)
55
+ class_name = params[:class_name]; raise "class_name is required!" if not class_name
56
+ action_name = params[:action_name]; action_name.sub!(/^\//, "") if action_name
57
+ inner_action = params[:inner_action]
58
+ ac = ActionConfig.new(class_name, action_name, inner_action)
59
+ addActionConfig(ac)
60
+ elsif params.is_a?(String)
61
+ class_name = params
62
+ ac = ActionConfig.new(class_name)
63
+ addActionConfig(ac)
64
+ else
65
+ raise "Invalid arguments to create action config!"
66
+ end
50
67
  end
51
68
 
52
69
  def addActionConfig(ac)
@@ -90,7 +107,8 @@ module Mentawai
90
107
  else
91
108
  map = @innerActions[actionName]
92
109
  if map == nil
93
- nil
110
+ # Use consequences from regular action
111
+ @actions[actionName]
94
112
  else
95
113
  map[innerAction]
96
114
  end
@@ -98,11 +116,32 @@ module Mentawai
98
116
  end
99
117
  end
100
118
 
119
+ # Methods to create consequences...
120
+
121
+ def fwd(page)
122
+ Forward.new(app.contextPathDir, page)
123
+ end
124
+
101
125
  private
102
126
 
103
127
  def initPageMethods
104
128
  h = Hash.new
105
129
  h[:m_out] = [ 'Mentawai::Page::Method::Out', 'print' ]
130
+ h[:m_content_type] = [ 'Mentawai::Page::Method::ContentType', 'set_content_type' ]
131
+
132
+ # Message, Error and Field Error
133
+
134
+ h[:m_has_errors] = %w{ Mentawai::Page::Method::Message hasErrors }
135
+ h[:m_has_error] = %w{ Mentawai::Page::Method::Message hasError }
136
+ h[:m_has_msgs] = %w{ Mentawai::Page::Method::Message hasMessages }
137
+ h[:m_has_msg] = h[:m_has_msgs]
138
+ h[:m_errors] = %w{ Mentawai::Page::Method::Message errors }
139
+ h[:m_error] = %w{ Mentawai::Page::Method::Message error }
140
+ h[:m_msgs] = %w{ Mentawai::Page::Method::Message messages }
141
+ h[:m_msg] = %w{ Mentawai::Page::Method::Message message }
142
+ h[:m_field_errors] = %w{ Mentawai::Page::Method::Message fieldErrors }
143
+
144
+ # Don't forget to return this
106
145
  h
107
146
  end
108
147
 
@@ -1,4 +1,7 @@
1
1
  require 'monitor'
2
+ require 'mentawai/i18n/loc_manager'
3
+
4
+ include Mentawai::I18N
2
5
 
3
6
  module Mentawai
4
7
  module Core
@@ -8,13 +11,20 @@ module Mentawai
8
11
  @@forward_cache = Hash.new
9
12
  @@forward_cache.extend(MonitorMixin)
10
13
 
11
- def initialize(contextPath, appContext, appManager)
12
- @contextPath = contextPath
13
- @appContext = appContext
14
- @appManager = appManager
14
+ def initialize(app)
15
+ @contextPath = app.contextPath
16
+ @appContext = app.appContext
17
+ @appManager = app.appManager
18
+ @extension = app.extension
19
+ @consequence = nil
20
+ @action = nil
15
21
  end
16
22
 
17
- attr_reader :appManager, :action, :contextPath
23
+ attr_reader :appManager, :contextPath, :extension
24
+
25
+ def action
26
+ @action
27
+ end
18
28
 
19
29
  def output
20
30
  @action.output
@@ -44,21 +54,26 @@ module Mentawai
44
54
  @action.page
45
55
  end
46
56
 
57
+ def consequence
58
+ @consequence
59
+ end
60
+
47
61
  def service(env, req, res)
48
62
 
49
63
  url = req.fullpath.dup
50
64
 
51
65
  # Check if we are directly acessing a
52
66
  # static page instead of an action
53
- if url =~ /\.(\w{1,4})$/ then
54
- if Forward.types.key?($1) then
55
- return service_direct(env, req, res)
56
- end
67
+ # By the time we get here, this can only be a mentawai action
68
+ # or a forward to a dynamic page such as ERB.
69
+ # (see MentaHandler to understand how it works!)
70
+ unless url =~ /\.#{extension}\??$/ or url =~ /\.#{extension}\?.+$/
71
+ return service_direct(env, req, res)
57
72
  end
58
73
 
59
74
  fix_url(url)
60
75
 
61
- url.gsub!(/\.\w{1,4}$/, "") # remote any extension (max 4 chars)
76
+ url.gsub!(/\.\w+$/, "") # remote any extension
62
77
 
63
78
  actionName = nil
64
79
  innerAction = nil
@@ -73,7 +88,7 @@ module Mentawai
73
88
  @appManager.reloadActions
74
89
 
75
90
  ac = @appManager.getActionConfig(actionName, innerAction)
76
-
91
+
77
92
  if ac then
78
93
  @action = eval("#{ac.className}.new")
79
94
  else
@@ -93,28 +108,34 @@ module Mentawai
93
108
 
94
109
  conseq = nil
95
110
 
111
+ # Action specific consequence...
96
112
  conseq = ac.getConsequence(result) if ac
97
113
 
114
+ # Global consequence...
98
115
  conseq = @appManager.getConsequence(result) if not conseq
99
116
 
100
117
  if not conseq then
101
118
 
102
119
  # Use conventions to find a view
103
- dest = actionName + "/" + (innerAction ? innerAction : 'index') + '.erb'
120
+ dest = '/' + actionName + "/" + (innerAction ? innerAction : 'index') + '.erb'
121
+
122
+ full_dest = @appManager.app.contextPathDir + dest
104
123
 
105
124
  # Cache forward consequence...
106
125
  @@forward_cache.synchronize {
107
- conseq = @@forward_cache[dest]
126
+ conseq = @@forward_cache[full_dest]
108
127
  if not conseq then
109
128
  # Check if file exists
110
- if File.exists?("./views/" + dest) then
111
- @@forward_cache[dest] = conseq = Forward.new(dest)
129
+ if File.exists?('.' + full_dest) then
130
+ @@forward_cache[full_dest] = conseq = Forward.new(@appManager.app.contextPathDir, dest)
112
131
  end
113
132
  end
114
133
  }
115
134
  end
116
135
 
117
- raise "Cannot find consequence! action=#{actionName} inner=#{innerAction ? innerAction : 'nil'}" if not conseq
136
+ raise "Cannot find consequence! result=#{result} action=#{actionName} inner=#{innerAction ? innerAction : 'nil'}" if not conseq
137
+
138
+ @consequence = conseq
118
139
 
119
140
  conseq.exec(self, req, res, @action)
120
141
 
@@ -124,6 +145,8 @@ module Mentawai
124
145
 
125
146
  # Accessing a page file directly
126
147
 
148
+ full_url = @appManager.app.contextPathDir + req.fullpath
149
+
127
150
  url = req.fullpath.dup
128
151
 
129
152
  fix_url(url)
@@ -136,10 +159,12 @@ module Mentawai
136
159
 
137
160
  # Cache forward consequence...
138
161
  @@forward_cache.synchronize {
139
- forward = @@forward_cache[url]
140
- @@forward_cache[url] = forward = Forward.new(url) if not forward
162
+ forward = @@forward_cache[full_url]
163
+ @@forward_cache[full_url] = forward = Forward.new(@appManager.app.contextPathDir, url) if not forward
141
164
  }
142
165
 
166
+ @consequence = forward
167
+
143
168
  forward.exec(self, req, res, @action)
144
169
 
145
170
  end
@@ -166,6 +191,8 @@ module Mentawai
166
191
  action.cookies = c
167
192
  action.application = @appContext
168
193
  action.page = Hash.new
194
+ locales = get_locales(request.env['HTTP_ACCEPT_LANGUAGE'])
195
+ action.locale = LocaleManager.instance.get(locales)
169
196
  else
170
197
  raise "Not an action: " + action.to_s
171
198
  end
@@ -174,6 +201,15 @@ module Mentawai
174
201
 
175
202
  private
176
203
 
204
+ def get_locales(langs)
205
+ return nil if langs.nil?
206
+ locs = langs.split(/\s*,\s*/)
207
+ locs.collect! do |x|
208
+ x.split(/\s*\;\s*/)[0]
209
+ end
210
+ locs
211
+ end
212
+
177
213
  def exec_page_method(className, methodName, args)
178
214
  if args.length <= 0 then
179
215
  m = eval(className + ".new(self, nil)")
@@ -184,7 +220,7 @@ module Mentawai
184
220
  end
185
221
 
186
222
  def method_missing(id, *args)
187
-
223
+
188
224
  # Crazy bug: method_missing is being called for everything
189
225
  # and not just for controller. Make a crazy call inside
190
226
  # an action an you will see;
@@ -6,26 +6,40 @@ module Mentawai
6
6
 
7
7
  class Forward
8
8
 
9
- DEFAULT_TYPE = 'text/html'
10
-
11
9
  @@types = Hash.new
12
- @@types['erb'] = 'text/html'
13
- @@types['txt'] = 'text/plain'
14
- @@types['xml'] = 'text/xml'
10
+ @@types['.erb'] = 'text/html'
11
+
12
+ def self.add_extension(extension, default_mime_type = "text/html")
13
+ @@types[extension] = default_mime_type
14
+ end
15
15
 
16
- def self.types
17
- @@types
16
+ def self.is_registered?(extension)
17
+ @@types.key?(extension)
18
18
  end
19
19
 
20
- def initialize(page)
20
+ def initialize(context_path_dir, page)
21
21
  @page = page
22
- if page =~ /\.(\w+)$/ then
23
- @contentType = @@types[$1] || DEFAULT_TYPE
22
+
23
+ extension = nil
24
+
25
+ if page =~ /(\.\w+)$/ then
26
+ extension = $1
27
+ end
28
+
29
+ if not extension or not @@types.key?(extension)
30
+ raise "File extension not supported: #{page}"
31
+ end
32
+
33
+ @contentType = @@types[extension]
34
+
35
+ if page !~ /^\//
36
+ @filename = ".#{context_path_dir}/#{page}"
24
37
  else
25
- @contentType = DEFAULT_TYPE
38
+ @filename = ".#{context_path_dir}#{page}"
26
39
  end
27
- @filename = './views/' + page
28
- raise "Cannot find page: " + page if not File.exists?(@filename)
40
+
41
+ raise "Cannot find page: " + @filename if not File.exists?(@filename)
42
+
29
43
  @file = File.new(@filename)
30
44
  @lastmodified = @file.mtime
31
45
  input = File.read(@filename)
@@ -33,6 +47,10 @@ module Mentawai
33
47
  @lock = Mutex.new
34
48
  end
35
49
 
50
+ def content_type=(type)
51
+ @contentType = type
52
+ end
53
+
36
54
  def exec(controller, req, res, action)
37
55
  @lock.synchronize {
38
56
  if @lastmodified != @file.mtime then
@@ -15,7 +15,7 @@ module Mentawai
15
15
  # Copy headers...
16
16
  @headers = Hash.new
17
17
  req.env.each do |k,v|
18
- @headers[k] = v if k =~ /^[A-Z]/ # Begin with capital
18
+ @headers[k] = v if k =~ /^HTTP_/ # Begin with HTTP
19
19
  end
20
20
  end
21
21
 
@@ -36,9 +36,7 @@ module Mentawai
36
36
  end
37
37
 
38
38
  def headers
39
- @headers.each do |k,v|
40
- yield k
41
- end
39
+ @headers
42
40
  end
43
41
 
44
42
  def keys
@@ -72,11 +70,13 @@ module Mentawai
72
70
  alias hasProp? hasProperty?
73
71
 
74
72
  def getProperty(prop)
75
- @request.class.public_instance_methods.each do |m|
76
- if m == prop then
77
- return @request.send(prop)
78
- end
79
- end
73
+ # Try env...
74
+ return @request.env[prop] if @request.env.key?(prop)
75
+
76
+ # Capitalize?
77
+ return @request.env[prop.upcase] if @request.env.key?(prop.upcase)
78
+
79
+ # Nothing found...
80
80
  nil
81
81
  end
82
82
 
@@ -0,0 +1,178 @@
1
+ require 'rack'
2
+ require 'mongrel/handlers'
3
+
4
+ module Mentawai
5
+ module Handler
6
+
7
+ class MentaHandler < Rack::Handler::Mongrel
8
+
9
+ def initialize(server, app)
10
+
11
+ @server = server
12
+
13
+ listing_allowed = false
14
+
15
+ # Check if any application has listing turned on...
16
+ @server.apps.each do |k,v|
17
+ if v.listing_allowed
18
+ listing_allowed = true
19
+ break
20
+ end
21
+ end
22
+
23
+ @dir_handler = Mongrel::DirHandler.new('.', listing_allowed)
24
+
25
+ super app
26
+ end
27
+
28
+ def self.mime_types
29
+ Mongrel::DirHandler.const_get(:MIME_TYPES)
30
+ end
31
+
32
+ def self.add_mime_type(extension, type)
33
+ mime_types[extension] = type
34
+ end
35
+
36
+ def self.run(menta_server, app, options={})
37
+ server = ::Mongrel::HttpServer.new(options[:Host] || '0.0.0.0', options[:Port] || 8080)
38
+ server.register('/', MentaHandler.new(menta_server, app))
39
+ yield server if block_given?
40
+ server.run.join
41
+ end
42
+
43
+ def process(request, response)
44
+
45
+ env = {}.replace(request.params)
46
+ env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
47
+ env["QUERY_STRING"] ||= ""
48
+ env.delete "PATH_INFO" if env["PATH_INFO"] == ""
49
+
50
+ req = Rack::Request.new(env)
51
+
52
+ url = req.fullpath
53
+
54
+ context_path = @server.find_context_path(url)
55
+
56
+ app = @server.apps[context_path]
57
+
58
+ context_path_dir = app.contextPathDir
59
+
60
+ ru = RequestURL.new(context_path, context_path_dir, url, app.default_page)
61
+
62
+ # Block forbidden directory...
63
+ if ru.forbidden? then
64
+ response.start(403) do |head,out|
65
+ out << "You are not allowed to go inside here !!!"
66
+ end
67
+ return
68
+ end
69
+
70
+ # Block if no listing is allowed and this is a directory...
71
+ if ru.is_dir? and not app.listing_allowed
72
+ response.start(403) do |head,out|
73
+ out << "Listing is not unabled for this application!"
74
+ end
75
+ return
76
+ end
77
+
78
+ # Block if listing is allowed but directory is not allowed...
79
+ if ru.is_dir? and app.listing_allowed and not app.dirs_allowed_for_listing.empty?
80
+
81
+ allowed = false
82
+
83
+ app.dirs_allowed_for_listing.each do |dir|
84
+ dir = '/' + dir if dir !~ /^\//
85
+ if ru.url_without_context_dir =~ /^#{dir}\/?$/
86
+ allowed = true
87
+ break
88
+ end
89
+ end
90
+
91
+ if not allowed
92
+ response.start(403) do |head,out|
93
+ out << "You cannot list this directory! (Forbidden)"
94
+ end
95
+ return
96
+ end
97
+ end
98
+
99
+ if url =~ /\.#{app.extension}\??$/ or url =~ /\.#{app.extension}\?.+$/
100
+ # We need to process this inside the Mentawai controller... (action request)
101
+ super
102
+ elsif (url =~ /(\.\w+)\??$/ or url =~ /(\.\w+)\?.+$/) and Mentawai::Core::Forward.is_registered?($1)
103
+ # We need to process this inside the Mentawai controller... (ERB page request)
104
+ super
105
+ else
106
+ # Change Mongrel request to reflect the context path dir...
107
+ request.params["PATH_INFO"] = ru.url_with_context_dir
108
+
109
+ # Let Mongrel serve this request...
110
+ @dir_handler.process(request, response)
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ class RequestURL
117
+
118
+ def initialize(context_path, context_path_dir, url, default_page)
119
+
120
+ # Construct file path for the URL...
121
+ if context_path == '/'
122
+ @url_file_path = '.' + context_path_dir + url
123
+ @url_with_context_dir = context_path_dir + url
124
+ @url_without_context_dir = url
125
+ else
126
+ @url_file_path = '.' + url
127
+ @url_with_context_dir = url
128
+ @url_without_context_dir = url.sub(/#{context_path_dir}/, "")
129
+ @url_without_context_dir = '/' if @url_without_context_dir == ""
130
+ end
131
+
132
+ @is_dir = File.directory?(@url_file_path)
133
+
134
+ # Check if we need to append the default_page
135
+ if @is_dir
136
+ url_file_path_with_default_page = append_filename(@url_file_path, default_page)
137
+ if File.exists?(url_file_path_with_default_page)
138
+ @is_dir = false
139
+ @url_without_context_dir = append_filename(@url_without_context_dir, default_page)
140
+ @url_with_context_dir = append_filename(@url_with_context_dir, default_page)
141
+ @url_file_path = url_file_path_with_default_page
142
+ end
143
+ end
144
+
145
+ # Check if this is a forbidden URL
146
+ if @url_with_context_dir =~ /^#{context_path_dir}\/WEB-INF/
147
+ @forbidden = true
148
+ else
149
+ @forbidden = false
150
+ end
151
+ end
152
+
153
+ attr_reader :url_without_context_dir, :url_with_context_dir, :url_file_path
154
+
155
+ def is_dir?
156
+ @is_dir
157
+ end
158
+
159
+ def forbidden?
160
+ @forbidden
161
+ end
162
+
163
+ def to_s
164
+ "RequestURL: without=#{url_without_context_dir} with=#{url_with_context_dir} path=#{url_file_path} forbidden=#{forbidden?} is_dir=#{is_dir?}"
165
+ end
166
+
167
+ private
168
+
169
+ def append_filename(path, filename)
170
+ p = path
171
+ p += '/' if p !~ /\/$/
172
+ p += filename
173
+ end
174
+
175
+ end
176
+
177
+ end
178
+ end
@@ -0,0 +1,61 @@
1
+ require 'singleton'
2
+
3
+ module Mentawai
4
+ module I18N
5
+
6
+ class LocaleManager
7
+ include Singleton
8
+
9
+ def initialize
10
+ @locales = Array.new
11
+ end
12
+
13
+ def add(loc)
14
+ @locales.add(loc)
15
+ end
16
+
17
+ def locales
18
+ @locales
19
+ end
20
+
21
+ def default
22
+ if @locales.empty?
23
+ 'en_US'
24
+ else
25
+ @locales[0]
26
+ end
27
+ end
28
+
29
+ def get(locale, return_default = true)
30
+
31
+ if locale.nil?
32
+ return return_default ? default : nil
33
+ end
34
+
35
+ # If array, try to match any of them
36
+ if locale.is_a?(Array)
37
+ locale.each do |loc|
38
+ l = get(loc, false) # recursive no default here...
39
+ return l if l
40
+ end
41
+ return return_default ? default : nil
42
+ end
43
+
44
+ # First match the whole thing
45
+ @locales.each do |loc|
46
+ return loc if locale == loc
47
+ end
48
+ # Now match only the language
49
+ l = locale.split(/_/)[0]
50
+ @locales.each do |loc|
51
+ l2 = loc.split(/_/)[0]
52
+ return loc if l2 == l
53
+ end
54
+ # Return default or the given locale
55
+ return_default ? default : nil
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
@@ -12,7 +12,13 @@ module Mentawai
12
12
  @files = { }
13
13
 
14
14
  Dir.glob(@file_mask).each do |f|
15
+
16
+ # Skip some files here...
15
17
  next if f =~ /loader\.rb$/
18
+ next if f =~ /menta_handler\.rb$/
19
+ next if f =~ /server\.rb$/
20
+ next if f =~ /application\.rb$/
21
+
16
22
  @files[File.new(f)] = 0
17
23
  end
18
24
 
@@ -0,0 +1,19 @@
1
+ require 'mentawai/page/page_method'
2
+
3
+ module Mentawai
4
+ module Page
5
+ module Method
6
+
7
+ class ContentType < Mentawai::Page::PageMethod
8
+
9
+ def set_content_type
10
+ raise "Value attribute is mandatory!" if not arg?(:value)
11
+ if consequence and consequence.respond_to?(:content_type=)
12
+ consequence.content_type = arg[:value]
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end