jekyll-import 0.1.0.beta3 → 0.1.0.beta4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +6 -14
  2. data/History.markdown +18 -0
  3. data/README.markdown +12 -1
  4. data/jekyll-import.gemspec +31 -25
  5. data/lib/jekyll-import.rb +50 -1
  6. data/lib/jekyll-import/importer.rb +11 -0
  7. data/lib/jekyll-import/importers.rb +10 -0
  8. data/lib/jekyll-import/importers/csv.rb +50 -0
  9. data/lib/jekyll-import/importers/drupal6.rb +139 -0
  10. data/lib/jekyll-import/importers/drupal7.rb +102 -0
  11. data/lib/jekyll-import/importers/enki.rb +76 -0
  12. data/lib/jekyll-import/importers/google_reader.rb +68 -0
  13. data/lib/jekyll-import/importers/joomla.rb +83 -0
  14. data/lib/jekyll-import/importers/jrnl.rb +127 -0
  15. data/lib/jekyll-import/importers/marley.rb +72 -0
  16. data/lib/jekyll-import/importers/mephisto.rb +109 -0
  17. data/lib/jekyll-import/importers/mt.rb +169 -0
  18. data/lib/jekyll-import/importers/posterous.rb +139 -0
  19. data/lib/jekyll-import/importers/rss.rb +71 -0
  20. data/lib/jekyll-import/importers/s9y.rb +67 -0
  21. data/lib/jekyll-import/importers/textpattern.rb +76 -0
  22. data/lib/jekyll-import/importers/tumblr.rb +265 -0
  23. data/lib/jekyll-import/importers/typo.rb +89 -0
  24. data/lib/jekyll-import/importers/wordpress.rb +323 -0
  25. data/lib/jekyll-import/importers/wordpressdotcom.rb +97 -0
  26. data/lib/jekyll/commands/import.rb +1 -0
  27. data/test/helper.rb +3 -1
  28. data/test/test_jrnl_importer.rb +39 -0
  29. data/test/test_mt_importer.rb +16 -16
  30. data/test/test_tumblr_importer.rb +61 -0
  31. data/test/test_wordpress_importer.rb +1 -1
  32. data/test/test_wordpressdotcom_importer.rb +1 -1
  33. metadata +53 -32
  34. data/lib/jekyll/jekyll-import/csv.rb +0 -30
  35. data/lib/jekyll/jekyll-import/drupal6.rb +0 -112
  36. data/lib/jekyll/jekyll-import/drupal7.rb +0 -74
  37. data/lib/jekyll/jekyll-import/enki.rb +0 -49
  38. data/lib/jekyll/jekyll-import/google_reader.rb +0 -61
  39. data/lib/jekyll/jekyll-import/joomla.rb +0 -53
  40. data/lib/jekyll/jekyll-import/marley.rb +0 -52
  41. data/lib/jekyll/jekyll-import/mephisto.rb +0 -84
  42. data/lib/jekyll/jekyll-import/mt.rb +0 -142
  43. data/lib/jekyll/jekyll-import/posterous.rb +0 -122
  44. data/lib/jekyll/jekyll-import/rss.rb +0 -63
  45. data/lib/jekyll/jekyll-import/s9y.rb +0 -59
  46. data/lib/jekyll/jekyll-import/textpattern.rb +0 -58
  47. data/lib/jekyll/jekyll-import/tumblr.rb +0 -242
  48. data/lib/jekyll/jekyll-import/typo.rb +0 -69
  49. data/lib/jekyll/jekyll-import/wordpress.rb +0 -299
  50. data/lib/jekyll/jekyll-import/wordpressdotcom.rb +0 -84
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YjI5ZmY4ODUyNjIxNTdlNmE1YjllMzVlOWJmYTc0Yjg3OGYyMWUwMA==
5
- data.tar.gz: !binary |-
6
- NTg5OWNkNTJmYjVmYzczMzFlYzQ5NDE3MmJlZTgxNmI4YjAwMTAzOQ==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- NjhlNzU5ZTVlN2Y1NGJiYmU4N2IxMWMyOWU2MzliMDkyMjdhMWM1NzZjZGZh
10
- MTViMGE2NDU4MDQyODQ4MWI3M2FhNjNlZGQ2OGQ3MTg0ODc3OGI4NDBkM2U1
11
- ZmI0Y2E1ZjM3ZjFlMGVjYzZlNTI4YThlOTRhZWFkZGQwMzk0NDc=
12
- data.tar.gz: !binary |-
13
- YjVkY2UyYmY3NzFkOGMwYjY0MzE1NzBiNTk5ODgwYTFmMDg3ZDI3MjExNDM3
14
- ZWMyMGMzZmRmN2IwNTcxMmYwMGIzYWMxMjcyNTJlM2QwZjk2ZTg2YmMzYTgw
15
- YTNiOTM1NjdmZGZlM2Q4ZDRjYTcxMzlhYjViOGRmMTNlMzZiZGE=
2
+ SHA1:
3
+ metadata.gz: cbe997aa363075c0bba1d699dd1b4ccbcd29f080
4
+ data.tar.gz: 6856676d49ecf6a8c64eebd4f85bd846696e63f4
5
+ SHA512:
6
+ metadata.gz: 303be81dcf7b40d878d25153ced30e8865602c8fe0b6b8f57111c021accd27ce3b6e5df2cb95290a2f23fa66fbdcdf791fa79cbdc527ba2851435a4d142902cf
7
+ data.tar.gz: 5812ffbedffcf64ce640c8af5d1140347d71406e2020e75fece71797b4f744b7e8ab29702a29d52ca27dbd11eda12a58698b36015dde65e287b869c41eb859ee
data/History.markdown CHANGED
@@ -6,8 +6,11 @@
6
6
  * Improve MovableType importer (#13)
7
7
  * Add an importer for Google Reader blog exports (#36)
8
8
  * Remove dependency on html2text in the tumblr importer (#33)
9
+ * Add the ability to import .jrnl files (#51)
10
+ * Handle missing gems a bit more gracefully (#59)
9
11
 
10
12
  ### Minor Enhancements
13
+ * Add date and redirection pages for blogs imported from Tumblr (#54)
11
14
  * Various Tumblr Enhancements (#27)
12
15
  * Adding tags to Typo and forcing their encoding to UTF-8 (#11)
13
16
  * S9Y Importer: specify data source using --source option (#18)
@@ -16,6 +19,9 @@
16
19
  importer (#31)
17
20
  * Use tumblr slug for post is available, use that instead (#39, #40)
18
21
  * Drupal 7 importer should use latest revision of a post (#38)
22
+ * Improve the handling of tags in the Drupal 6 importer. Tags with
23
+ spaces are handled now and the importer doesn't eat tags anymore. (#42)
24
+ * Upgrade to `jekyll ~> 1.3` and `safe_yaml ~> 0.9.7`
19
25
 
20
26
  ### Bug Fixes
21
27
  * Remove usage of `Hash#at` in Tumblr importer (#14)
@@ -27,8 +33,20 @@
27
33
  * Fix Drupal 7 importer so it compares node ID's properly between `node` and
28
34
  `field_data_body` tables (#38)
29
35
  * Fix prefix replacement for Drupal6 (#41)
36
+ * Fix an exception when a Movable Type blog did not have additional
37
+ entry text (#45)
38
+ * Create `_layouts/` before writing refresh.html in Drupal migrators (#48)
39
+ * Fix bug where post date in `MT` importer was not imported for older versions
40
+ of MT sites (#62)
41
+ * Fix interface of importers' `#process` method (#69)
30
42
 
31
43
  ### Site Enhancements
32
44
 
33
45
  ### Development Fixes
34
46
  * Update usage docs in RSS importer (#35)
47
+ * Added initial version of a test case for Tumblr (#43)
48
+ * Remove some outdated comments in the Drupal migrators (#50)
49
+ * Update the README to be more informative (#52)
50
+ * Add comment to Wordpress importer on how to install mysql with
51
+ MacPorts (#56)
52
+ * Correcting the homepage URL so links from rubygems.org will work (#63)
data/README.markdown CHANGED
@@ -1,7 +1,18 @@
1
1
  jekyll-import
2
2
  =============
3
3
 
4
- The Jekyll import command for importing from various blogs to Jekyll format.
4
+ The new __Jekyll__ command for importing from various blogs to Jekyll format.
5
+
6
+ **Note: _migrators_ are now called _importers_ and are only available if one installs the `jekyll-import` _gem_.**
7
+
8
+ How `jekyll-import` works:
9
+ ---
10
+
11
+ Install the _rubygem_ with `gem install jekyll-import`
12
+ * In your IRB/Ruby configurations, `require jekyll-import`
13
+ * For CLI uses, the newly installed CLI command is now a Jekyll plugin/sub-command invoked with `jekyll import`
14
+
15
+ Read [Blog migrations](http://jekyllrb.com/docs/migrations/) for more detailed info.
5
16
 
6
17
  [![Build
7
18
  Status](https://travis-ci.org/jekyll/jekyll-import.png?branch=master)](https://travis-ci.org/jekyll/jekyll-import)
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
5
5
  s.required_ruby_version = '>= 1.9.2'
6
6
 
7
7
  s.name = 'jekyll-import'
8
- s.version = '0.1.0.beta3'
9
- s.date = '2013-07-14'
8
+ s.version = '0.1.0.beta4'
9
+ s.date = '2013-11-09'
10
10
  s.rubyforge_project = 'jekyll-import'
11
11
 
12
12
  s.summary = "Import command for Jekyll (static site generator)."
@@ -14,23 +14,23 @@ Gem::Specification.new do |s|
14
14
 
15
15
  s.authors = ["Tom Preston-Werner"]
16
16
  s.email = 'tom@mojombo.com'
17
- s.homepage = 'http://github.com/mojombo/jekyll-import'
17
+ s.homepage = 'http://github.com/jekyll/jekyll-import'
18
18
 
19
19
  s.require_paths = %w[lib]
20
20
 
21
21
  s.rdoc_options = ["--charset=UTF-8"]
22
22
  s.extra_rdoc_files = %w[README.markdown LICENSE]
23
23
 
24
- s.add_runtime_dependency('jekyll', '~> 1.0')
24
+ s.add_runtime_dependency('jekyll', '~> 1.3')
25
25
  s.add_runtime_dependency('fastercsv')
26
26
  s.add_runtime_dependency('nokogiri')
27
- s.add_runtime_dependency('safe_yaml', '~> 0.7.0')
28
-
27
+ s.add_runtime_dependency('safe_yaml', '~> 0.9.7')
28
+
29
29
  # development dependencies
30
- s.add_development_dependency('rake', "~> 10.0.3")
30
+ s.add_development_dependency('rake', "~> 10.1.0")
31
31
  s.add_development_dependency('rdoc', "~> 4.0.0")
32
32
  s.add_development_dependency('activesupport', '~> 3.2')
33
-
33
+
34
34
  # test dependencies:
35
35
  s.add_development_dependency('redgreen', "~> 1.2")
36
36
  s.add_development_dependency('shoulda', "~> 3.3.2")
@@ -44,6 +44,7 @@ Gem::Specification.new do |s|
44
44
  s.add_development_dependency('hpricot', "~> 0.8")
45
45
  s.add_development_dependency('mysql', "~> 2.8")
46
46
  s.add_development_dependency('pg', "~> 0.12")
47
+ s.add_development_dependency('mysql2', "~> 0.3")
47
48
 
48
49
  # = MANIFEST =
49
50
  s.files = %w[
@@ -54,26 +55,31 @@ Gem::Specification.new do |s|
54
55
  Rakefile
55
56
  jekyll-import.gemspec
56
57
  lib/jekyll-import.rb
58
+ lib/jekyll-import/importer.rb
59
+ lib/jekyll-import/importers.rb
60
+ lib/jekyll-import/importers/csv.rb
61
+ lib/jekyll-import/importers/drupal6.rb
62
+ lib/jekyll-import/importers/drupal7.rb
63
+ lib/jekyll-import/importers/enki.rb
64
+ lib/jekyll-import/importers/google_reader.rb
65
+ lib/jekyll-import/importers/joomla.rb
66
+ lib/jekyll-import/importers/jrnl.rb
67
+ lib/jekyll-import/importers/marley.rb
68
+ lib/jekyll-import/importers/mephisto.rb
69
+ lib/jekyll-import/importers/mt.rb
70
+ lib/jekyll-import/importers/posterous.rb
71
+ lib/jekyll-import/importers/rss.rb
72
+ lib/jekyll-import/importers/s9y.rb
73
+ lib/jekyll-import/importers/textpattern.rb
74
+ lib/jekyll-import/importers/tumblr.rb
75
+ lib/jekyll-import/importers/typo.rb
76
+ lib/jekyll-import/importers/wordpress.rb
77
+ lib/jekyll-import/importers/wordpressdotcom.rb
57
78
  lib/jekyll/commands/import.rb
58
- lib/jekyll/jekyll-import/csv.rb
59
- lib/jekyll/jekyll-import/drupal6.rb
60
- lib/jekyll/jekyll-import/drupal7.rb
61
- lib/jekyll/jekyll-import/enki.rb
62
- lib/jekyll/jekyll-import/google_reader.rb
63
- lib/jekyll/jekyll-import/joomla.rb
64
- lib/jekyll/jekyll-import/marley.rb
65
- lib/jekyll/jekyll-import/mephisto.rb
66
- lib/jekyll/jekyll-import/mt.rb
67
- lib/jekyll/jekyll-import/posterous.rb
68
- lib/jekyll/jekyll-import/rss.rb
69
- lib/jekyll/jekyll-import/s9y.rb
70
- lib/jekyll/jekyll-import/textpattern.rb
71
- lib/jekyll/jekyll-import/tumblr.rb
72
- lib/jekyll/jekyll-import/typo.rb
73
- lib/jekyll/jekyll-import/wordpress.rb
74
- lib/jekyll/jekyll-import/wordpressdotcom.rb
75
79
  test/helper.rb
80
+ test/test_jrnl_importer.rb
76
81
  test/test_mt_importer.rb
82
+ test/test_tumblr_importer.rb
77
83
  test/test_wordpress_importer.rb
78
84
  test/test_wordpressdotcom_importer.rb
79
85
  ]
data/lib/jekyll-import.rb CHANGED
@@ -1,7 +1,56 @@
1
1
  $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
2
2
  require 'rubygems'
3
3
  require 'jekyll/commands/import'
4
+ require 'jekyll/stevenson'
5
+ require 'colorator'
6
+
7
+ require 'jekyll-import/importer'
8
+ require 'jekyll-import/importers'
4
9
 
5
10
  module JekyllImport
6
- VERSION = '0.1.0.beta3'
11
+ VERSION = '0.1.0.beta4'
12
+
13
+ def self.logger
14
+ @logger ||= Jekyll::Stevenson.new
15
+ end
16
+
17
+ # Public: Add the subcommands for each importer
18
+ #
19
+ # cmd - the instance of Mercenary::Command from the
20
+ #
21
+ # Returns a list of valid subcommands
22
+ def self.add_importer_commands(cmd)
23
+ commands = []
24
+ JekyllImport::Importer.subclasses.each do |importer|
25
+ name = importer.to_s.split("::").last.downcase
26
+ commands << name
27
+ cmd.command(name.to_sym) do |c|
28
+ c.syntax "jekyll import #{name} [options]"
29
+ importer.specify_options(c)
30
+ c.action do |args, options|
31
+ importer.require_deps
32
+ importer.validate(options) if importer.respond_to?(:validate)
33
+ importer.process(options)
34
+ end
35
+ end
36
+ end
37
+ commands
38
+ end
39
+
40
+ def self.require_with_fallback(gems)
41
+ Array[gems].flatten.each do |gem|
42
+ begin
43
+ require gem
44
+ rescue LoadError
45
+ logger.error "Whoops! Looks like you need to install '#{gem}' before you can use this importer."
46
+ logger.error ""
47
+ logger.error "If you're using bundler:"
48
+ logger.error " 1. Add 'gem \"#{gem}\"' to your Gemfile"
49
+ logger.error " 2. Run 'bundle install'"
50
+ logger.error ""
51
+ logger.error "If you're not using bundler:"
52
+ logger.abort_with " 1. Run 'gem install #{gem}'."
53
+ end
54
+ end
55
+ end
7
56
  end
@@ -0,0 +1,11 @@
1
+ module JekyllImport
2
+ class Importer
3
+ def self.inherited(base)
4
+ subclasses << base
5
+ end
6
+
7
+ def self.subclasses
8
+ @subclasses ||= []
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module JekyllImport
2
+ module Importers
3
+ Dir.chdir(File.expand_path(File.join("importers"), File.dirname(__FILE__))) do
4
+ Dir.entries(".").each do |f|
5
+ next if f[0..0].eql?(".")
6
+ require "jekyll-import/importers/#{f}"
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,50 @@
1
+ module JekyllImport
2
+ module Importers
3
+ class CSV < Importer
4
+ def self.require_deps
5
+ JekyllImport.require_with_fallback(%w[
6
+ csv
7
+ fileutils
8
+ ])
9
+ end
10
+
11
+ def self.specify_options(c)
12
+ c.option 'file', '--file NAME', 'The CSV file to import (default: "posts.csv")'
13
+ end
14
+
15
+ # Reads a csv with title, permalink, body, published_at, and filter.
16
+ # It creates a post file for each row in the csv
17
+ def self.process(options)
18
+ file = options.fetch('file', "posts.csv")
19
+
20
+ FileUtils.mkdir_p "_posts"
21
+ posts = 0
22
+ abort "Cannot find the file '#{file}'. Aborting." unless File.file?(file)
23
+
24
+ ::CSV.foreach(file) do |row|
25
+ next if row[0] == "title"
26
+ posts += 1
27
+ name = build_name(row)
28
+ write_post(name, row[0], row[2])
29
+ end
30
+ "Created #{posts} posts!"
31
+ end
32
+
33
+ def self.write_post(name, title, content)
34
+ File.open("_posts/#{name}", "w") do |f|
35
+ f.puts <<-HEADER
36
+ ---
37
+ layout: post
38
+ title: #{title}
39
+ ---
40
+ HEADER
41
+ f.puts content
42
+ end
43
+ end
44
+
45
+ def self.build_name(row)
46
+ row[3].split(" ")[0]+"-"+row[1]+(row[4] =~ /markdown/ ? ".markdown" : ".textile")
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,139 @@
1
+ # NOTE: This converter requires Sequel and the MySQL gems.
2
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
3
+ # installed, running the following commands should work:
4
+ # $ sudo gem install sequel
5
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
6
+
7
+ module JekyllImport
8
+ module Importers
9
+ class Drupal6 < Importer
10
+ # Reads a MySQL database via Sequel and creates a post file for each story
11
+ # and blog node.
12
+ QUERY = "SELECT n.nid, \
13
+ n.title, \
14
+ nr.body, \
15
+ n.created, \
16
+ n.status, \
17
+ GROUP_CONCAT( td.name SEPARATOR '|' ) AS 'tags' \
18
+ FROM node_revisions AS nr, \
19
+ node AS n \
20
+ LEFT OUTER JOIN term_node AS tn ON tn.nid = n.nid \
21
+ LEFT OUTER JOIN term_data AS td ON tn.tid = td.tid \
22
+ WHERE (n.type = 'blog' OR n.type = 'story' OR n.type = 'article') \
23
+ AND n.vid = nr.vid \
24
+ GROUP BY n.nid"
25
+
26
+ def self.validate(options)
27
+ %w[dbname user].each do |option|
28
+ if options[option].nil?
29
+ abort "Missing mandatory option --#{option}."
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.specify_options(c)
35
+ c.option 'dbname', '--dbname DB', 'Database name'
36
+ c.option 'user', '--user USER', 'Database user name'
37
+ c.option 'password', '--password PW', "Database user's password (default: '')"
38
+ c.option 'host', '--host HOST', 'Database host name (default: "localhost")'
39
+ c.option 'prefix', '--prefix PREFIX', 'Table prefix name'
40
+ end
41
+
42
+ def self.require_deps
43
+ JekyllImport.require_with_fallback(%w[
44
+ rubygems
45
+ sequel
46
+ fileutils
47
+ safe_yaml
48
+ ])
49
+ end
50
+
51
+ def self.process(options)
52
+ dbname = options.fetch('dbname')
53
+ user = options.fetch('user')
54
+ pass = options.fetch('password', "")
55
+ host = options.fetch('host', "localhost")
56
+ prefix = options.fetch('prefix', "")
57
+
58
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
59
+
60
+ if prefix != ''
61
+ QUERY[" node "] = " " + prefix + "node "
62
+ QUERY[" node_revisions "] = " " + prefix + "node_revisions "
63
+ QUERY[" term_node "] = " " + prefix + "term_node "
64
+ QUERY[" term_data "] = " " + prefix + "term_data "
65
+ end
66
+
67
+ FileUtils.mkdir_p "_posts"
68
+ FileUtils.mkdir_p "_drafts"
69
+ FileUtils.mkdir_p "_layouts"
70
+
71
+ # Create the refresh layout
72
+ # Change the refresh url if you customized your permalink config
73
+ File.open("_layouts/refresh.html", "w") do |f|
74
+ f.puts <<EOF
75
+ <!DOCTYPE html>
76
+ <html>
77
+ <head>
78
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
79
+ <meta http-equiv="refresh" content="0;url={{ page.refresh_to_post_id }}.html" />
80
+ </head>
81
+ </html>
82
+ EOF
83
+ end
84
+
85
+ db[QUERY].each do |post|
86
+ # Get required fields and construct Jekyll compatible name
87
+ node_id = post[:nid]
88
+ title = post[:title]
89
+ content = post[:body]
90
+ tags = post.fetch(:tags, '').downcase.strip
91
+ created = post[:created]
92
+ time = Time.at(created)
93
+ is_published = post[:status] == 1
94
+ dir = is_published ? "_posts" : "_drafts"
95
+ slug = title.strip.downcase.gsub(/(&|&amp;)/, ' and ').gsub(/[\s\.\/\\]/, '-').gsub(/[^\w-]/, '').gsub(/[-_]{2,}/, '-').gsub(/^[-_]/, '').gsub(/[-_]$/, '')
96
+ name = time.strftime("%Y-%m-%d-") + slug + '.md'
97
+
98
+ # Get the relevant fields as a hash, delete empty fields and convert
99
+ # to YAML for the header
100
+ data = {
101
+ 'layout' => 'post',
102
+ 'title' => title.to_s,
103
+ 'created' => created,
104
+ 'categories' => tags.split('|')
105
+ }.delete_if { |k,v| v.nil? || v == ''}.each_pair {
106
+ |k,v| ((v.is_a? String) ? v.force_encoding("UTF-8") : v)
107
+ }.to_yaml
108
+
109
+ # Write out the data and content to file
110
+ File.open("#{dir}/#{name}", "w") do |f|
111
+ f.puts data
112
+ f.puts "---"
113
+ f.puts content
114
+ end
115
+
116
+ # Make a file to redirect from the old Drupal URL
117
+ if is_published
118
+ aliases = db["SELECT dst FROM #{prefix}url_alias WHERE src = ?", "node/#{node_id}"].all
119
+
120
+ aliases.push(:dst => "node/#{node_id}")
121
+
122
+ aliases.each do |url_alias|
123
+ FileUtils.mkdir_p url_alias[:dst]
124
+ File.open("#{url_alias[:dst]}/index.md", "w") do |f|
125
+ f.puts "---"
126
+ f.puts "layout: refresh"
127
+ f.puts "refresh_to_post_id: /#{time.strftime("%Y/%m/%d/") + slug}"
128
+ f.puts "---"
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ # TODO: Make dirs & files for nodes of type 'page'
135
+ # Make refresh pages for these as well
136
+ end
137
+ end
138
+ end
139
+ end