merb 0.3.1 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/Rakefile +19 -9
- data/TODO +2 -15
- data/examples/skeleton.tar +0 -0
- data/examples/skeleton/dist/conf/router.rb +2 -2
- data/lib/merb.rb +9 -11
- data/lib/merb/core_ext.rb +0 -1
- data/lib/merb/core_ext/merb_hash.rb +70 -1
- data/lib/merb/core_ext/merb_symbol.rb +1 -7
- data/lib/merb/merb_controller.rb +13 -9
- data/lib/merb/merb_dispatcher.rb +1 -1
- data/lib/merb/merb_mailer.rb +7 -5
- data/lib/merb/merb_request.rb +8 -4
- data/lib/merb/merb_router.rb +238 -150
- data/lib/merb/merb_server.rb +26 -3
- data/lib/merb/merb_view_context.rb +20 -4
- data/lib/merb/mixins/controller_mixin.rb +24 -13
- data/lib/merb/mixins/form_control_mixin.rb +5 -0
- data/lib/merb/mixins/render_mixin.rb +24 -11
- data/lib/merb/mixins/view_context_mixin.rb +1 -1
- data/lib/merb/template/haml.rb +35 -29
- data/lib/merb/template/markaby.rb +8 -1
- data/lib/tasks/merb.rake +8 -0
- metadata +29 -2
data/README
CHANGED
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ include FileUtils
|
|
15
15
|
|
16
16
|
|
17
17
|
NAME = "merb"
|
18
|
-
VERS = "0.3.
|
18
|
+
VERS = "0.3.3"
|
19
19
|
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
20
20
|
|
21
21
|
setup_clean [ "pkg", "lib/*.bundle", "*.gem",
|
@@ -29,13 +29,6 @@ task :merb => [:clean, :rdoc, :package]
|
|
29
29
|
task :doc => [:rdoc]
|
30
30
|
|
31
31
|
|
32
|
-
#Rake::RDocTask.new do |rdoc|
|
33
|
-
# rdoc.rdoc_dir = 'doc/rdoc'
|
34
|
-
# rdoc.options += RDOC_OPTS
|
35
|
-
# rdoc.main = "README"
|
36
|
-
# rdoc.title = "Merb Documentation"
|
37
|
-
# rdoc.rdoc_files.add ['README', 'LICENSE', 'TODO', 'lib/**/*.rb']
|
38
|
-
#end
|
39
32
|
Rake::RDocTask.new do |rdoc|
|
40
33
|
files =['README', 'LICENSE', 'TODO', 'lib/**/*.rb']
|
41
34
|
rdoc.rdoc_files.add(files)
|
@@ -66,6 +59,9 @@ spec = Gem::Specification.new do |s|
|
|
66
59
|
s.add_dependency('mongrel')
|
67
60
|
s.add_dependency('erubis')
|
68
61
|
s.add_dependency('json')
|
62
|
+
s.add_dependency('mime-types')
|
63
|
+
s.add_dependency('xml-simple')
|
64
|
+
s.add_dependency('archive-tar-minitar')
|
69
65
|
s.required_ruby_version = '>= 1.8.4'
|
70
66
|
|
71
67
|
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{bin,test,lib,examples}/**/*")
|
@@ -132,7 +128,7 @@ end
|
|
132
128
|
|
133
129
|
desc 'Run all tests, specs and finish with rcov'
|
134
130
|
task :aok do
|
135
|
-
sh %{rake specs}
|
131
|
+
sh %{rake specs;rake rcov}
|
136
132
|
end
|
137
133
|
|
138
134
|
desc "Run all specs"
|
@@ -144,11 +140,25 @@ end
|
|
144
140
|
|
145
141
|
desc "RCov"
|
146
142
|
Spec::Rake::SpecTask.new('rcov') do |t|
|
143
|
+
t.spec_opts = ["--format", "specdoc"]
|
147
144
|
t.spec_files = FileList['specs/**/*_spec.rb']
|
148
145
|
t.libs = ['lib', 'server/lib' ]
|
149
146
|
t.rcov = true
|
150
147
|
end
|
151
148
|
|
149
|
+
STATS_DIRECTORIES = [
|
150
|
+
%w(Code lib/),
|
151
|
+
%w(Unit\ tests specs),
|
152
|
+
].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) }
|
153
|
+
|
154
|
+
desc "Report code statistics (KLOCs, etc) from the application"
|
155
|
+
task :stats do
|
156
|
+
require __DIR__ + '/tools/code_statistics'
|
157
|
+
#require 'extra/stats'
|
158
|
+
verbose = true
|
159
|
+
CodeStatistics.new(*STATS_DIRECTORIES).to_s
|
160
|
+
end
|
161
|
+
|
152
162
|
##############################################################################
|
153
163
|
# SVN
|
154
164
|
##############################################################################
|
data/TODO
CHANGED
@@ -1,16 +1,3 @@
|
|
1
1
|
[TODO]
|
2
|
-
*
|
3
|
-
*
|
4
|
-
* Helpers
|
5
|
-
* config file **DONE**
|
6
|
-
* session
|
7
|
-
** AR ** DONE **
|
8
|
-
** DRb
|
9
|
-
* Rails integration
|
10
|
-
* Text backend?
|
11
|
-
* plugins support(rails plugins too?)
|
12
|
-
* Layouts **DONE**
|
13
|
-
* auto template_dir **DONE**
|
14
|
-
* before/after filters **DONE**
|
15
|
-
* merbjs **DONE**
|
16
|
-
* testing
|
2
|
+
* more specs for Template engines and Rendering
|
3
|
+
*
|
data/examples/skeleton.tar
CHANGED
Binary file
|
@@ -10,7 +10,7 @@
|
|
10
10
|
|
11
11
|
|
12
12
|
puts "Compiling routes.."
|
13
|
-
Merb::
|
13
|
+
Merb::Router.prepare do |r|
|
14
14
|
# restfull routes
|
15
15
|
# r.resources :posts
|
16
16
|
|
@@ -19,4 +19,4 @@ Merb::RouteMatcher.prepare do |r|
|
|
19
19
|
|
20
20
|
# change this for your home page to be avaiable at /
|
21
21
|
#r.add '/', :controller => 'whatever', :action =>'index'
|
22
|
-
end
|
22
|
+
end
|
data/lib/merb.rb
CHANGED
@@ -11,16 +11,11 @@ end
|
|
11
11
|
require 'fileutils'
|
12
12
|
require 'erubis'
|
13
13
|
require 'logger'
|
14
|
-
|
15
|
-
begin
|
16
|
-
require 'fjson'
|
17
|
-
rescue LoadError
|
18
|
-
require 'json'
|
19
|
-
end
|
14
|
+
require 'json'
|
20
15
|
|
21
16
|
|
22
17
|
module Merb
|
23
|
-
VERSION='0.3.
|
18
|
+
VERSION='0.3.3' unless defined?VERSION
|
24
19
|
class Server
|
25
20
|
class << self
|
26
21
|
def config
|
@@ -56,6 +51,7 @@ MERB_FRAMEWORK_ROOT = __DIR__
|
|
56
51
|
MERB_ROOT = Merb::Server.merb_root || Dir.pwd
|
57
52
|
DIST_ROOT = Merb::Server.dist_root || Dir.pwd+'/dist'
|
58
53
|
MERB_ENV = Merb::Server.config[:environment]
|
54
|
+
MERB_VIEW_ROOT = MERB_ROOT / "dist/app/views"
|
59
55
|
|
60
56
|
logpath = $TESTING ? "/tmp/merb_test.log" : "#{MERB_ROOT}/log/merb.#{Merb::Server.port}.log"
|
61
57
|
MERB_LOGGER = Logger.new(logpath)
|
@@ -78,10 +74,12 @@ MERB_LOGGER.level = case (Merb::Server.log_level.downcase rescue '')
|
|
78
74
|
end
|
79
75
|
|
80
76
|
module Mongrel::Const
|
81
|
-
HTTP_COOKIE
|
82
|
-
QUERY_STRING
|
83
|
-
APPLICATION_JSON
|
84
|
-
TEXT_JSON
|
77
|
+
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
78
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
79
|
+
APPLICATION_JSON = 'application/json'.freeze
|
80
|
+
TEXT_JSON = 'text/x-json'.freeze
|
81
|
+
APPLICATION_XML = 'application/xml'.freeze
|
82
|
+
TEXT_XML = 'text/xml'.freeze
|
85
83
|
UPCASE_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
86
84
|
end
|
87
85
|
|
data/lib/merb/core_ext.rb
CHANGED
@@ -1,8 +1,77 @@
|
|
1
1
|
class Hash
|
2
|
+
|
3
|
+
class << self
|
4
|
+
def from_xml(xml)
|
5
|
+
# TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
|
6
|
+
undasherize_keys(typecast_xml_value(XmlSimple.xml_in(xml,
|
7
|
+
'forcearray' => false,
|
8
|
+
'forcecontent' => true,
|
9
|
+
'keeproot' => true,
|
10
|
+
'contentkey' => '__content__')
|
11
|
+
))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def typecast_xml_value(value)
|
16
|
+
case value.class.to_s
|
17
|
+
when "Hash"
|
18
|
+
if value.has_key?("__content__")
|
19
|
+
content = translate_xml_entities(value["__content__"])
|
20
|
+
case value["type"]
|
21
|
+
when "integer" then content.to_i
|
22
|
+
when "boolean" then content.strip == "true"
|
23
|
+
when "datetime" then ::Time.parse(content).utc
|
24
|
+
when "date" then ::Date.parse(content)
|
25
|
+
else content
|
26
|
+
end
|
27
|
+
else
|
28
|
+
(value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)|
|
29
|
+
h[k] = typecast_xml_value(v)
|
30
|
+
h
|
31
|
+
end
|
32
|
+
end
|
33
|
+
when "Array"
|
34
|
+
value.map! { |i| typecast_xml_value(i) }
|
35
|
+
case value.length
|
36
|
+
when 0 then nil
|
37
|
+
when 1 then value.first
|
38
|
+
else value
|
39
|
+
end
|
40
|
+
when "String"
|
41
|
+
value
|
42
|
+
else
|
43
|
+
raise "can't typecast #{value.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def translate_xml_entities(value)
|
48
|
+
value.gsub(/</, "<").
|
49
|
+
gsub(/>/, ">").
|
50
|
+
gsub(/"/, '"').
|
51
|
+
gsub(/'/, "'").
|
52
|
+
gsub(/&/, "&")
|
53
|
+
end
|
54
|
+
|
55
|
+
def undasherize_keys(params)
|
56
|
+
case params.class.to_s
|
57
|
+
when "Hash"
|
58
|
+
params.inject({}) do |h,(k,v)|
|
59
|
+
h[k.to_s.tr("-", "_")] = undasherize_keys(v)
|
60
|
+
h
|
61
|
+
end
|
62
|
+
when "Array"
|
63
|
+
params.map { |v| undasherize_keys(v) }
|
64
|
+
else
|
65
|
+
params
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
2
71
|
def with_indifferent_access
|
3
72
|
MerbHash.new(self)
|
4
73
|
end
|
5
|
-
def to_params
|
74
|
+
def to_params
|
6
75
|
result = ''
|
7
76
|
stack = []
|
8
77
|
|
data/lib/merb/merb_controller.rb
CHANGED
@@ -21,8 +21,8 @@ module Merb
|
|
21
21
|
self._layout = :application
|
22
22
|
self._session_id_key = :_session_id
|
23
23
|
self._template_extensions = { }
|
24
|
-
self._template_root = File.expand_path(
|
25
|
-
self._layout_root = File.expand_path(
|
24
|
+
self._template_root = File.expand_path(MERB_VIEW_ROOT)
|
25
|
+
self._layout_root = File.expand_path(MERB_VIEW_ROOT / "layout")
|
26
26
|
|
27
27
|
include Merb::ControllerMixin
|
28
28
|
include Merb::RenderMixin
|
@@ -51,6 +51,8 @@ module Merb
|
|
51
51
|
json = JSON.parse(request.read || "") || {}
|
52
52
|
json = MerbHash.new(json) if json.is_a? Hash
|
53
53
|
querystring.update(json)
|
54
|
+
elsif [Mongrel::Const::APPLICATION_XML, Mongrel::Const::TEXT_XML].include?(@env[Mongrel::Const::UPCASE_CONTENT_TYPE])
|
55
|
+
querystring.update(Hash.from_xml(request.read).with_indifferent_access)
|
54
56
|
else
|
55
57
|
querystring.update(query_parse(request.read))
|
56
58
|
end
|
@@ -64,7 +66,7 @@ module Merb
|
|
64
66
|
if @params.key?(:_method) && allow.include?(@method)
|
65
67
|
@method = @params.delete(:_method).downcase.intern
|
66
68
|
end
|
67
|
-
@request = Request.new(@env, @method)
|
69
|
+
@request = Request.new(@env, @method, request)
|
68
70
|
|
69
71
|
MERB_LOGGER.info("Params: #{params.inspect}\nCookies: #{cookies.inspect}")
|
70
72
|
end
|
@@ -178,12 +180,14 @@ module Merb
|
|
178
180
|
else
|
179
181
|
ok = true
|
180
182
|
end
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
183
|
+
if ok
|
184
|
+
case filter
|
185
|
+
when Symbol, String
|
186
|
+
send(filter)
|
187
|
+
when Proc
|
188
|
+
filter.call(self)
|
189
|
+
end
|
190
|
+
end
|
187
191
|
end
|
188
192
|
return :filter_chain_completed
|
189
193
|
end
|
data/lib/merb/merb_dispatcher.rb
CHANGED
@@ -49,7 +49,7 @@ module Merb
|
|
49
49
|
def route_path(path)
|
50
50
|
path = path.sub(/\/+/, '/').sub(/\?.*$/, '')
|
51
51
|
path = path[0..-2] if (path[-1] == ?/) && path.size > 1
|
52
|
-
Merb::
|
52
|
+
Merb::Router.match(path)
|
53
53
|
end
|
54
54
|
|
55
55
|
# take a controller class name string and reload or require
|
data/lib/merb/merb_mailer.rb
CHANGED
@@ -45,11 +45,7 @@ module Merb
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
=begin
|
48
|
-
|
49
|
-
:from => 'bar@foo.com',
|
50
|
-
:subject => 'Welcome to whatever!',
|
51
|
-
:body => partial(:sometemplate)
|
52
|
-
m.deliver!
|
48
|
+
|
53
49
|
|
54
50
|
Merb::Mailer.config = {
|
55
51
|
:host=>'smtp.yourserver.com',
|
@@ -58,4 +54,10 @@ Merb::Mailer.config = {
|
|
58
54
|
:pass=>'pass',
|
59
55
|
:auth=>:plain # :plain, :login, or :cram_md5, default :plain
|
60
56
|
}
|
57
|
+
m = Merb::Mailer.new :to => 'foo@bar.com',
|
58
|
+
:from => 'bar@foo.com',
|
59
|
+
:subject => 'Welcome to whatever!',
|
60
|
+
:body => partial(:sometemplate)
|
61
|
+
m.deliver!
|
62
|
+
|
61
63
|
=end
|
data/lib/merb/merb_request.rb
CHANGED
@@ -2,11 +2,17 @@ module Merb
|
|
2
2
|
|
3
3
|
class Request
|
4
4
|
attr_accessor :env
|
5
|
-
def initialize(env, method)
|
5
|
+
def initialize(env, method, request)
|
6
6
|
@env = env
|
7
7
|
@method = method
|
8
|
+
@request = request
|
8
9
|
end
|
9
10
|
|
11
|
+
def raw_post
|
12
|
+
@request.rewind
|
13
|
+
@request.read
|
14
|
+
end
|
15
|
+
|
10
16
|
# returns true if the request is an ajax request.
|
11
17
|
def xml_http_request?
|
12
18
|
not /XMLHttpRequest/i.match(@env['HTTP_X_REQUESTED_WITH']).nil?
|
@@ -148,9 +154,7 @@ module Merb
|
|
148
154
|
|
149
155
|
# create predicate methods for querying the REQUEST_METHOD
|
150
156
|
[:get, :post, :put, :delete, :head].each do |m|
|
151
|
-
|
152
|
-
def #{m}?; method == :#{m}; end
|
153
|
-
}
|
157
|
+
define_method("#{m}?") { method == m.to_sym }
|
154
158
|
end
|
155
159
|
|
156
160
|
end
|
data/lib/merb/merb_router.rb
CHANGED
@@ -1,97 +1,248 @@
|
|
1
1
|
module Merb
|
2
2
|
|
3
|
-
|
4
|
-
# You can define placeholder parts of the url with the :symbol notation.
|
5
|
-
# so r.add '/foo/:bar/baz/:id', :class => 'Bar', :method => 'foo'
|
6
|
-
# will match against a request to /foo/123/baz/456. It will then
|
7
|
-
# use the class Bar as your merb controller and call the foo method on it.
|
8
|
-
# the foo method will recieve a hash with {:bar => '123', :id => '456'}
|
9
|
-
# as the content. So the :placeholders sections of your routes become
|
10
|
-
# a hash of arguments to your controller methods. This route maps
|
11
|
-
# the default /controller/action and /controller/action/id urls:
|
12
|
-
# r.add '/:class/:method/:id'
|
13
|
-
class RouteMatcher
|
3
|
+
class Router
|
14
4
|
|
15
|
-
|
16
|
-
@@section_regexp = /(?::([a-z*_]+))/.freeze
|
5
|
+
SECTION_REGEXP = /(?::([a-z*_]+))/.freeze
|
17
6
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def prepare(&block)
|
10
|
+
@@matcher = RouteMatcher.new
|
11
|
+
@@generator = RouteGenerator.new
|
12
|
+
|
13
|
+
yield self
|
14
|
+
|
15
|
+
@@matcher.compile_router
|
16
|
+
end
|
17
|
+
|
18
|
+
def add(*route)
|
19
|
+
@@matcher.add(*route)
|
20
|
+
end
|
21
|
+
|
22
|
+
def matcher
|
23
|
+
@@matcher
|
24
|
+
end
|
25
|
+
|
26
|
+
def generator
|
27
|
+
@@generator
|
28
|
+
end
|
29
|
+
|
30
|
+
def match(path)
|
31
|
+
@@matcher.route_request(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate(method, options = {})
|
35
|
+
@@generator.generate(method, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def resources(res, opts={})
|
39
|
+
opts[:prefix] ||= ""
|
40
|
+
if block_given?
|
41
|
+
procs = []
|
42
|
+
yield Resource.new(res, procs, opts)
|
43
|
+
procs.reverse.each &:call
|
44
|
+
else
|
45
|
+
generate_resources_routes(res,opts)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource(res, opts={})
|
50
|
+
opts[:prefix] ||= ""
|
51
|
+
if block_given?
|
52
|
+
procs = []
|
53
|
+
yield Resource.new(res, procs, opts)
|
54
|
+
procs.reverse.each &:call
|
55
|
+
else
|
56
|
+
generate_singleton_routes(res,opts)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def default_routes
|
61
|
+
@@matcher.default_routes
|
62
|
+
end
|
63
|
+
|
64
|
+
def compiled_statement
|
65
|
+
@@matcher.compiled_statement
|
66
|
+
end
|
67
|
+
|
68
|
+
def compiled_regexen
|
69
|
+
@@matcher.compiled_regexen
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_resources_routes(res, opts)
|
73
|
+
[@@matcher,@@generator].each { |r| r.generate_resources_routes(res, opts) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_singleton_routes(res, opts)
|
77
|
+
[@@matcher,@@generator].each { |r| r.generate_singleton_routes(res, opts) }
|
78
|
+
end
|
27
79
|
|
28
|
-
#
|
29
|
-
# as the body of the route_request method.
|
30
|
-
def self.compiled_statement
|
31
|
-
@@compiled_statement
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.compiled_regexen
|
35
|
-
@@compiled_regexen
|
36
|
-
end
|
80
|
+
end # class << self
|
37
81
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
82
|
+
class RouteMatcher
|
83
|
+
|
84
|
+
def initialize
|
85
|
+
@routes = Array.new
|
86
|
+
@compiled_statement = String.new
|
87
|
+
@compiled_regexen = Array.new
|
88
|
+
end
|
89
|
+
|
90
|
+
# the final compiled lambda that gets used
|
91
|
+
# as the body of the route_request method.
|
92
|
+
def compiled_statement
|
93
|
+
@compiled_statement
|
94
|
+
end
|
95
|
+
|
96
|
+
def compiled_regexen
|
97
|
+
@compiled_regexen
|
98
|
+
end
|
99
|
+
|
100
|
+
# add a route to be compiled
|
101
|
+
def add(*route)
|
102
|
+
@routes << [route[0], (route[1] || {})]
|
103
|
+
end
|
104
|
+
|
105
|
+
# build up a string that defines a lambda
|
106
|
+
# that does a case statement on the PATH_INFO
|
107
|
+
# against each of the compiled routes in turn.
|
108
|
+
# first route that matches wins.
|
109
|
+
def compile_router
|
110
|
+
router_lambda = @routes.inject("lambda{|path| \n sections={}\n case path\n") { |m,r|
|
111
|
+
m << compile(r)
|
112
|
+
} << " else\n return {:controller=>'Noroutefound', :action=>'noroute'}\n end\n}"
|
113
|
+
@compiled_statement = router_lambda
|
114
|
+
meta_def(:route_request, &eval(router_lambda))
|
115
|
+
end
|
116
|
+
|
117
|
+
# compile each individual route into a when /.../
|
118
|
+
# component of the case statement. Takes /:sections
|
119
|
+
# of the route def that start with : and turns them
|
120
|
+
# into placeholders for whatever urls match against
|
121
|
+
# the route in question.
|
122
|
+
def compile(route)
|
123
|
+
raise ArgumentError unless String === route[0]
|
124
|
+
code, count = '', 0
|
125
|
+
while route[0] =~ Router::SECTION_REGEXP
|
126
|
+
route[0] = route[0].dup
|
127
|
+
name = $1
|
128
|
+
(name =~ /(\*+)(\w+)/) ? (flag = true; name = $2) : (flag = false)
|
129
|
+
count += 1
|
130
|
+
if flag
|
131
|
+
route[0].sub!(Router::SECTION_REGEXP, "([^,?]+)")
|
132
|
+
else
|
133
|
+
route[0].sub!(Router::SECTION_REGEXP, "([^\/,?]+)")
|
134
|
+
end
|
135
|
+
code << " sections[:#{name}] = $#{count}\n"
|
136
|
+
end
|
137
|
+
@compiled_regexen << Regexp.new(route[0])
|
138
|
+
index = @compiled_regexen.size - 1
|
139
|
+
condition = " when @compiled_regexen[#{index}] "
|
140
|
+
statement = "#{condition}\n#{code}"
|
141
|
+
statement << " return #{route[1].inspect}.update(sections)\n"
|
142
|
+
statement
|
143
|
+
end
|
144
|
+
|
145
|
+
def generate_resources_routes(res,opt)
|
146
|
+
with_options :controller => res.to_s, :rest => true do |r|
|
147
|
+
r.add "#{opt[:prefix]}/#{res}/:id[;/]edit", :allowed => {:get => 'edit'}
|
148
|
+
r.add "#{opt[:prefix]}/#{res}/new[;/]:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'}
|
149
|
+
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
150
|
+
if mem = opt[:member]
|
151
|
+
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
152
|
+
allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
153
|
+
r.add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed
|
154
|
+
}
|
155
|
+
end
|
156
|
+
if coll = opt[:collection]
|
157
|
+
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
158
|
+
allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
159
|
+
r.add "#{opt[:prefix]}/#{res}[;/]#{action}", :allowed => allowed
|
160
|
+
}
|
161
|
+
end
|
162
|
+
r.add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
163
|
+
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'index', :post => 'create'}
|
164
|
+
r.add "#{opt[:prefix]}/#{res}/:id", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
165
|
+
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'index', :post => 'create'}
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def generate_singleton_routes(res,opt)
|
170
|
+
with_options :controller => res.to_s, :rest => true do |r|
|
171
|
+
r.add "#{opt[:prefix]}/#{res}[;/]edit", :allowed => {:get => 'edit'}
|
172
|
+
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'}
|
173
|
+
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
174
|
+
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def default_routes
|
179
|
+
add "/:controller/:action/:id\\.:format"
|
180
|
+
add "/:controller/:action/:id"
|
181
|
+
add "/:controller/:action\\.:format"
|
182
|
+
add "/:controller/:action"
|
183
|
+
add "/:controller\\.:format", :action => 'index'
|
184
|
+
add "/:controller", :action => 'index'
|
185
|
+
end
|
45
186
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
187
|
+
|
188
|
+
class RouteGenerator
|
189
|
+
|
190
|
+
attr_accessor :paths
|
191
|
+
|
192
|
+
def initialize
|
193
|
+
@paths = {}
|
194
|
+
end
|
195
|
+
|
196
|
+
def add(name, path)
|
197
|
+
name = name.intern unless Symbol === name
|
198
|
+
@paths[name] = path
|
199
|
+
end
|
200
|
+
|
201
|
+
def generate(name, options = {})
|
202
|
+
path = @paths[name]
|
203
|
+
while path =~ Router::SECTION_REGEXP
|
204
|
+
path.sub!(Router::SECTION_REGEXP, options[$~[1].intern].to_s)
|
205
|
+
end
|
206
|
+
if f = options[:format]
|
207
|
+
"#{path}.#{f}"
|
208
|
+
else
|
209
|
+
path
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def generate_singleton_routes(res,opt)
|
214
|
+
add "edit_#{res}", "#{opt[:prefix]}/#{res}/edit"
|
215
|
+
add "new_#{res}","#{opt[:prefix]}/#{res}/new"
|
216
|
+
add res, "#{opt[:prefix]}/#{res}"
|
217
|
+
end
|
218
|
+
|
219
|
+
def generate_resources_routes(res,opt)
|
220
|
+
res_singular = res.to_s.singularize
|
221
|
+
add res, "#{opt[:prefix]}/#{res}"
|
222
|
+
add res_singular, "#{opt[:prefix]}/#{res}/:id"
|
223
|
+
add "new_#{res_singular}", "#{opt[:prefix]}/#{res}/new"
|
224
|
+
add "custom_new_#{res_singular}", "#{opt[:prefix]}/#{res}/new/:action"
|
225
|
+
add "edit_#{res_singular}", "#{opt[:prefix]}/#{res}/:id/edit"
|
226
|
+
if mem = opt[:member]
|
227
|
+
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
228
|
+
add "#{action}_#{res_singular}", "#{opt[:prefix]}/#{res}/:id/#{action}"
|
229
|
+
}
|
230
|
+
end
|
231
|
+
if coll = opt[:collection]
|
232
|
+
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
233
|
+
add "#{action}_#{res_singular}", "#{opt[:prefix]}/#{res}/#{action}"
|
234
|
+
}
|
76
235
|
end
|
77
|
-
|
78
|
-
end
|
79
|
-
@@compiled_regexen << Regexp.new(route[0])
|
80
|
-
index = @@compiled_regexen.size - 1
|
81
|
-
condition = " when @@compiled_regexen[#{index}] "
|
82
|
-
statement = "#{condition}\n#{code}"
|
83
|
-
statement << " return #{route[1].inspect}.update(sections)\n"
|
84
|
-
statement
|
236
|
+
end
|
85
237
|
end
|
86
|
-
|
238
|
+
|
87
239
|
class Resource
|
88
|
-
# TODO : come up with naming convention and generate helper
|
89
|
-
# methods for route generation. Route generation
|
90
|
-
# framework needed but needs to be simple and light
|
91
240
|
|
92
241
|
def initialize(resource, procs=[], opts={})
|
93
242
|
@resource, @procs, @opts = resource, procs, opts
|
94
|
-
@procs << proc
|
243
|
+
@procs << proc do
|
244
|
+
[::Merb::Router.matcher, ::Merb::Router.generator].each {|r| r.generate_resources_routes(@resource, @opts) }
|
245
|
+
end
|
95
246
|
end
|
96
247
|
|
97
248
|
def resources(res, opts={})
|
@@ -101,7 +252,9 @@ module Merb
|
|
101
252
|
if block_given?
|
102
253
|
yield self.class.new(res, @procs, opts)
|
103
254
|
else
|
104
|
-
@procs << proc
|
255
|
+
@procs << proc do
|
256
|
+
[::Merb::Router.matcher, ::Merb::Router.generator].each {|r| r.generate_resources_routes(res, opts) }
|
257
|
+
end
|
105
258
|
end
|
106
259
|
end
|
107
260
|
|
@@ -112,76 +265,11 @@ module Merb
|
|
112
265
|
if block_given?
|
113
266
|
yield self.class.new(res, @procs, opts)
|
114
267
|
else
|
115
|
-
@procs << proc
|
116
|
-
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
# add a resource to be compiled for rest style dispatch
|
121
|
-
def self.resources(res, opts={})
|
122
|
-
opts[:prefix] ||= ""
|
123
|
-
if block_given?
|
124
|
-
procs = []
|
125
|
-
yield Resource.new(res, procs, opts)
|
126
|
-
procs.reverse.each &:call
|
127
|
-
else
|
128
|
-
generate_resources_routes(res,opts)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# add a resource to be compiled for rest style dispatch
|
133
|
-
def self.resource(res, opts={})
|
134
|
-
opts[:prefix] ||= ""
|
135
|
-
if block_given?
|
136
|
-
procs = []
|
137
|
-
yield Resource.new(res, procs, opts)
|
138
|
-
procs.reverse.each &:call
|
139
|
-
else
|
140
|
-
generate_singleton_routes(res,opts)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def self.generate_resources_routes(res,opt)
|
145
|
-
with_options :controller => res.to_s, :rest => true do |r|
|
146
|
-
r.add "#{opt[:prefix]}/#{res}/:id[;/]+edit", :allowed => {:get => 'edit'}
|
147
|
-
r.add "#{opt[:prefix]}/#{res}/new[;/]+:action", :allowed => {:get => 'new', :post => 'new', :put => 'new', :delete => 'new'}
|
148
|
-
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
149
|
-
if mem = opt[:member]
|
150
|
-
mem.keys.sort_by{|x| "#{x}"}.each {|action|
|
151
|
-
allowed = mem[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
152
|
-
r.add "#{opt[:prefix]}/#{res}/:id[;/]+#{action}", :allowed => allowed
|
153
|
-
}
|
268
|
+
@procs << proc do
|
269
|
+
[::Merb::Router.matcher, ::Merb::Router.generator].each { |r| r.generate_singleton_routes(res, opts) }
|
270
|
+
end
|
154
271
|
end
|
155
|
-
if coll = opt[:collection]
|
156
|
-
coll.keys.sort_by{|x| "#{x}"}.each {|action|
|
157
|
-
allowed = coll[action].injecting({}) {|h, verb| h[verb] = "#{action}"}
|
158
|
-
r.add "#{opt[:prefix]}/#{res}[;/]+#{action}", :allowed => allowed
|
159
|
-
}
|
160
|
-
end
|
161
|
-
r.add "#{opt[:prefix]}/#{res}/:id\\.:format", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
162
|
-
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'index', :post => 'create'}
|
163
|
-
r.add "#{opt[:prefix]}/#{res}/:id", :allowed => {:get => 'show', :put => 'update', :delete => 'destroy'}
|
164
|
-
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'index', :post => 'create'}
|
165
272
|
end
|
166
273
|
end
|
167
|
-
|
168
|
-
def self.generate_singleton_routes(res,opt)
|
169
|
-
with_options :controller => res.to_s, :rest => true do |r|
|
170
|
-
r.add "#{opt[:prefix]}/#{res}[;/]+edit", :allowed => {:get => 'edit'}
|
171
|
-
r.add "#{opt[:prefix]}/#{res}\\.:format", :allowed => {:get => 'show'}
|
172
|
-
r.add "#{opt[:prefix]}/#{res}/new" , :allowed => {:get => 'new'}
|
173
|
-
r.add "#{opt[:prefix]}/#{res}/?", :allowed => {:get => 'show', :post => 'create', :put => 'update', :delete => 'destroy'}
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def self.default_routes
|
178
|
-
add "/:controller/:action/:id\\.:format"
|
179
|
-
add "/:controller/:action/:id"
|
180
|
-
add "/:controller/:action\\.:format"
|
181
|
-
add "/:controller/:action"
|
182
|
-
add "/:controller\\.:format", :action => 'index'
|
183
|
-
add "/:controller", :action => 'index'
|
184
|
-
end
|
185
274
|
end
|
186
|
-
|
187
275
|
end
|
data/lib/merb/merb_server.rb
CHANGED
@@ -91,6 +91,10 @@ module Merb
|
|
91
91
|
options[:environment] = env
|
92
92
|
end
|
93
93
|
|
94
|
+
opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]", "Command-line option to run scripts and/or code in the merb app") do |stuff_to_run|
|
95
|
+
options[:runner] = stuff_to_run
|
96
|
+
end
|
97
|
+
|
94
98
|
opts.on("-g", "--generate-app PATH", "Generate a fresh merb app at PATH") do |path|
|
95
99
|
options[:generate] = path || Dir.pwd
|
96
100
|
end
|
@@ -102,8 +106,8 @@ module Merb
|
|
102
106
|
opts.on("-M", "--merb-config FILENAME", "This flag is for explicitly declaring the merb app's config file") do |config|
|
103
107
|
options[:merb_config] = config
|
104
108
|
end
|
105
|
-
|
106
|
-
opts.on("-X", "--mutex on/off", "This flag is for
|
109
|
+
|
110
|
+
opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex|
|
107
111
|
if mutex == 'off'
|
108
112
|
options[:use_mutex] = false
|
109
113
|
else
|
@@ -191,6 +195,15 @@ module Merb
|
|
191
195
|
|
192
196
|
if @@merb_opts[:console]
|
193
197
|
initialize_merb
|
198
|
+
Object.class_eval do
|
199
|
+
def show_routes
|
200
|
+
Merb::Router.generator.paths.each {|p| puts p.inspect}
|
201
|
+
nil
|
202
|
+
end
|
203
|
+
def url(path, o={})
|
204
|
+
Merb::Router.generator.generate(path,o)
|
205
|
+
end
|
206
|
+
end
|
194
207
|
ARGV.clear # Avoid passing args to IRB
|
195
208
|
require 'irb'
|
196
209
|
require 'irb/completion'
|
@@ -203,6 +216,17 @@ module Merb
|
|
203
216
|
IRB.start
|
204
217
|
exit!
|
205
218
|
end
|
219
|
+
|
220
|
+
if @@merb_opts[:runner]
|
221
|
+
initialize_merb
|
222
|
+
code_or_file = @@merb_opts[:runner]
|
223
|
+
if File.exists?(code_or_file)
|
224
|
+
eval(File.read(code_or_file))
|
225
|
+
else
|
226
|
+
eval(code_or_file)
|
227
|
+
end
|
228
|
+
exit!
|
229
|
+
end
|
206
230
|
|
207
231
|
if @@merb_opts[:start_drb]
|
208
232
|
puts "Starting merb drb server on port: #{@@merb_opts[:drb_server_port]}"
|
@@ -220,7 +244,6 @@ module Merb
|
|
220
244
|
delete_pidfiles(@@merb_opts[:port])
|
221
245
|
start(@@merb_opts[:port])
|
222
246
|
else
|
223
|
-
initialize_merb
|
224
247
|
trap('TERM') { exit }
|
225
248
|
mongrel_start(@@merb_opts[:port])
|
226
249
|
end
|
@@ -3,9 +3,22 @@ require File.dirname(__FILE__)+'/mixins/view_context_mixin'
|
|
3
3
|
require File.dirname(__FILE__)+'/mixins/form_control_mixin'
|
4
4
|
|
5
5
|
module Merb
|
6
|
-
PROTECTED_IVARS = %w[@
|
7
|
-
|
8
|
-
|
6
|
+
PROTECTED_IVARS = %w[@_new_cookie
|
7
|
+
@method
|
8
|
+
@env
|
9
|
+
@body
|
10
|
+
@_fingerprint_before
|
11
|
+
@pager
|
12
|
+
@session
|
13
|
+
@headers
|
14
|
+
@page
|
15
|
+
@cookies
|
16
|
+
@request
|
17
|
+
@status
|
18
|
+
@_view_context_cache
|
19
|
+
@response
|
20
|
+
@params]
|
21
|
+
|
9
22
|
module GlobalHelper
|
10
23
|
end
|
11
24
|
# the ViewContext is really
|
@@ -19,6 +32,7 @@ module Merb
|
|
19
32
|
include Merb::GlobalHelper
|
20
33
|
|
21
34
|
def initialize(controller)
|
35
|
+
@_merb_partial_locals = {}
|
22
36
|
@controller = controller
|
23
37
|
(@controller.instance_variables - PROTECTED_IVARS).each do |ivar|
|
24
38
|
self.instance_variable_set(ivar, @controller.instance_variable_get(ivar))
|
@@ -43,7 +57,7 @@ module Merb
|
|
43
57
|
alias_method :old_respond_to?, :respond_to?
|
44
58
|
|
45
59
|
def respond_to?(sym, include_private=false)
|
46
|
-
old_respond_to?(sym, include_private) || @controller.respond_to?(sym, include_private)
|
60
|
+
old_respond_to?(sym, include_private) || @controller.respond_to?(sym, include_private) || @_merb_partial_locals.key?(sym)
|
47
61
|
end
|
48
62
|
|
49
63
|
# catch any method calls that the controller responds to
|
@@ -51,6 +65,8 @@ module Merb
|
|
51
65
|
def method_missing(sym, *args, &blk)
|
52
66
|
if @controller.respond_to? sym
|
53
67
|
@controller.send(sym, *args, &blk)
|
68
|
+
elsif @_merb_partial_locals.key? sym
|
69
|
+
@_merb_partial_locals[sym]
|
54
70
|
else
|
55
71
|
super
|
56
72
|
end
|
@@ -18,39 +18,46 @@ module Merb
|
|
18
18
|
status = input.read(boundary_size)
|
19
19
|
raise EOFError, "bad content body" unless status == boundary + EOL
|
20
20
|
rx = /(?:#{EOL})?#{Regexp.quote(boundary,'n')}(#{EOL}|--)/
|
21
|
-
|
22
21
|
loop {
|
23
22
|
head = nil
|
24
23
|
body = ''
|
25
24
|
filename = content_type = name = nil
|
26
|
-
|
25
|
+
read_size = 0
|
27
26
|
until head && buf =~ rx
|
28
|
-
|
27
|
+
i = buf.index("\r\n\r\n")
|
28
|
+
if( i == nil && read_size == 0 && content_length == 0 )
|
29
|
+
content_length = -1
|
30
|
+
break
|
31
|
+
end
|
32
|
+
if !head && i
|
29
33
|
head = buf.slice!(0, i+2) # First \r\n
|
30
34
|
buf.slice!(0, 2) # Second \r\n
|
31
|
-
|
32
35
|
filename = head[FILENAME_REGEX, 1]
|
33
36
|
content_type = head[CONTENT_TYPE_REGEX, 1]
|
34
37
|
name = head[NAME_REGEX, 1]
|
35
38
|
|
36
|
-
if filename && !filename.empty?
|
39
|
+
if filename && !filename.empty?
|
37
40
|
body = Tempfile.new(:Merb)
|
38
41
|
body.binmode if defined? body.binmode
|
39
42
|
end
|
40
43
|
next
|
41
44
|
end
|
42
|
-
|
45
|
+
|
46
|
+
|
43
47
|
# Save the read body part.
|
44
48
|
if head && (boundary_size+4 < buf.size)
|
45
49
|
body << buf.slice!(0, buf.size - (boundary_size+4))
|
46
50
|
end
|
47
51
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
+
read_size = bufsize < content_length ? bufsize : content_length
|
53
|
+
if( read_size > 0 )
|
54
|
+
c = input.read(read_size)
|
55
|
+
raise EOFError, "bad content body" if c.nil? || c.empty?
|
56
|
+
buf << c
|
57
|
+
content_length -= c.size
|
58
|
+
end
|
52
59
|
end
|
53
|
-
|
60
|
+
|
54
61
|
# Save the rest.
|
55
62
|
if i = buf.index(rx)
|
56
63
|
body << buf.slice!(0, i)
|
@@ -58,7 +65,7 @@ module Merb
|
|
58
65
|
|
59
66
|
content_length = -1 if $1 == "--"
|
60
67
|
end
|
61
|
-
|
68
|
+
|
62
69
|
if filename && !filename.empty?
|
63
70
|
body.rewind
|
64
71
|
data = {
|
@@ -92,6 +99,10 @@ module Merb
|
|
92
99
|
parms
|
93
100
|
end
|
94
101
|
|
102
|
+
def url(path, o={})
|
103
|
+
::Merb::Router.generator.generate(path,o)
|
104
|
+
end
|
105
|
+
|
95
106
|
# parses a query string or the payload of a POST
|
96
107
|
# request into the params hash. So for example:
|
97
108
|
# /foo?bar=nik&post[title]=heya&post[body]=whatever
|
@@ -246,4 +257,4 @@ module Merb
|
|
246
257
|
end
|
247
258
|
|
248
259
|
end
|
249
|
-
end
|
260
|
+
end
|
@@ -27,6 +27,9 @@ module Merb
|
|
27
27
|
# render :nothing => 200
|
28
28
|
# renders nothing with a status of 200
|
29
29
|
#
|
30
|
+
# render :template => 'shared/message'
|
31
|
+
# renders views/shared/message
|
32
|
+
#
|
30
33
|
# render :js => "$('some-div').toggle();"
|
31
34
|
# if the right hand side of :js => is a string then the proper
|
32
35
|
# javascript headers will be set and the string will be returned
|
@@ -57,6 +60,13 @@ module Merb
|
|
57
60
|
when partial = opts[:partial]
|
58
61
|
template = find_partial(partial, opts)
|
59
62
|
opts[:layout] = :none
|
63
|
+
|
64
|
+
# Add an instance variable that can be used to create the locals in the
|
65
|
+
# partial
|
66
|
+
if opts[:locals]
|
67
|
+
@_merb_partial_locals = opts[:locals]
|
68
|
+
end
|
69
|
+
opts[:clean_context] = true
|
60
70
|
when js = opts[:js]
|
61
71
|
headers['Content-Type'] = "text/javascript"
|
62
72
|
opts[:layout] = :none
|
@@ -71,14 +81,16 @@ module Merb
|
|
71
81
|
headers['Content-Type'] = 'application/xml'
|
72
82
|
headers['Encoding'] = 'UTF-8'
|
73
83
|
return xml
|
74
|
-
|
84
|
+
when template = opts[:template]
|
85
|
+
template = find_template(:template => template)
|
86
|
+
else
|
75
87
|
template = find_template(:action => action)
|
76
88
|
end
|
77
89
|
|
78
90
|
engine = engine_for(template)
|
79
91
|
options = {
|
80
92
|
:file => template,
|
81
|
-
:view_context => (opts[:clean_context] ? clean_view_context :
|
93
|
+
:view_context => (opts[:clean_context] ? clean_view_context : cached_view_context),
|
82
94
|
:opts => opts
|
83
95
|
}
|
84
96
|
content = engine.transform(options)
|
@@ -92,7 +104,7 @@ module Merb
|
|
92
104
|
# this returns a ViewContext object populated with all
|
93
105
|
# the instance variables in your controller. This is used
|
94
106
|
# as the view context object for the Erubis templates.
|
95
|
-
def
|
107
|
+
def cached_view_context
|
96
108
|
@_view_context_cache ||= ViewContext.new(self)
|
97
109
|
end
|
98
110
|
|
@@ -129,8 +141,8 @@ module Merb
|
|
129
141
|
# you can render partials in other view directories. So
|
130
142
|
# if you create a views/shared directory then you can call
|
131
143
|
# partials that live there like partial('shared/foo')
|
132
|
-
def partial(template)
|
133
|
-
render :partial => template
|
144
|
+
def partial(template, locals={})
|
145
|
+
render :partial => template, :locals => locals
|
134
146
|
end
|
135
147
|
|
136
148
|
private
|
@@ -146,11 +158,11 @@ module Merb
|
|
146
158
|
end
|
147
159
|
end
|
148
160
|
|
149
|
-
|
161
|
+
cached_view_context.instance_variable_set('@_layout_content', content)
|
150
162
|
engine = engine_for(layout_choice)
|
151
163
|
options = {
|
152
164
|
:file => layout_choice,
|
153
|
-
:view_context =>
|
165
|
+
:view_context => cached_view_context,
|
154
166
|
:opts => opts
|
155
167
|
}
|
156
168
|
engine.transform(options)
|
@@ -158,14 +170,15 @@ module Merb
|
|
158
170
|
|
159
171
|
# OPTIMIZE : combine find_template and find_partial ?
|
160
172
|
def find_template(opts={})
|
161
|
-
if
|
162
|
-
path =
|
163
|
-
|
173
|
+
if template = opts[:template]
|
174
|
+
path = _template_root / template
|
175
|
+
elsif action = opts[:action]
|
176
|
+
path = _template_root / self.class.name.snake_case / action
|
164
177
|
elsif _layout = opts[:layout]
|
165
178
|
path = _layout_root / _layout
|
166
179
|
else
|
167
180
|
raise "called find_template without an :action or :layout"
|
168
|
-
end
|
181
|
+
end
|
169
182
|
extensions = [_template_extensions.keys].flatten.uniq
|
170
183
|
glob = "#{path}.{#{opts[:ext] || extensions.join(',')}}"
|
171
184
|
Dir[glob].first
|
@@ -32,7 +32,7 @@ module Merb
|
|
32
32
|
%{<img src="#{opts.delete(:path) + img}" #{opts.map{|k,v| "#{k}=\"#{v}\""}.join(' ')} />}
|
33
33
|
end
|
34
34
|
|
35
|
-
# calls .to_json on data.
|
35
|
+
# calls .to_json on data.
|
36
36
|
# so it can be faster than escape_js
|
37
37
|
def js(data)
|
38
38
|
if data.respond_to? :to_json
|
data/lib/merb/template/haml.rb
CHANGED
@@ -27,9 +27,6 @@ module Merb
|
|
27
27
|
|
28
28
|
class << self
|
29
29
|
|
30
|
-
class_inheritable_accessor :haml_options
|
31
|
-
self.haml_options = { :locals => {} }
|
32
|
-
|
33
30
|
@@hamls ||= {}
|
34
31
|
@@mtimes ||= {}
|
35
32
|
|
@@ -39,16 +36,14 @@ module Merb
|
|
39
36
|
|
40
37
|
def transform(options = {})
|
41
38
|
opts, file, view_context = options.values_at(:opts, :file, :view_context)
|
42
|
-
|
39
|
+
|
43
40
|
begin
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
|
41
|
+
locals_code = build_locals(opts[:locals])
|
42
|
+
opts[:locals] = {}
|
43
|
+
|
44
|
+
template = load_template(file)
|
45
|
+
|
46
|
+
haml = ::Haml::Engine.new("#{locals_code}#{template}", opts)
|
52
47
|
haml.to_html(view_context)
|
53
48
|
rescue
|
54
49
|
# ::Haml::Engine often inserts a bogus "(haml):#{line_number}" entry in the backtrace.
|
@@ -59,26 +54,37 @@ module Merb
|
|
59
54
|
end
|
60
55
|
|
61
56
|
private
|
57
|
+
def load_template(file)
|
58
|
+
template = ""
|
59
|
+
if @@hamls[file] && !cache_template?(file)
|
60
|
+
template = @@hamls[file]
|
61
|
+
else
|
62
|
+
template = IO.read(file)
|
63
|
+
if cache_template?(file)
|
64
|
+
@@hamls[file], @@mtimes[file] = template, Time.now
|
65
|
+
end
|
66
|
+
return template
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_locals(locals)
|
71
|
+
locals_code = ""
|
72
|
+
if locals
|
73
|
+
locals_code = locals.keys.inject("") do |code, key|
|
74
|
+
code << "- #{key} = @_merb_partial_locals[:#{key}]\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
62
78
|
|
63
|
-
|
64
|
-
|
65
|
-
return @@hamls[path]
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def set_precompiled(path, precompiled)
|
70
|
-
@@hamls[path], @@mtimes[path] = precompiled, Time.now
|
71
|
-
end
|
72
|
-
|
73
|
-
def cache_template?(path)
|
74
|
-
return false unless ::Merb::Server.config[:cache_templates]
|
75
|
-
return true unless @@hamls[path]
|
76
|
-
@@mtimes[path] < File.mtime(path) ||
|
77
|
-
(File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
|
78
|
-
end
|
79
|
+
def cache_template?(path)
|
80
|
+
return false unless ::Merb::Server.config[:cache_templates]
|
81
|
+
return true unless @@hamls[path]
|
82
|
+
@@mtimes[path] < File.mtime(path) ||
|
83
|
+
(File.symlink?(path) && (@@mtimes[path] < File.lstat(path).mtime))
|
84
|
+
end
|
79
85
|
|
80
86
|
end
|
81
87
|
|
82
88
|
end
|
83
89
|
end
|
84
|
-
end
|
90
|
+
end
|
@@ -33,8 +33,15 @@ module Merb
|
|
33
33
|
# mab is just ruby, there's no two phase compile and run
|
34
34
|
def transform(options = {})
|
35
35
|
opts, file, view_context = options.values_at(:opts, :file, :view_context)
|
36
|
+
|
37
|
+
if opts[:locals]
|
38
|
+
locals = opts[:locals].keys.inject("") do |code, key|
|
39
|
+
code << "#{key} = @_merb_partial_locals[:#{key}]\n"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
36
43
|
mab = ::Markaby::Builder.new({}, view_context) {
|
37
|
-
instance_eval(File.read(file))
|
44
|
+
instance_eval("#{locals}#{File.read(file)}")
|
38
45
|
}
|
39
46
|
mab.to_s
|
40
47
|
end
|
data/lib/tasks/merb.rake
CHANGED
@@ -21,4 +21,12 @@ namespace :merb do
|
|
21
21
|
puts " - #{MERB_ROOT / 'dist/framework'} (recursive) "
|
22
22
|
puts " - #{MERB_ROOT / 'script/merb'}"
|
23
23
|
end
|
24
|
+
|
25
|
+
desc "freeze the merb framework from svn"
|
26
|
+
task :freeze_from_svn do
|
27
|
+
puts "Freezing Merb Framework from svn"
|
28
|
+
FileUtils.rm_rf MERB_ROOT / 'dist/framework'
|
29
|
+
system "svn co http://svn.devjavu.com/merb/trunk/lib #{MERB_ROOT / 'dist/framework'}"
|
30
|
+
end
|
31
|
+
|
24
32
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: merb
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.3.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.3.3
|
7
|
+
date: 2007-05-31 00:00:00 -07:00
|
8
8
|
summary: Merb == Mongrel + Erb. Pocket rocket web framework.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -175,3 +175,30 @@ dependencies:
|
|
175
175
|
- !ruby/object:Gem::Version
|
176
176
|
version: 0.0.0
|
177
177
|
version:
|
178
|
+
- !ruby/object:Gem::Dependency
|
179
|
+
name: mime-types
|
180
|
+
version_requirement:
|
181
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
182
|
+
requirements:
|
183
|
+
- - ">"
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: 0.0.0
|
186
|
+
version:
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: xml-simple
|
189
|
+
version_requirement:
|
190
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 0.0.0
|
195
|
+
version:
|
196
|
+
- !ruby/object:Gem::Dependency
|
197
|
+
name: archive-tar-minitar
|
198
|
+
version_requirement:
|
199
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
200
|
+
requirements:
|
201
|
+
- - ">"
|
202
|
+
- !ruby/object:Gem::Version
|
203
|
+
version: 0.0.0
|
204
|
+
version:
|