htot_conv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: db822ef12503c3c67ad25292d6843071c98ace01
4
+ data.tar.gz: 8bf51fe9de74f2ab6d135dd7a65f212df9d3a4dc
5
+ SHA512:
6
+ metadata.gz: 5d7e7917107721f317b69f02d18eacf268241e4fdee0bc601b927781fccd1e8cdf0faddb4dd99581bf75ed5e1e56ec744c58d2f7cad5ca601bb93d1cfa4db6e1
7
+ data.tar.gz: f3065da8d0f122319037639cc1934296a804d8080241bd5b611aa6f0372a2fbe7ff91e9c234e4f31a825c7f9c51a8ad8ce2684e20e6a31e2a27ae6386288b00e
data/.gitignore ADDED
@@ -0,0 +1,135 @@
1
+
2
+ # Created by https://www.gitignore.io/api/vim,emacs,ruby
3
+
4
+ ### Emacs ###
5
+ # -*- mode: gitignore; -*-
6
+ *~
7
+ \#*\#
8
+ /.emacs.desktop
9
+ /.emacs.desktop.lock
10
+ *.elc
11
+ auto-save-list
12
+ tramp
13
+ .\#*
14
+
15
+ # Org-mode
16
+ .org-id-locations
17
+ *_archive
18
+
19
+ # flymake-mode
20
+ *_flymake.*
21
+
22
+ # eshell files
23
+ /eshell/history
24
+ /eshell/lastdir
25
+
26
+ # elpa packages
27
+ /elpa/
28
+
29
+ # reftex files
30
+ *.rel
31
+
32
+ # AUCTeX auto folder
33
+ /auto/
34
+
35
+ # cask packages
36
+ .cask/
37
+ dist/
38
+
39
+ # Flycheck
40
+ flycheck_*.el
41
+
42
+ # server auth directory
43
+ /server/
44
+
45
+ # projectiles files
46
+ .projectile
47
+ projectile-bookmarks.eld
48
+
49
+ # directory configuration
50
+ .dir-locals.el
51
+
52
+ # saveplace
53
+ places
54
+
55
+ # url cache
56
+ url/cache/
57
+
58
+ # cedet
59
+ ede-projects.el
60
+
61
+ # smex
62
+ smex-items
63
+
64
+ # company-statistics
65
+ company-statistics-cache.el
66
+
67
+ # anaconda-mode
68
+ anaconda-mode/
69
+
70
+ ### Ruby ###
71
+ *.gem
72
+ *.rbc
73
+ /.config
74
+ /coverage/
75
+ /InstalledFiles
76
+ /pkg/
77
+ /spec/reports/
78
+ /spec/examples.txt
79
+ /test/tmp/
80
+ /test/version_tmp/
81
+ /tmp/
82
+
83
+ # Used by dotenv library to load environment variables.
84
+ # .env
85
+
86
+ ## Specific to RubyMotion:
87
+ .dat*
88
+ .repl_history
89
+ build/
90
+ *.bridgesupport
91
+ build-iPhoneOS/
92
+ build-iPhoneSimulator/
93
+
94
+ ## Specific to RubyMotion (use of CocoaPods):
95
+ #
96
+ # We recommend against adding the Pods directory to your .gitignore. However
97
+ # you should judge for yourself, the pros and cons are mentioned at:
98
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
99
+ #
100
+ # vendor/Pods/
101
+
102
+ ## Documentation cache and generated files:
103
+ /.yardoc/
104
+ /_yardoc/
105
+ /doc/
106
+ /rdoc/
107
+
108
+ ## Environment normalization:
109
+ /.bundle/
110
+ /vendor/bundle
111
+ /lib/bundler/man/
112
+
113
+ # for a library or gem, you might want to ignore these files since the code is
114
+ # intended to run in multiple environments; otherwise, check them in:
115
+ Gemfile.lock
116
+ # .ruby-version
117
+ # .ruby-gemset
118
+
119
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
120
+ .rvmrc
121
+
122
+ ### Vim ###
123
+ # swap
124
+ [._]*.s[a-v][a-z]
125
+ [._]*.sw[a-p]
126
+ [._]s[a-v][a-z]
127
+ [._]sw[a-p]
128
+ # session
129
+ Session.vim
130
+ # temporary
131
+ .netrwhist
132
+ # auto-generated tag files
133
+ tags
134
+
135
+ # End of https://www.gitignore.io/api/vim,emacs,ruby
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.3.4
6
+ before_install: gem install bundler -v 1.12.5
7
+ notifications:
8
+ email: false
9
+ slack:
10
+ secure: "XBa7qrPhwAl3D8C2mnzHHPlFcWj/IDjQ2+ab1P+oaWSzUd6kzFaQeuuBp21omIozfZwZNOLKRLTquN7vrenBL5h9+wtJsIzx5VK9bwPQijm8MAPQ4tWuhYTJfRmEQxhxkT/TKj+OhHLEgf1ZybDdw4PIsSjQEbpGr2Ro6pT13SS6DcmWPBM7eRryd0CN9Y2/bml94nF48LsNh4u5duTDHuB1+CD0dsbN+jtaPb0+rl2VK8+NrEhi4ghr4HKLvNRVyRLfc53aLsJyv5Wehhv5/iAJIeTXV/ys1TLEpt75FIDTTmZDj3zslHCShOuer8O1wXpofOXNOdbHjQ7tGjcg8nit6FezbjBJxPW6ocI3cq7Tl3lVcQqS0Z1s4mtfqgVm6Fw+eRtbFcXVSOu+NyS1ArlEYp624DKsFAb2asELKBD64r18dRSTecdD6LpUe5Dremw5OP4UfVcekbtNs31X/G11WZE6LGidfJjr3Yk9Y16T05ZykGtZDttykhFbDt8dd/YGu5f3rsPsPLVcTYa3ciVk4+e+V8rwNjUZ81o30ChS1PEedUsq7ttZ2QJj4wC6awdaLp+ue+yn7cjJPPZY3cpcgiVTaWjDjqyeGPB/sf0cVhB1nAnkR0d+YIBa+01v7uerfKpZ/UvJSDXCYy1GOFpKuajGgtQ0qd8ceNgcsmE="
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in htot_conv.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # HTOTConv - Hierarchical-Tree Outline Text Converter
2
+
3
+ Convert from a simple hierarchical-tree outline text into ugly xlsx file
4
+
5
+ ## Installation
6
+
7
+ Install `htot_conv` via RubyGems. Simply run the following command to install:
8
+
9
+ $ gem install htot_conv
10
+
11
+ ## Usage
12
+
13
+ $ cat outline.txt
14
+ President
15
+ .VP Marketing
16
+ ..Manager
17
+ ..Manager
18
+ .VP Production
19
+ ..Manager
20
+ ..Manager
21
+ .VP Sales
22
+ ..Manager
23
+ ..Manager
24
+ $ htot_conv -f simple_text --from-indent=. -t xlsx_type2 outline.txt outline.xlsx
25
+ $ xdg-open outline.xlsx
26
+
27
+ ### Types of input
28
+
29
+ #### `simple_text`
30
+
31
+ * A text file consisting of multiple lines where:
32
+ * `<line> ::= { <indent> } <key> { <delimiter> <value> }`
33
+ * `<key>` : a text that does not start with `<indent>` and does not contain `<delimiter>` (if `<delimiter>` specified).
34
+ * `<value>` : a text that does not contain `<delimiter>`.
35
+ * `<indent>` : specified by `--from-indent` option
36
+ * `<delimiter>` : specified by `--from-delimiter` option
37
+
38
+ #### `html_list`
39
+
40
+ * HTML `<ul><li>` and/or `<ol><li>` [nesting list](https://www.w3.org/wiki/HTML_lists#Nesting_lists).
41
+ * All text outside of `<li>` elements is ignored.
42
+
43
+ ### Types of output
44
+
45
+ The sample input used in this section are as follows:
46
+
47
+ 1,1(1),1(2)
48
+ 1.1,1.1(1),1.1(2)
49
+ 1.2,1.2(1),1.2(2)
50
+ 1.2.1,1.2.1(1),1.2.1(2)
51
+
52
+ * key header: H1, H2, H3
53
+ * value header: H(1), H(2)
54
+
55
+ #### `xlsx_type0`
56
+
57
+ | H1 | Level | H(1) | H(2) |
58
+ |-------|-------|----------|----------|
59
+ | 1 | 1 | 1(1) | 1(2) |
60
+ | 1.1 | 2 | 1.1(1) | 1.1(2) |
61
+ | 1.2 | 2 | 1.2(1) | 1.2(2) |
62
+ | 1.2.1 | 3 | 1.2.1(1) | 1.2.1(2) |
63
+
64
+ #### `xlsx_type1`
65
+
66
+ | H1 | H(1) | H(2) |
67
+ |-------|----------|----------|
68
+ | 1 | 1(1) | 1(2) |
69
+ | 1.1 | 1.1(1) | 1.1(2) |
70
+ | 1.2 | 1.2(1) | 1.2(2) |
71
+ | 1.2.1 | 1.2.1(1) | 1.2.1(2) |
72
+
73
+ Not implemented (TODO):
74
+
75
+ * Fill with different background color for each level.
76
+
77
+ #### `xlsx_type2`
78
+
79
+ | H1 | H2 | H3 | H(1) | H(2) |
80
+ |----|-----|-------|----------|----------|
81
+ | 1 | | | 1(1) | 1(2) |
82
+ | | 1.1 | | 1.1(1) | 1.1(2) |
83
+ | | 1.2 | | 1.2(1) | 1.2(2) |
84
+ | | | 1.2.1 | 1.2.1(1) | 1.2.1(2) |
85
+
86
+ Not implemented (TODO):
87
+
88
+ * Cell integration over row.
89
+
90
+ #### `xlsx_type3`
91
+
92
+ Not supported (implemented) as of now.
93
+
94
+ | H1 | H(1) | | | H(2) |
95
+ |----|------|--------|----------|----------|
96
+ | 1 | 1(1) | | | 1(2) |
97
+ | | 1.1 | 1.1(1) | | 1.1(2) |
98
+ | | 1.2 | 1.2(1) | | 1.2(2) |
99
+ | | | 1.2.1 | 1.2.1(1) | 1.2.1(2) |
100
+
101
+ TODO: Github Flavored Markdown does not support for column span.
102
+ So, this document does not correctly represent type-3 xlsx spread sheet.
103
+
104
+ #### `xlsx_type4`
105
+
106
+ | H1 | H2 | H3 | H(1) | H(2) |
107
+ |----|-----|-------|----------|----------|
108
+ | 1 | 1.1 | | 1.1(1) | 1.1(2) |
109
+ | | 1.2 | 1.2.1 | 1.2.1(1) | 1.2.1(2) |
110
+
111
+ Not implemented (TODO):
112
+
113
+ * Cell integration over column.
114
+
115
+ #### `xlsx_type5`
116
+
117
+ | H1 | H2 | H3 | H(1) | H(2) |
118
+ |----|-----|-------|----------|----------|
119
+ | 1 | 1.1 | | 1.1(1) | 1.1(2) |
120
+ | 1 | 1.2 | 1.2.1 | 1.2.1(1) | 1.2.1(2) |
121
+
122
+ Not implemented (TODO):
123
+
124
+ * Cell integration over column.
125
+ * Apply auto filter to the first (header) row.
126
+
127
+ ## Development
128
+
129
+ $ bundle install --path=vendor/bundle --with development test
130
+ $ bundle exec rake test
131
+
132
+ ## Contributing
133
+
134
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/cat-in-136/htot_conv>.
135
+
136
+
137
+ ## License
138
+
139
+ [MIT License](http://opensource.org/licenses/MIT).
140
+ See the `LICENSE.txt` file.
141
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "htot_conv"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/htot_conv ADDED
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env ruby
2
+ require 'optparse'
3
+
4
+ require 'rubygems'
5
+ require 'rinne'
6
+ require 'htot_conv'
7
+
8
+ module HTOTConv
9
+ module CLI
10
+ class ScriptOptions
11
+ def initialize
12
+ @options = {
13
+ :from_type => :simple_text,
14
+ :to_type => :xlsx_type2,
15
+ }
16
+ @from_options = {}
17
+ @to_options = {}
18
+ end
19
+ attr_accessor :options, :from_options, :to_options
20
+
21
+ def define_options(opts)
22
+ opts.banner = %q{Hierarchical-Tree Outline Text Converter}
23
+ opts.define_head %q{Usage: htot_conv [options] [input] [output]}
24
+ opts.separator %q{}
25
+ opts.separator %q{Options:}
26
+
27
+ from_types = HTOTConv::Parser.types.map { |v| [v, v.to_s.tr("_", "-")] }.flatten
28
+ to_types = HTOTConv::Generator.types.map { |v| [v, v.to_s.tr("_", "-")] }.flatten
29
+
30
+ opts.on("-f", "--from-type=TYPE", from_types, "type of input (default: #{options[:from_type]})") do |v|
31
+ options[:from_type] = v.to_s.tr("-", "_")
32
+ end
33
+ opts.on("-t", "--to-type=TYPE", to_types, "type of output (default: #{options[:to_type]})") do |v|
34
+ options[:to_type] = v.to_s.tr("-", "_")
35
+ end
36
+ opts.on("-l", "--list-type", "list input/output type") do
37
+ $stdout << "type of input:\n"
38
+ $stdout << HTOTConv::Parser.types.join(" ") << "\n"
39
+ $stdout << "\n"
40
+ $stdout << "type of output:\n"
41
+ $stdout << HTOTConv::Generator.types.join(" ") << "\n"
42
+ $stdout << "\n"
43
+ exit
44
+ end
45
+
46
+ opts.separator ""
47
+ opts.on("-h", "-?", "--help", "Show this message") do
48
+ puts opts
49
+ exit
50
+ end
51
+ opts.on("--version", "Show version") do
52
+ $stdout << "htot_conv #{HTOTConv::VERSION}\n"
53
+ exit
54
+ end
55
+
56
+ opts.separator ""
57
+ opts.separator "I/O Options:"
58
+ define_sub_options(opts, HTOTConv::Parser, "from") do |key, v|
59
+ @from_options[key] = v
60
+ end
61
+ define_sub_options(opts, HTOTConv::Generator, "to") do |key, v|
62
+ @to_options[key] = v
63
+ end
64
+ end
65
+
66
+ private
67
+ def define_sub_options(opts, klass, prefix) # :yields: key, v
68
+ klass.types.each do |type|
69
+ type_klass = klass.const_get(Rinne.camelize(type.to_s))
70
+ type_klass.option_help.each do |key,v|
71
+ long_option = "--#{prefix}-#{key.to_s.tr('_','-')}=VAL"
72
+ cmd_switch = opts.top.list.find { |v| v.kind_of?(OptionParser::Switch) && v.long.include?(long_option) }
73
+ if cmd_switch
74
+ cmd_switch.desc << "For #{type}, #{v[:desc]}"
75
+ else
76
+ opts.on(long_option, v[:pat], "For #{type}, #{v[:desc]}") do |v|
77
+ yield key, v
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def optparse(args)
86
+ script_opts = ScriptOptions.new
87
+ OptionParser.new do |opts|
88
+ script_opts.define_options(opts)
89
+
90
+ begin
91
+ opts.parse!(args)
92
+ rescue OptionParser::ParseError => ex
93
+ $stderr << ex.message << "\n"
94
+ exit 1
95
+ end
96
+ end
97
+ script_opts
98
+ end
99
+ module_function :optparse
100
+ end
101
+ end
102
+
103
+ script_opts = HTOTConv::CLI.optparse(ARGV)
104
+ options = script_opts.options
105
+ from_options = script_opts.from_options
106
+ to_options = script_opts.to_options
107
+
108
+ inio = ((ARGV.length > 0) && (ARGV[0] != "-"))? File.open(ARGV[0], "rb") : $stdin
109
+ outio = ((ARGV.length > 1) && (ARGV[1] != "-"))? File.open(ARGV[1], "wb") : $stdout
110
+
111
+ HTOTConv.convert(inio, options[:from_type], outio, options[:to_type], from_options, to_options)
data/htot_conv.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'htot_conv/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "htot_conv"
8
+ spec.version = HTOTConv::VERSION
9
+ spec.authors = ["@cat_in_136"]
10
+ spec.email = ["cat.in.136+github@gmail.com"]
11
+
12
+ spec.summary = %q{Hierarchical-Tree Outline Text Converter}
13
+ spec.description = %q{Convert from a simple hierarchical-tree outline text into ugly xlsx file}
14
+ spec.homepage = "https://github.com/cat-in-136/htot_conv"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ #spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_dependency "axlsx", "~> 2.0.1"
31
+ spec.add_dependency "rinne", "~> 0.0.3"
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.12"
34
+ spec.add_development_dependency "rake", "~> 10.0"
35
+ spec.add_development_dependency "minitest", "~> 5.0"
36
+ end
@@ -0,0 +1,34 @@
1
+ module HTOTConv
2
+ module Generator
3
+ class Base
4
+ def initialize(data, option={})
5
+ @data = data
6
+ @option = self.class.option_help.inject({}) { |h, pair| h[pair[0]] = pair[1][:default]; h}.merge(option)
7
+ raise ArgumentError, "data is invalid" unless data.valid?
8
+ end
9
+ def self.option_help
10
+ {}
11
+ end
12
+
13
+ def output(outputfile)
14
+ raise NotImplementedError.new("#{self.class.name}.#{__method__} is an abstract method.")
15
+ end
16
+ end
17
+
18
+ class XlsxBase < Base
19
+ def output_to_worksheet(ws)
20
+ raise NotImplementedError.new("#{self.class.name}.#{__method__} is an abstract method.")
21
+ end
22
+
23
+ def output(outputfile)
24
+ p = Axlsx::Package.new
25
+ p.workbook do |wb|
26
+ wb.add_worksheet do |ws|
27
+ output_to_worksheet(ws)
28
+ end
29
+ end
30
+ p.serialize(outputfile)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ require 'axlsx'
2
+
3
+ require 'htot_conv/generator/base'
4
+
5
+ module HTOTConv
6
+ module Generator
7
+ class XlsxType0 < XlsxBase
8
+ def output_to_worksheet(ws)
9
+ max_value_length = @data.max_value_length
10
+
11
+ ws.add_row([@data.key_header[0], 'Level'].concat(
12
+ HTOTConv::Util.pad_array(@data.value_header, max_value_length)),
13
+ :style => Axlsx::STYLE_THIN_BORDER)
14
+
15
+ @data.item.each do |item|
16
+ ws.add_row([item.key, item.level.to_i].concat(
17
+ HTOTConv::Util.pad_array(item.value, max_value_length)),
18
+ :style => Axlsx::STYLE_THIN_BORDER)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+
2
+ require 'axlsx'
3
+
4
+ require 'htot_conv/generator/base'
5
+
6
+ module HTOTConv
7
+ module Generator
8
+ class XlsxType1 < XlsxBase
9
+ def output_to_worksheet(ws)
10
+ max_value_length = @data.max_value_length
11
+
12
+ ws.add_row([@data.key_header[0]].concat(
13
+ HTOTConv::Util.pad_array(@data.value_header, max_value_length)),
14
+ :style => Axlsx::STYLE_THIN_BORDER)
15
+
16
+ @data.item.each do |item|
17
+ ws.add_row([item.key].concat(
18
+ HTOTConv::Util.pad_array(item.value, max_value_length)),
19
+ :style => Axlsx::STYLE_THIN_BORDER)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,37 @@
1
+ require 'axlsx'
2
+
3
+ require 'htot_conv/generator/base'
4
+
5
+ module HTOTConv
6
+ module Generator
7
+ class XlsxType2 < XlsxBase
8
+ def output_to_worksheet(ws)
9
+ max_level = @data.max_level
10
+ max_value_length = @data.max_value_length
11
+
12
+ ws.add_row(((1..max_level).map {|l| @data.key_header[l - 1] || nil }).concat(
13
+ HTOTConv::Util.pad_array(@data.value_header, max_value_length)),
14
+ :style => Axlsx::STYLE_THIN_BORDER)
15
+
16
+ @data.item.each_with_index do |item, item_index|
17
+ key_cell = Array.new(max_level, nil)
18
+ key_cell[item.level - 1] = item.key
19
+ value_cell = HTOTConv::Util.pad_array(item.value, max_value_length)
20
+
21
+ ws.add_row(key_cell.concat(value_cell),
22
+ :style => Axlsx::STYLE_THIN_BORDER)
23
+
24
+ (1..max_level).each do |level|
25
+ edges = []
26
+ edges << :left if (level <= item.level)
27
+ edges << :right if ((level < item.level) || (level == max_level))
28
+ edges << :top if ((level >= item.level) || (item_index == 0))
29
+ edges << :bottom if ((level > item.level) || (item_index == @data.item.length - 1))
30
+ ws.rows.last.cells[level - 1].style = ws.styles.add_style(
31
+ :border => { :style => :thin, :color => "00", :edges => edges })
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ require 'axlsx'
2
+
3
+ require 'htot_conv/generator/base'
4
+
5
+ module HTOTConv
6
+ module Generator
7
+ class XlsxType4 < XlsxBase
8
+ def output_to_worksheet(ws)
9
+ max_level = @data.max_level
10
+ max_value_length = @data.max_value_length
11
+
12
+ ws.add_row(((1..max_level).map {|l| @data.key_header[l - 1] || nil }).concat(
13
+ HTOTConv::Util.pad_array(@data.value_header, max_value_length)),
14
+ :style => Axlsx::STYLE_THIN_BORDER)
15
+
16
+ @data.to_tree.descendants.each do |node|
17
+ if node.leaf?
18
+ item = node.item
19
+
20
+ key_cell = Array.new(max_level, nil)
21
+ key_cell[node.item.level - 1] = item.key
22
+ node.ancestors do |ancestor|
23
+ key_cell[ancestor.item.level - 1] = ancestor.item.key if ancestor.item
24
+ break if ancestor.prev
25
+ end
26
+
27
+ value_cell = HTOTConv::Util.pad_array(item.value, max_value_length)
28
+
29
+ ws.add_row(key_cell.concat(value_cell),
30
+ :style => Axlsx::STYLE_THIN_BORDER)
31
+
32
+ [node].concat(node.ancestors).each do |ancestor|
33
+ if (ancestor.parent && ancestor.parent.item && ancestor.parent.item.level)
34
+ edges = [:left, :right]
35
+ edges << :top unless ancestor.prev
36
+ edges << :bottom unless ancestor.next
37
+ ws.rows.last.cells[ancestor.parent.item.level - 1].style = ws.styles.add_style(
38
+ :border => { :style => :thin, :color => "00", :edges => edges })
39
+ end
40
+ end
41
+ (item.level..max_level).each do |level|
42
+ edges = [:top, :bottom]
43
+ edges << :left if (level == item.level)
44
+ edges << :right if (level == max_level)
45
+ ws.rows.last.cells[level - 1].style = ws.styles.add_style(
46
+ :border => { :style => :thin, :color => "00", :edges => edges })
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ require 'axlsx'
2
+
3
+ require 'htot_conv/generator/base'
4
+
5
+ module HTOTConv
6
+ module Generator
7
+ class XlsxType5 < XlsxBase
8
+ def output_to_worksheet(ws)
9
+ max_level = @data.max_level
10
+ max_value_length = @data.max_value_length
11
+
12
+ ws.add_row(((1..max_level).map {|l| @data.key_header[l - 1] || nil }).concat(
13
+ HTOTConv::Util.pad_array(@data.value_header, max_value_length)),
14
+ :style => Axlsx::STYLE_THIN_BORDER)
15
+
16
+ @data.to_tree.descendants.each do |node|
17
+ if node.leaf?
18
+ item = node.item
19
+
20
+ key_cell = Array.new(max_level, nil)
21
+ key_cell[node.item.level - 1] = item.key
22
+ node.ancestors do |ancestor|
23
+ key_cell[ancestor.item.level - 1] = ancestor.item.key if ancestor.item
24
+ end
25
+
26
+ value_cell = HTOTConv::Util.pad_array(item.value, max_value_length)
27
+
28
+ ws.add_row(key_cell.concat(value_cell),
29
+ :style => Axlsx::STYLE_THIN_BORDER)
30
+
31
+ (item.level..max_level).each do |level|
32
+ edges = [:top, :bottom]
33
+ edges << :left if (level == item.level)
34
+ edges << :right if (level == max_level)
35
+ ws.rows.last.cells[level - 1].style = ws.styles.add_style(
36
+ :border => { :style => :thin, :color => "00", :edges => edges })
37
+ end
38
+ end
39
+ end
40
+
41
+ ws.auto_filter = "A1:#{ws.rows.last.cells[max_level + max_value_length - 1].r}"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,28 @@
1
+ require 'htot_conv/generator/xlsx_type0.rb'
2
+ require 'htot_conv/generator/xlsx_type1.rb'
3
+ require 'htot_conv/generator/xlsx_type2.rb'
4
+ require 'htot_conv/generator/xlsx_type4.rb'
5
+ require 'htot_conv/generator/xlsx_type5.rb'
6
+
7
+ require 'rinne'
8
+
9
+ module HTOTConv
10
+ module Generator
11
+ def create(type, *args)
12
+ klass = HTOTConv::Generator.const_get(Rinne.camelize(type.to_s))
13
+ klass.new(*args)
14
+ end
15
+ module_function :create
16
+
17
+ def types
18
+ HTOTConv::Generator.constants.reject { |klass|
19
+ klass =~ /Base$/
20
+ }.select { |klass|
21
+ HTOTConv::Generator.const_get(klass).kind_of?(Class)
22
+ }.map { |klass|
23
+ Rinne.to_snake(klass.to_s).to_sym
24
+ }
25
+ end
26
+ module_function :types
27
+ end
28
+ end
@@ -0,0 +1,157 @@
1
+ module HTOTConv
2
+ class Outline
3
+ def initialize
4
+ @item = []
5
+ end
6
+
7
+ attr_accessor :key_header
8
+ attr_accessor :value_header
9
+ attr_accessor :item
10
+
11
+ def add_item(*args)
12
+ @item << Item.new(*args)
13
+ end
14
+
15
+ def valid?
16
+ @key_header.kind_of?(Array) &&
17
+ @key_header.all? { |v| v.nil? || v.kind_of?(String) } &&
18
+ @value_header.kind_of?(Array) &&
19
+ @value_header.all? { |v| v.nil? || v.kind_of?(String) } &&
20
+ @item.kind_of?(Array) &&
21
+ @item.all? { |item| item.valid? }
22
+ end
23
+
24
+ def max_level
25
+ [
26
+ @key_header.length,
27
+ *(@item.map { |v| v.level.to_i }),
28
+ ].max
29
+ end
30
+
31
+ def max_value_length
32
+ [
33
+ @value_header.length,
34
+ *(@item.map { |v| (v.value)? v.value.length : 0 }),
35
+ ].max
36
+ end
37
+
38
+ def ==(v)
39
+ (@key_header == v.key_header) &&
40
+ (@value_header == v.value_header) &&
41
+ (@item == v.item)
42
+ end
43
+
44
+ def to_tree
45
+ root = Tree.new
46
+ last_node = root
47
+ @item.each_with_index do |item,i|
48
+ parent_node = root
49
+ if ((item.level > 1) && !(last_node.root?))
50
+ if item.level > last_node.item.level
51
+ parent_node = last_node
52
+ else
53
+ parent_node = last_node.parent
54
+ parent_node = parent_node.parent until (parent_node.root? || parent_node.item.level < item.level)
55
+ end
56
+ end
57
+
58
+ parent_node << item
59
+ last_node = parent_node.to_a.last
60
+ end
61
+ root
62
+ end
63
+
64
+ Item = Struct.new(:key, :level, :value) do
65
+ def valid?
66
+ self.level.kind_of?(Numeric) &&
67
+ (self.level > 0) &&
68
+ (self.level.to_i == self.level) &&
69
+ self.value.kind_of?(Array)
70
+ end
71
+ end
72
+
73
+ class Tree
74
+ include Enumerable
75
+
76
+ def initialize(item=nil, parent=nil)
77
+ @item = item
78
+ @parent = parent
79
+ @children = []
80
+ end
81
+ attr_accessor :item
82
+ attr_reader :parent
83
+
84
+ def root?
85
+ @parent.nil?
86
+ end
87
+
88
+ def leaf?
89
+ @children.empty?
90
+ end
91
+
92
+ def add(item)
93
+ child = Tree.new(item, self)
94
+ @children << child
95
+ self
96
+ end
97
+ alias :<< :add
98
+
99
+ def each # :yields: child
100
+ @children.each do |v|
101
+ yield v if block_given?
102
+ end
103
+ @children.dup
104
+ end
105
+
106
+ def root
107
+ node = self
108
+ node = node.parent until node.root?
109
+ node
110
+ end
111
+
112
+ def next
113
+ if root?
114
+ nil
115
+ else
116
+ brothers = parent.to_a
117
+ index = brothers.index(self)
118
+ (index + 1 < brothers.length)? brothers[index + 1] : nil
119
+ end
120
+ end
121
+
122
+ def prev
123
+ if root?
124
+ nil
125
+ else
126
+ brothers = parent.to_a
127
+ index = brothers.index(self)
128
+ (index - 1 >= 0)? brothers[index - 1] : nil
129
+ end
130
+ end
131
+
132
+ def ancestors # :yields: ancestor
133
+ ancestors = []
134
+ node = self.parent
135
+ until (node.nil? || node.root?)
136
+ ancestors << node
137
+ yield node if block_given?
138
+ node = node.parent
139
+ end
140
+ ancestors
141
+ end
142
+
143
+ def descendants # :yields: descendant
144
+ descendants = []
145
+ @children.each do |child|
146
+ descendants << child
147
+ yield child if block_given?
148
+ child.descendants do |descendant|
149
+ descendants << descendant
150
+ yield descendant if block_given?
151
+ end
152
+ end
153
+ descendants
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,14 @@
1
+ module HTOTConv
2
+ module Parser
3
+ class Base
4
+ def initialize(option={})
5
+ @option = self.class.option_help.inject({}) { |h, pair| h[pair[0]] = pair[1][:default]; h}.merge(option)
6
+ end
7
+ attr_accessor :option
8
+
9
+ def self.option_help
10
+ {}
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,60 @@
1
+ require 'htot_conv/parser/base'
2
+
3
+ module HTOTConv
4
+ module Parser
5
+ class HtmlList < Base
6
+ def parse(input)
7
+ outline = HTOTConv::Outline.new
8
+ outline.key_header = []
9
+ outline.value_header = []
10
+
11
+ parser = Nokogiri::HTML::SAX::Parser.new(ListDoc.new(outline))
12
+ parser.parse(input)
13
+
14
+ outline
15
+ end
16
+
17
+ class ListDoc < Nokogiri::XML::SAX::Document
18
+ def initialize(outline)
19
+ @outline = outline
20
+ @breadcrumb = []
21
+ @li_text = nil
22
+ end
23
+
24
+ def start_element(name, attrs=[])
25
+ if ((name == "ul") || (name == "ol"))
26
+ generate_outline_item unless @li_text.nil?
27
+ @breadcrumb << name
28
+ elsif name == "li"
29
+ @li_text = "".dup if @breadcrumb.length > 0
30
+ end
31
+ end
32
+
33
+ def end_element(name)
34
+ if ((name == "ul") || (name == "ol"))
35
+ generate_outline_item unless @li_text.nil?
36
+ @breadcrumb.pop
37
+ elsif name == "li"
38
+ generate_outline_item unless @li_text.nil?
39
+ end
40
+ end
41
+
42
+ def characters(string)
43
+ @li_text << string unless @li_text.nil?
44
+ end
45
+
46
+ def cdata_block(string)
47
+ @li_text << string unless @li_text.nil?
48
+ end
49
+
50
+ private
51
+ def generate_outline_item
52
+ level = @breadcrumb.length
53
+ @outline.add_item(@li_text.strip, level, [])
54
+ @li_text = nil
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,52 @@
1
+ require 'htot_conv/parser/base'
2
+
3
+ module HTOTConv
4
+ module Parser
5
+ class SimpleText < Base
6
+ def self.option_help
7
+ {
8
+ :indent => {
9
+ :default => "\t",
10
+ :pat => String,
11
+ :desc => "indent character (default: TAB)",
12
+ },
13
+ :delimiter => {
14
+ :default => nil,
15
+ :pat => String,
16
+ :desc => "separator character of additional data"
17
+ },
18
+ }
19
+ end
20
+
21
+ def parse(input)
22
+ indent_regexp = Regexp.new("^(?<indents>(#{Regexp.escape(option[:indent])})*)")
23
+ delimiter_regexp = (option[:delimiter].kind_of?(String))? Regexp.new(Regexp.escape(option[:delimiter])) : option[:delimiter]
24
+ outline = HTOTConv::Outline.new
25
+ outline.key_header = []
26
+ outline.value_header = []
27
+
28
+ input.each_line do |line|
29
+ level = 1
30
+ value = []
31
+ if (option[:indent] || '').length > 0
32
+ indents = indent_regexp.match(line)[:indents]
33
+ level = 1 + indents.length / option[:indent].length
34
+ line = line.sub(indent_regexp, "")
35
+ end
36
+
37
+ line = line.strip
38
+ if delimiter_regexp
39
+ key = line.split(delimiter_regexp)[0]
40
+ value = line.split(delimiter_regexp)[1..-1] || []
41
+ else
42
+ key = line
43
+ end
44
+
45
+ outline.add_item(key, level, value)
46
+ end
47
+
48
+ outline
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,24 @@
1
+ require 'htot_conv/parser/base.rb'
2
+ require 'htot_conv/parser/simple_text.rb'
3
+ require 'htot_conv/parser/html_list.rb'
4
+
5
+ module HTOTConv
6
+ module Parser
7
+ def create(type, *args)
8
+ klass = HTOTConv::Parser.const_get(Rinne.camelize(type.to_s))
9
+ klass.new(*args)
10
+ end
11
+ module_function :create
12
+
13
+ def types
14
+ HTOTConv::Parser.constants.reject { |klass|
15
+ klass =~ /Base$/
16
+ }.select { |klass|
17
+ HTOTConv::Parser.const_get(klass).kind_of?(Class)
18
+ }.map { |klass|
19
+ Rinne.to_snake(klass.to_s).to_sym
20
+ }
21
+ end
22
+ module_function :types
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+
2
+ module HTOTConv
3
+ module Util
4
+ def pad_array(array, length, pad=nil)
5
+ raise ArgumentError, "array is not an array" unless array.kind_of?(Array)
6
+ raise ArgumentError, "array length #{array.length} is larger than #{length}" if array.length > length
7
+
8
+ array.concat(Array.new(length - array.length, pad))
9
+ end
10
+ module_function :pad_array
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module HTOTConv
2
+ VERSION = "0.0.1"
3
+ end
data/lib/htot_conv.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'htot_conv/version'
2
+
3
+ require 'htot_conv/util'
4
+ require 'htot_conv/outline'
5
+ require 'htot_conv/generator'
6
+ require 'htot_conv/parser'
7
+
8
+ module HTOTConv
9
+
10
+ def convert(input, input_type, output, output_type, input_option={}, output_option={})
11
+ parser = HTOTConv::Parser.create(input_type, input_option)
12
+ outline = parser.parse(input)
13
+ generator = HTOTConv::Generator.create(output_type, outline, output_option)
14
+ generator.output(output)
15
+ end
16
+ module_function :convert
17
+
18
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: htot_conv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - "@cat_in_136"
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-09-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: axlsx
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: rinne
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.0'
83
+ description: Convert from a simple hierarchical-tree outline text into ugly xlsx file
84
+ email:
85
+ - cat.in.136+github@gmail.com
86
+ executables:
87
+ - htot_conv
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/console
98
+ - bin/setup
99
+ - exe/htot_conv
100
+ - htot_conv.gemspec
101
+ - lib/htot_conv.rb
102
+ - lib/htot_conv/generator.rb
103
+ - lib/htot_conv/generator/base.rb
104
+ - lib/htot_conv/generator/xlsx_type0.rb
105
+ - lib/htot_conv/generator/xlsx_type1.rb
106
+ - lib/htot_conv/generator/xlsx_type2.rb
107
+ - lib/htot_conv/generator/xlsx_type4.rb
108
+ - lib/htot_conv/generator/xlsx_type5.rb
109
+ - lib/htot_conv/outline.rb
110
+ - lib/htot_conv/parser.rb
111
+ - lib/htot_conv/parser/base.rb
112
+ - lib/htot_conv/parser/html_list.rb
113
+ - lib/htot_conv/parser/simple_text.rb
114
+ - lib/htot_conv/util.rb
115
+ - lib/htot_conv/version.rb
116
+ homepage: https://github.com/cat-in-136/htot_conv
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.5.2.1
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Hierarchical-Tree Outline Text Converter
140
+ test_files: []