yapra 0.1.2 → 0.1.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.
@@ -1,3 +1,11 @@
1
+ == 0.1.3
2
+
3
+ * 1 bug fix:
4
+
5
+ * 2 majar enhancement:
6
+ * Publish::Mail plugin is added.
7
+ * Feed::Custom supports url list.
8
+
1
9
  == 0.1.2
2
10
 
3
11
  * 1 bug fix:
data/LICENCE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 fraction.jp. Some rights reserved.
1
+ Copyright (c) 2008 Yuanying Ohtsuka
2
2
 
3
3
  Original from 2008-06-12 http://pragger.ikejisoft.com/
4
4
 
data/README.mdown ADDED
@@ -0,0 +1,70 @@
1
+ yapra
2
+ ====================================
3
+ - http://yapra.rubyforge.org/
4
+ - http://github.com/yuanying/yapra/tree/master
5
+
6
+ DESCRIPTION:
7
+ ------------------------------------
8
+ Yet Another Pragger(http://pragger.ikejisoft.com/) implementation.
9
+
10
+ FEATURES/PROBLEMS:
11
+ ------------------------------------
12
+ - 99% compatible of Pragger.
13
+ - Class-based plugin support.
14
+ - Loadpass based plugin loading strategy. (also support pragger's plugin in advance mode.)
15
+ - Support Python habu like config file.
16
+
17
+ SYNOPSIS:
18
+ ------------------------------------
19
+
20
+ ### Use at command
21
+
22
+ $ yapra -c config_file.yml
23
+
24
+ ### Use in your application
25
+
26
+ require 'yapra/runtime'
27
+ require 'yapra/config'
28
+
29
+ config = YAML.load(config_file)
30
+ config = Yapra::Config.new(config)
31
+
32
+ Yapra::Runtime.logger = Logger.new(STDOUT)
33
+
34
+ yapra = Yapra::Runtime.new(config.env)
35
+ yapra.execute(config.pipeline_commands)
36
+
37
+ ### REQUIREMENTS:
38
+
39
+ - mechanize (>= 0.7.6)
40
+
41
+ INSTALL:
42
+ ------------------------------------
43
+
44
+ sudo gem install yapra
45
+
46
+ LICENSE:
47
+ ------------------------------------
48
+
49
+ (The MIT License)
50
+
51
+ Copyright (c) 2008 Yuanying Ohtsuka
52
+
53
+ Permission is hereby granted, free of charge, to any person obtaining
54
+ a copy of this software and associated documentation files (the
55
+ 'Software'), to deal in the Software without restriction, including
56
+ without limitation the rights to use, copy, modify, merge, publish,
57
+ distribute, sublicense, and/or sell copies of the Software, and to
58
+ permit persons to whom the Software is furnished to do so, subject to
59
+ the following conditions:
60
+
61
+ The above copyright notice and this permission notice shall be
62
+ included in all copies or substantial portions of the Software.
63
+
64
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
65
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
66
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
67
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
68
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
69
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
70
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,13 +1,119 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
3
-
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
5
-
6
- ##############################################################################
7
- # SVN
8
- ##############################################################################
9
-
10
- desc "Add new files to subversion"
11
- task :svn_add do
12
- system "svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add"
13
- end
1
+ $:.unshift('lib')
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/packagetask'
7
+ require 'rake/gempackagetask'
8
+ require 'rake/rdoctask'
9
+ require 'rake/contrib/rubyforgepublisher'
10
+ require 'rake/contrib/sshpublisher'
11
+ require 'fileutils'
12
+ require 'yapra/version'
13
+ include FileUtils
14
+ NAME = "yapra"
15
+ AUTHOR = "yuanying"
16
+ EMAIL = "yuanying at fraction dot jp"
17
+ DESCRIPTION = "Yet another pragger implementation."
18
+ RUBYFORGE_PROJECT = "yapra"
19
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
20
+ BIN_FILES = %w( yapra )
21
+ VERS = Yapra::VERSION::STRING
22
+
23
+ REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
24
+ CLEAN.include ['**/.*.sw?', '*.gem', '.config' ,'pkg']
25
+ RDOC_OPTS = [
26
+ '--title', "#{NAME} documentation",
27
+ "--charset", "utf-8",
28
+ "--opname", "index.html",
29
+ "--line-numbers",
30
+ "--main", "README.mdown",
31
+ "--inline-source",
32
+ ]
33
+
34
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
35
+
36
+ task :default => [:spec]
37
+ task :package => [:clean]
38
+
39
+ spec = Gem::Specification.new do |s|
40
+ s.name = NAME
41
+ s.version = VERS
42
+ s.platform = Gem::Platform::RUBY
43
+ s.has_rdoc = true
44
+ s.extra_rdoc_files = ["README.mdown", "ChangeLog"]
45
+ s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
46
+ s.summary = DESCRIPTION
47
+ s.description = DESCRIPTION
48
+ s.author = AUTHOR
49
+ s.email = EMAIL
50
+ s.homepage = HOMEPATH
51
+ s.executables = BIN_FILES
52
+ s.rubyforge_project = RUBYFORGE_PROJECT
53
+ s.bindir = "bin"
54
+ s.require_paths << "lib-plugins"
55
+ s.autorequire = ""
56
+ s.test_files = Dir["test/test_*.rb"]
57
+
58
+ s.add_dependency('mechanize', '>=0.7.6')
59
+ #s.required_ruby_version = '>= 1.8.2'
60
+
61
+ s.files = %w(README.mdown ChangeLog Rakefile LICENCE) +
62
+ Dir.glob("{bin,doc,fixtures,legacy_plugins,lib,lib-plugins,plugins,spec,tasks,website,script}/**/*") +
63
+ # Dir.glob("ext/**/*.{h,c,rb}") +
64
+ # Dir.glob("examples/**/*.rb") +
65
+ # Dir.glob("tools/*.rb")
66
+
67
+ s.extensions = FileList["ext/**/extconf.rb"].to_a
68
+ end
69
+
70
+ Rake::GemPackageTask.new(spec) do |p|
71
+ p.need_tar = true
72
+ p.gem_spec = spec
73
+ end
74
+
75
+ task :install do
76
+ name = "#{NAME}-#{VERS}.gem"
77
+ sh %{rake package}
78
+ sh %{sudo gem install pkg/#{name}}
79
+ end
80
+
81
+ task :uninstall => [:clean] do
82
+ sh %{sudo gem uninstall #{NAME}}
83
+ end
84
+
85
+
86
+ Rake::RDocTask.new do |rdoc|
87
+ rdoc.rdoc_dir = 'html'
88
+ rdoc.options += RDOC_OPTS
89
+ rdoc.template = "resh"
90
+ #rdoc.template = "#{ENV['template']}.rb" if ENV['template']
91
+ if ENV['DOC_FILES']
92
+ rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
93
+ else
94
+ rdoc.rdoc_files.include('README', 'ChangeLog')
95
+ rdoc.rdoc_files.include('lib/**/*.rb')
96
+ rdoc.rdoc_files.include('ext/**/*.c')
97
+ end
98
+ end
99
+
100
+ desc "Publish to RubyForge"
101
+ task :rubyforge => [:rdoc, :package] do
102
+ require 'rubyforge'
103
+ Rake::RubyForgePublisher.new(RUBYFORGE_PROJECT, 'yuanying').upload
104
+ end
105
+
106
+ desc 'Package and upload the release to gemcutter.'
107
+ task :release => [:clean, :package] do |t|
108
+ v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
109
+ abort "Versions don't match #{v} vs #{VERS}" unless v == VERS
110
+ pkg = "pkg/#{NAME}-#{VERS}"
111
+
112
+ files = [
113
+ "#{pkg}.tgz",
114
+ "#{pkg}.gem"
115
+ ].compact
116
+
117
+ puts "Releasing #{NAME} v. #{VERS}"
118
+ sh %{gem push #{pkg}.gem}
119
+ end
data/bin/yapra CHANGED
@@ -23,11 +23,13 @@ require 'yapra/runtime'
23
23
  require 'yapra/config'
24
24
  require 'yapra/legacy_plugin/registry_factory'
25
25
 
26
- Version = Yapra::VERSION::STRING
27
26
  mode = 'compatible'
28
27
  config_files = []#"config.yaml"
29
28
  loglebel = nil
29
+
30
30
  opt = OptionParser.new
31
+ opt.version = Yapra::VERSION::STRING
32
+ opt.release = nil
31
33
  opt.on("-c", "--configfile CONFIGFILE") {|v| config_files << v }
32
34
  opt.on("-d", "--configfile-directory CONFIGFILE_DIRECTORY") { |v|
33
35
  Dir.glob(File.join(v, '**/*.yml')).each do |file|
@@ -1,31 +1,14 @@
1
1
  require 'yapra'
2
+ require 'yapra/pipeline_base'
2
3
  require 'yapra/runtime'
3
4
  require 'yapra/inflector'
4
5
  require 'yapra/legacy_plugin/base'
5
6
 
6
- class Yapra::Pipeline
7
- attr_reader :yapra, :context
8
- attr_writer :logger
7
+ class Yapra::Pipeline < Yapra::PipelineBase
9
8
  attr_accessor :legacy_plugin_registry
10
9
 
11
10
  UPPER_CASE = /[A-Z]/
12
11
 
13
- def initialize pipeline_name, yapra=Yapra::Runtime.new
14
- @logger = nil
15
- @yapra = yapra
16
- @context = { 'pipeline_name' => pipeline_name }
17
-
18
- @module_name_prefix = construct_module_name_prefix yapra.env
19
- end
20
-
21
- def name
22
- self.context[ 'pipeline_name' ]
23
- end
24
-
25
- def logger
26
- return @logger || Yapra::Runtime.logger
27
- end
28
-
29
12
  # start pipeline from commands.
30
13
  #
31
14
  # example:
@@ -87,20 +70,11 @@ class Yapra::Pipeline
87
70
 
88
71
  def run_class_based_plugin command, data
89
72
  self.logger.debug("evaluate plugin as class based")
90
- load_error_stack = []
91
- plugin_class = nil
92
- @module_name_prefix.each do |prefix|
93
- yapra_module_name = "#{prefix}#{command['module']}"
94
- begin
95
- plugin_class = Yapra.load_class_constant(yapra_module_name)
96
- break if plugin_class
97
- rescue LoadError, NameError => ex
98
- load_error_stack << ex
99
- end
100
- end
101
- raise_load_error(load_error_stack, command) unless plugin_class
73
+ plugin = load(command['module'])
74
+
75
+ # yml pipeline specific.
76
+ plugin.plugin_config = command['config'] if plugin.respond_to?('plugin_config=')
102
77
 
103
- plugin = initialize_plugin(plugin_class, command)
104
78
  @plugins << plugin
105
79
  data = plugin.run(data)
106
80
  return data
@@ -116,24 +90,4 @@ class Yapra::Pipeline
116
90
  load_error.set_backtrace(backtrace)
117
91
  raise load_error
118
92
  end
119
-
120
- def initialize_plugin plugin_class, command
121
- plugin = plugin_class.new
122
- plugin.yapra = yapra if plugin.respond_to?('yapra=')
123
- plugin.pipeline = self if plugin.respond_to?('pipeline=')
124
- plugin.plugin_config = command['config'] if plugin.respond_to?('plugin_config=')
125
- plugin
126
- end
127
-
128
- def construct_module_name_prefix env
129
- module_name_prefix = [ 'Yapra::Plugin::', '' ]
130
- if env['module_name_prefix']
131
- if env['module_name_prefix'].kind_of?(Array)
132
- module_name_prefix = env['module_name_prefix']
133
- else
134
- module_name_prefix = [ env['module_name_prefix'] ]
135
- end
136
- end
137
- module_name_prefix
138
- end
139
93
  end
@@ -0,0 +1,64 @@
1
+ require 'yapra'
2
+ require 'yapra/inflector'
3
+ require 'yapra/legacy_plugin/base'
4
+
5
+ class Yapra::PipelineBase
6
+ attr_reader :yapra, :context
7
+ attr_writer :logger
8
+
9
+ def initialize pipeline_name, yapra=Yapra::Runtime.new
10
+ @logger = nil
11
+ @yapra = yapra
12
+ @context = { 'pipeline_name' => pipeline_name }
13
+
14
+ @module_name_prefix = construct_module_name_prefix yapra.env
15
+ end
16
+
17
+ def name
18
+ self.context[ 'pipeline_name' ]
19
+ end
20
+
21
+ def logger
22
+ return @logger || Yapra::Runtime.logger
23
+ end
24
+
25
+ def load plugin_name
26
+ load_error_stack = []
27
+ plugin_class = nil
28
+ @module_name_prefix.each do |prefix|
29
+ yapra_module_name = "#{prefix}#{plugin_name}"
30
+ begin
31
+ plugin_class = Yapra.load_class_constant(yapra_module_name)
32
+ break if plugin_class
33
+ rescue LoadError, NameError => ex
34
+ load_error_stack << ex
35
+ end
36
+ end
37
+ raise_load_error(load_error_stack, command) unless plugin_class
38
+
39
+ plugin = initialize_plugin( plugin_class )
40
+
41
+ plugin
42
+ end
43
+
44
+ protected
45
+ def initialize_plugin plugin_class
46
+ plugin = plugin_class.new
47
+ plugin.yapra = yapra if plugin.respond_to?('yapra=')
48
+ plugin.pipeline = self if plugin.respond_to?('pipeline=')
49
+
50
+ plugin
51
+ end
52
+
53
+ def construct_module_name_prefix env
54
+ module_name_prefix = [ 'Yapra::Plugin::', '' ]
55
+ if env['module_name_prefix']
56
+ if env['module_name_prefix'].kind_of?(Array)
57
+ module_name_prefix = env['module_name_prefix']
58
+ else
59
+ module_name_prefix = [ env['module_name_prefix'] ]
60
+ end
61
+ end
62
+ module_name_prefix
63
+ end
64
+ end
@@ -8,7 +8,7 @@ class Yapra::Plugin::MechanizeBase < Yapra::Plugin::Base
8
8
  pipeline.context['mechanize_agent']
9
9
  end
10
10
 
11
- def extract_attribute_from element, item
11
+ def extract_attribute_from element, item, binding=nil
12
12
  if plugin_config['extract_xpath']
13
13
  plugin_config['extract_xpath'].each do |k, v|
14
14
  value = nil
@@ -0,0 +1,7 @@
1
+ require 'yapra'
2
+ require 'yapra/inflector'
3
+ require 'yapra/legacy_plugin/base'
4
+
5
+ class Yapra::RbPipeline < Yapra::PipelineBase
6
+
7
+ end
data/lib/yapra/version.rb CHANGED
@@ -2,7 +2,7 @@ module Yapra
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 0
4
4
  MINOR = 1
5
- TINY = 2
5
+ TINY = 3
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
data/lib/yapra.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #--
2
- # Copyright (c) 2008 fraction.jp. Yuanying, Some rights reserved.
2
+ # Copyright (c) 2008 Yuanying Ohtsuka
3
3
  # Original from 2008-06-12 http://pragger.ikejisoft.com/
4
4
  #
5
5
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -20,24 +20,37 @@ module Yapra::Plugin::Feed
20
20
  # content_encoded: '<div><%= title %></div>'
21
21
  class Custom < Yapra::Plugin::MechanizeBase
22
22
  def run(data)
23
- page = agent.get(config['url'])
24
- root = page.root
25
-
23
+ urls =
24
+ if config['url'].kind_of?(Array)
25
+ config['url']
26
+ else
27
+ [ config['url'] ]
28
+ end
26
29
  xconfig = config['extract_xpath']
30
+ wait = config['wait'] || 1
31
+ capture = xconfig['capture']
32
+ split = xconfig['split']
27
33
 
28
- if xconfig['capture']
29
- root = root.at(xconfig['capture'])
30
- end
31
- split = xconfig['split']
32
34
  xconfig.delete('capture')
33
35
  xconfig.delete('split')
34
36
 
35
- root.search(split).each do |element|
36
- item = RSS::RDF::Item.new
37
-
38
- extract_attribute_from element, item
37
+ urls.each do |url|
38
+ logger.debug("Process: #{url}")
39
+ page = agent.get(url)
40
+ root = page.root
41
+
42
+ if capture
43
+ root = root.at(capture)
44
+ end
45
+
46
+ root.search(split).each do |element|
47
+ item = RSS::RDF::Item.new
48
+
49
+ extract_attribute_from element, item, binding
39
50
 
40
- data << item
51
+ data << item
52
+ end
53
+ sleep wait
41
54
  end
42
55
 
43
56
  data
@@ -47,7 +47,7 @@ module Yapra::Plugin::Filter
47
47
  item = new_item
48
48
  end
49
49
 
50
- extract_attribute_from page.root, item
50
+ extract_attribute_from page.root, item, binding
51
51
 
52
52
  end
53
53
  item
@@ -20,8 +20,11 @@ module Yapra::Plugin::Publish
20
20
  #
21
21
  class Gmail < Yapra::Plugin::Publish::Imap
22
22
  protected
23
- def create_imap server, port, usessl
24
- Net::IMAP.new('imap.gmail.com', 993, true)
23
+ def prepare
24
+ super
25
+ config['imap_server'] = 'imap.gmail.com'
26
+ config['port'] = 993
27
+ config['ssl'] = true
25
28
  end
26
29
  end
27
- end
30
+ end
@@ -1,6 +1,5 @@
1
1
  require 'net/imap'
2
- require 'yapra/version'
3
- require 'yapra/plugin/base'
2
+ require 'yapra/plugin/publish/mail'
4
3
 
5
4
  module Yapra::Plugin::Publish
6
5
  # = module: Publish::Imap -- Yuanying
@@ -23,114 +22,32 @@ module Yapra::Plugin::Publish
23
22
  # #from: 'test@example.com'
24
23
  # to: 'test2@example.com'
25
24
  #
26
- class Imap < Yapra::Plugin::Base
27
- def run(data)
28
- username = config['username']
29
- password = config['password']
30
- server = config['imap_server'] || 'imap.gmail.com'
31
- port = config['port'] || 993
32
- usessl = ('off' != config['ssl'])
33
- mailbox = config['mailbox'] || 'inbox'
34
- wait = config['wait'] || 1
35
-
36
- unless config['mail']
37
- config['mail'] = {}
38
- end
39
- subject_prefix = config['mail']['subject_prefix'] || ''
40
- from = config['mail']['from'] || 'yapra@localhost'
41
- to = config['mail']['to'] || 'me@localhost'
42
-
43
- imap = create_imap server, port, usessl
44
- logger.debug(imap.greeting)
45
-
46
- imap.login(username, password)
47
- logger.info('imap login was succeed.')
48
- imap.examine(mailbox)
49
- data.each do |item|
50
- date = item.date || item.dc_date || Time.now
51
- content = item.content_encoded || item.description || 'from Yapra.'
52
- content = [content].pack('m')
53
- if config['mail']['from_template']
54
- from = apply_template(config['mail']['from_template'], binding)
55
- end
56
- if config['mail']['to_template']
57
- to = apply_template(config['mail']['to_template'], binding)
58
- end
59
- subject = (subject_prefix + item.title).gsub(/\n/, '').chomp
60
- logger.debug("try append item: #{subject}")
61
- boundary = "----_____====#{Time.now.to_i}--BOUDARY"
62
- attachments = create_attachments(item, config)
63
- imap.append(mailbox, apply_template(mail_template, binding), nil, date)
64
- # puts apply_template(mail_template, binding)
65
-
66
- sleep wait
67
- end
68
- imap.disconnect
69
-
70
- data
71
- end
72
-
25
+ class Imap < Mail
73
26
  protected
74
- def create_imap server, port, usessl
75
- logger.debug("server: #{server}:#{port}, usessl = #{usessl}")
76
- Net::IMAP.new(server, port, usessl)
27
+ def prepare
28
+ super
29
+ config['imap_server'] = config['imap_server'] || 'imap.gmail.com'
30
+ config['port'] = config['port'] || 993
31
+ config['ssl'] = ('off' != config['ssl'])
32
+ config['mailbox'] = config['mailbox'] || 'inbox'
77
33
  end
78
-
79
- def encode_field field
80
- field.gsub(/[^\x01-\x7f]*/) {|x|
81
- x.scan(/.{1,10}/).map {|y|
82
- "=?UTF-8?B?" + y.to_a.pack('m').chomp + "?="
83
- }.join("\n ")
84
- }
85
- end
86
-
87
- def create_attachments item, config
88
- attachments = []
89
- attachment_attributes = config['mail']['attachments']
90
- if attachment_attributes.kind_of?(String)
91
- file = item.__send__(attachment_attributes)
92
- attachments << file if file.kind_of?(WWW::Mechanize::File)
93
- elsif attachment_attributes.kind_of?(Array)
94
- attachment_attributes.each do |atc|
95
- file = item.__send__(atc)
96
- attachments << file if file.kind_of?(WWW::Mechanize::File)
97
- end
98
- end
99
- attachments
100
- end
101
-
102
- def mail_template
103
- return <<EOT
104
- From: <%=encode_field(from) %>
105
- To: <%=encode_field(to) %>
106
- Date: <%=date.rfc2822 %>
107
- MIME-Version: 1.0
108
- X-Mailer: Yapra <%=Yapra::VERSION::STRING %>
109
- Subject: <%=encode_field(subject) %>
110
- Content-Type: multipart/mixed; boundary="<%=boundary -%>"
111
-
112
- This is a multi-part message in MIME format.
113
34
 
114
- --<%=boundary %>
115
- Content-type: text/html; charset=UTF-8
116
- Content-transfer-encoding: base64
117
-
118
- <%=content %>
119
-
120
- --<%=boundary %>
121
- <% attachments.each do |file| -%>
122
- Content-Type: <%=file.header['Content-Type'] %>;
123
- name="<%=encode_field(file.filename) %>"
124
- Content-Disposition: attachment;
125
- filename="<%=encode_field(file.filename) %>"
126
- Content-Transfer-Encoding: base64
127
-
128
- <%=[file.body].pack('m') -%>
35
+ def open_session
36
+ logger.debug("server: #{config['imap_server']}:#{config['port']}, usessl = #{config['ssl']}")
37
+ imap = Net::IMAP.new(config['imap_server'], config['port'], config['ssl'])
38
+ logger.debug(imap.greeting)
39
+ imap.login(config['username'], config['password'])
40
+ logger.info('imap login was succeed.')
41
+ imap.examine(config['mailbox'])
42
+ @session = imap
43
+ end
129
44
 
130
- --<%=boundary %>
45
+ def close_session
46
+ @session.disconnect
47
+ end
131
48
 
132
- <% end -%>
133
- EOT
49
+ def send_item(msg, opt)
50
+ @session.append(config['mailbox'], msg, nil, opt['date'])
134
51
  end
135
52
  end
136
- end
53
+ end