merb 0.3.1 → 0.3.3

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.
data/README CHANGED
@@ -8,7 +8,7 @@ Lightweight MVC Ruby app server. For high performance dynamic pages.
8
8
  ** Dependencies **
9
9
  mongrel
10
10
  erubis
11
- json or fjson
11
+ json
12
12
  mime-types
13
13
  archive-tar-minitar
14
14
  rspec
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ include FileUtils
15
15
 
16
16
 
17
17
  NAME = "merb"
18
- VERS = "0.3.1"
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
- * Irb Console **DONE**
3
- * Migrations **DONE**
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
+ *
Binary file
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
  puts "Compiling routes.."
13
- Merb::RouteMatcher.prepare do |r|
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.1' unless defined?VERSION
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 = 'HTTP_COOKIE'.freeze
82
- QUERY_STRING = 'QUERY_STRING'.freeze
83
- APPLICATION_JSON = 'application/json'.freeze
84
- TEXT_JSON = 'text/x-json'.freeze
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
@@ -10,5 +10,4 @@ corelib = __DIR__+'/merb/core_ext'
10
10
  merb_hash
11
11
  merb_numeric
12
12
  merb_symbol
13
-
14
13
  ].each {|fn| require File.join(corelib, fn)}
@@ -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(/&lt;/, "<").
49
+ gsub(/&gt;/, ">").
50
+ gsub(/&quot;/, '"').
51
+ gsub(/&apos;/, "'").
52
+ gsub(/&amp;/, "&")
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
 
@@ -1,10 +1,4 @@
1
- class Symbol
2
-
3
- # faster Symbol#to_s to speed up routing.
4
- def to_s
5
- @str_rep ||= id2name.freeze
6
- end
7
-
1
+ class Symbol
8
2
  # ["foo", "bar"].map &:reverse #=> ['oof', 'rab']
9
3
  def to_proc
10
4
  Proc.new{|*args| args.shift.__send__(self, *args)}
@@ -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(MERB_ROOT / "dist/app/views")
25
- self._layout_root = File.expand_path(MERB_ROOT / "dist/app/views/layout")
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
- case filter
182
- when Symbol, String
183
- send(filter) if ok
184
- when Proc
185
- filter.call(self) if ok
186
- end
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
@@ -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::RouteMatcher.route_request(path)
52
+ Merb::Router.match(path)
53
53
  end
54
54
 
55
55
  # take a controller class name string and reload or require
@@ -45,11 +45,7 @@ module Merb
45
45
  end
46
46
  end
47
47
  =begin
48
- m = Merb::Mailer.new :to => 'foo@bar.com',
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
@@ -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
- eval %{
152
- def #{m}?; method == :#{m}; end
153
- }
157
+ define_method("#{m}?") { method == m.to_sym }
154
158
  end
155
159
 
156
160
  end
@@ -1,97 +1,248 @@
1
1
  module Merb
2
2
 
3
- # Merb::RouteMatcher is the request routing mapper for the merb framework.
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
- attr_accessor :sections
16
- @@section_regexp = /(?::([a-z*_]+))/.freeze
5
+ SECTION_REGEXP = /(?::([a-z*_]+))/.freeze
17
6
 
18
- # setup the router and yield it out to
19
- # add routes to it. Then compile all routes
20
- def self.prepare
21
- @@routes = Array.new
22
- @@compiled_statement = String.new
23
- @@compiled_regexen = Array.new
24
- yield self
25
- compile_router
26
- end
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
- # the final compiled lambda that gets used
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
- def compiled_statement
39
- @@compiled_statement
40
- end
41
-
42
- # add a route to be compiled
43
- def self.add(*route)
44
- @@routes << [route[0], (route[1] || {})]
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
- # build up a string that defines a lambda
48
- # that does a case statement on the PATH_INFO
49
- # against each of the compiled routes in turn.
50
- # first route that matches wins.
51
- def self.compile_router
52
- router_lambda = @@routes.inject("lambda{|path| \n sections={}\n case path\n") { |m,r|
53
- m << compile(r)
54
- } <<" else\n return {:controller=>'Noroutefound', :action=>'noroute'}\n end\n}"
55
- @@compiled_statement = router_lambda
56
- meta_def(:route_request, &eval(router_lambda))
57
- end
58
-
59
- # compile each individual route into a when /.../
60
- # component of the case statement. Takes /:sections
61
- # of the route def that start with : and turns them
62
- # into placeholders for whatever urls match against
63
- # the route in question.
64
- def self.compile(route)
65
- raise ArgumentError unless String === route[0]
66
- code, count = '', 0
67
- while route[0] =~ @@section_regexp
68
- route[0] = route[0].dup
69
- name = $1
70
- (name =~ /(\*+)(\w+)/) ? (flag = true; name = $2) : (flag = false)
71
- count += 1
72
- if flag
73
- route[0].sub!(@@section_regexp, "([^,?]+)")
74
- else
75
- route[0].sub!(@@section_regexp, "([^\/,?]+)")
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
- code << " sections[:#{name}] = $#{count}\n"
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 { ::Merb::RouteMatcher.generate_resources_routes(@resource, @opts) }
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 { ::Merb::RouteMatcher.generate_resources_routes(res, opts) }
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 { ::Merb::RouteMatcher.generate_singleton_routes(res, opts) }
116
- end
117
- end
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
@@ -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 turnhing on and off the mutex lock.") do |mutex|
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[@cookies @session @headers @params
7
- @env @in @status @root @method @request @fingerprint_before
8
- @_new_cookie @tmpl_ext_cache @body]
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
- if !head && i = buf.index("\r\n\r\n")
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
- c = input.read(bufsize < content_length ? bufsize : content_length)
49
- raise EOFError, "bad content body" if c.nil? || c.empty?
50
- buf << c
51
- content_length -= c.size
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
@@ -41,6 +41,11 @@ module Merb
41
41
  text(o)
42
42
  end
43
43
 
44
+ def hidden(o)
45
+ o.value ||= ""
46
+ %{<input type="hidden" name="#{o.name}" value="#{o.value}" #{o.html ? o.html.map{|k,v| "#{k}=\"#{v}\""}.join(' ') : nil}/>}
47
+ end
48
+
44
49
  def text(o)
45
50
  o.value ||= ""
46
51
  tag = ''
@@ -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
- else
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 : _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 _view_context
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
- _view_context.instance_variable_set('@_layout_content', content)
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 => _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 action = opts[:action]
162
- path =
163
- File.expand_path(MERB_ROOT / "dist/app/views" / self.class.name.snake_case / action)
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. This will use fjson if installed
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
@@ -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
- opts = haml_options.merge(opts)
39
+
43
40
  begin
44
- if precompiled = get_precompiled(file)
45
- opts[:precompiled] ||= precompiled
46
- haml = ::Haml::Engine.new("", opts)
47
- else
48
- haml = ::Haml::Engine.new(IO.read(file), opts)
49
- set_precompiled(file, haml.precompiled)
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
- def get_precompiled(path, opts={})
64
- if @@hamls[path] && !cache_template?(path)
65
- return @@hamls[path]
66
- end
67
- end
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.1
7
- date: 2007-04-30 00:00:00 -07:00
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: