runoff 1.0.1 → 2.0.0

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.
@@ -2,83 +2,62 @@ require 'zip'
2
2
  require 'fileutils'
3
3
 
4
4
  module Runoff
5
+ # Public: Writes data received from the database to text files.
5
6
  class FileWriter
6
- attr_accessor :selected_entries
7
-
8
- def initialize(db_handler)
9
- @db_handler = db_handler
10
- @selected_entries = []
7
+ # Public: Initializes a FileWriter object.
8
+ #
9
+ # options - A Hash with commandline options.
10
+ def initialize(options)
11
+ @export_path = Location.get_export_path options
12
+ @adapter = Object.const_get("Runoff::Adapters::#{options[:adapter]}").new
13
+ @current_file_name = nil
14
+ @buffer = []
11
15
  end
12
16
 
13
- # Public: Exports all the chats from the database.
17
+ # Public: Writes a single row of data to a text file.
18
+ #
19
+ # messages - An Array of data received from the database.
14
20
  #
15
- # data_format - an object that defines how the data should be parsed.
16
- # export_path - a string that points to the directory where exported files must be saved.
17
- def export_database(data_format, export_path, create_archive)
18
- @export_path = export_path
21
+ # Examples
22
+ #
23
+ # write [{ chatname: "#first_user/$second_user;d3d86c6b0e3b8320" ... }, ...]
24
+ def write(messages)
25
+ messages.each_with_index do |m, i|
26
+ file_name = @adapter.get_file_name m[Runoff::COLUMNS[1]]
19
27
 
20
- schema = data_format.get_schema
21
- dataset = @db_handler[schema[:table]]
22
- dataset = dataset.select(*schema[:columns])
28
+ dump unless @current_file_name.nil? || @current_file_name == file_name
23
29
 
24
- dataset.each do |row|
25
- write data_format.build_entry(row)
26
- end
30
+ @current_file_name = file_name
27
31
 
28
- archive unless create_archive == false
32
+ @buffer << @adapter.build_entry(m)
33
+ dump if i == (messages.count - 1)
34
+ end
29
35
  end
30
36
 
31
- # Public: Exports specific chats from the database.
32
- #
33
- # data_format - an object that defines how the data should be parsed.
34
- # export_path - a string that points to the directory where exported files must be saved.
35
- def export_database_partially(data_format, export_path, create_archive, &block)
36
- @export_path = export_path
37
-
38
- schema = data_format.get_schema
39
- dataset = @db_handler[schema[:table]]
40
- indices = block.call self, dataset
41
- clean_indices = indices.split(',').map { |e| e.strip }
42
-
43
- clean_indices.each do |i|
44
- chatname = @selected_entries[i.to_i]
45
- approximation = data_format.denormalize chatname
46
- dataset = @db_handler[schema[:table]]
47
- dataset = dataset.where(Sequel.like(:chatname, "#{approximation}%"))
37
+ # Public: Saves all the exported files in a Zip archive.
38
+ def archive
39
+ archive_name = "#{@export_path}_#{Time.now.to_i}.zip"
48
40
 
49
- dataset.each { |row| write data_format.build_entry(row) }
41
+ Zip::File.open archive_name, Zip::File::CREATE do |archive|
42
+ Dir[File.join(@export_path, '**', '**')].each do |file|
43
+ archive.add File.basename(file), file
44
+ end
50
45
  end
51
46
 
52
- archive unless create_archive == false
47
+ FileUtils.rm_rf @export_path # Delete the folder.
53
48
  end
54
49
 
55
50
  private
56
51
 
57
- # Internal: Appends a new entry to a file.
58
- #
59
- # entry - a hash containing "filename" and "content" keys.
60
- #
61
- # Examples
62
- #
63
- # write { filename: "test.txt", content: "test content" }
64
- def write(entry)
65
- path = "#@export_path/#{entry[:filename]}"
66
-
67
- File.open(path, "a+") { |file| file.write entry[:content] }
68
- end
52
+ # Internal: Dumps the content buffer to a file.
53
+ def dump
54
+ content = @adapter.format_file_content @buffer
69
55
 
70
- # Internal: Creates a Zip file of the destination directory.
71
- def archive
72
- zip_file_name = "#@export_path-#{Time.now.strftime "%Y%m%d%H%S%S"}.zip"
73
-
74
- Zip::File.open(zip_file_name, Zip::File::CREATE) do |zf|
75
- Dir[File.join(@export_path, '**', '**')].each do |f|
76
- zf.add(f.sub("#@export_path/", ''), f)
77
- end
56
+ File.open("#{@export_path}/#@current_file_name", "w") do |file|
57
+ file.puts content
78
58
  end
79
59
 
80
- # Delete the destination directory because it is no longer needed.
81
- FileUtils.rm_r @export_path
60
+ @buffer.clear
82
61
  end
83
62
  end
84
63
  end
@@ -5,13 +5,14 @@ module Runoff
5
5
  #
6
6
  # Examples
7
7
  #
8
- # Location::default_skype_data_location skype_username
8
+ # Location.default_skype_data_location skype_username
9
9
  # # => /home/user/.Skype/skype_username/main.db
10
10
  class Location
11
- # Public: Gets a path to the Skype's main.db file.
11
+ # Public: Gets a path to the Skype main.db file.
12
12
  #
13
- # username - A String containing Skype username
14
- # options - A hash containing command-line options passed to the command.
13
+ # args - An Array of commandline arguments, which might contain a String
14
+ # with the Skype username.
15
+ # options - A hash containing commandline options passed to the command.
15
16
  # If the username is empty, then the hash must contain :from key.
16
17
  #
17
18
  # Examples
@@ -20,14 +21,16 @@ module Runoff
20
21
  # # => Path to the default Skype database location depending on the operating system.
21
22
  #
22
23
  # get_database_path('', { from: '~/Desktop/main.db' })
23
- # # => '~/Desktop/main.db'
24
+ # # => '/Users/username/Desktop/main.db'
24
25
  #
25
26
  # Returns a String
26
- def self.get_database_path(username, options)
27
- if options.from
28
- options.from
27
+ def self.get_database_path(args, options)
28
+ if options.key?(:from)
29
+ options[:from]
30
+ elsif args.count > 0
31
+ self.default_skype_data_location args[0]
29
32
  else
30
- self.default_skype_data_location username
33
+ raise ArgumentError, 'No username or database file path provided.'
31
34
  end
32
35
  end
33
36
 
@@ -40,12 +43,16 @@ module Runoff
40
43
  #
41
44
  # On Linux:
42
45
  # default_skype_data_location skype_username
43
- # # => /home/user/.Skype/skype_username/mai.db
46
+ # # => /home/user/.Skype/skype_username/main.db
44
47
  #
45
48
  # On Windows:
46
49
  # default_skype_data_location skype_username
47
50
  # # => /Users/user/AppData/Roaming/Skype/skype_username/main.db
48
51
  #
52
+ # On Mac OS:
53
+ # default_skype_data_location skype_username
54
+ # # => /Users/user/Library/Application\ Support/Skype/skype_username/main.db
55
+ #
49
56
  # Returns a String that contains the path to the Skype database file.
50
57
  def self.default_skype_data_location(skype_username)
51
58
  case RbConfig::CONFIG['host_os']
@@ -64,25 +71,21 @@ module Runoff
64
71
 
65
72
  # Public: Composes a path where the exported files must be saved.
66
73
  #
67
- # options - an object with command line options passed to the runoff executable.
74
+ # options - A Hash with command line options passed to the runoff executable.
68
75
  #
69
76
  # Returns a string with a directory path.
70
77
  def self.get_export_path(options)
71
- path = options.destination || "#{ENV['HOME']}"
78
+ path = options[:destination] || "#{ENV['HOME']}"
72
79
  path = "#{path}/skype_chat_history"
73
80
 
74
- unless File.exist?(path)
75
- FileUtils::mkdir_p path
76
- end
81
+ FileUtils::mkdir_p path unless File.exist?(path)
77
82
 
78
83
  path
79
84
  end
80
85
 
81
- private
82
-
83
- # Internal: Replaces backslashes with forward slashes and removes drive letter.
86
+ # Public: Replaces backslashes with forward slashes and removes drive letter.
84
87
  #
85
- # path - String containing a directory path.
88
+ # path - A String containing a directory path.
86
89
  #
87
90
  # Examples
88
91
  #
@@ -95,7 +98,7 @@ module Runoff
95
98
  path.gsub(/^[a-zA-Z]:/, '')
96
99
  end
97
100
 
98
- # Internal: Composes the default Skype database location for the Windows 8 operating system.
101
+ # Public: Composes the default Skype database location for the Windows 8 operating system.
99
102
  #
100
103
  # skype_username - A String that contains a username of the Skype account,
101
104
  # which database we want to access.
@@ -1,3 +1,3 @@
1
1
  module Runoff
2
- VERSION = '1.0.1'
2
+ VERSION = '2.0.0'
3
3
  end
data/lib/runoff.rb CHANGED
@@ -1,7 +1,15 @@
1
1
  require 'runoff/version'
2
2
  require 'runoff/location'
3
- require 'runoff/skype_data_format'
3
+ require 'runoff/adapters/adapter'
4
+ require 'runoff/adapters/txt_adapter'
5
+ require 'runoff/adapters/json_adapter'
4
6
  require 'runoff/file_writer'
5
- require 'runoff/commands/command'
6
- require 'runoff/commands/all'
7
- require 'runoff/commands/some'
7
+ require 'runoff/chat'
8
+
9
+ module Runoff
10
+ # Public: A String containing the name of the table, where all the chat messages are stored.
11
+ TABLE = :Messages
12
+
13
+ # Public: An Array with all the important column names.
14
+ COLUMNS = [ :convo_id, :chatname, :timestamp, :from_dispname, :body_xml ]
15
+ end
data/runoff.gemspec CHANGED
@@ -19,7 +19,6 @@ Gem::Specification.new do |gem|
19
19
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
20
  gem.require_paths = ["lib"]
21
21
 
22
- gem.add_dependency 'commander'
23
22
  gem.add_dependency 'sequel'
24
23
  gem.add_dependency 'rubyzip'
25
24
  gem.add_dependency 'sqlite3'
@@ -0,0 +1,90 @@
1
+ require 'minitest/autorun'
2
+ require 'runoff/commandline/all'
3
+ require 'colorize'
4
+ require 'fileutils'
5
+
6
+ class CommandAllTest < MiniTest::Unit::TestCase
7
+ def setup
8
+ @current_directory = File.expand_path(File.dirname(__FILE__))
9
+
10
+ @default_output_dir = "#@current_directory/data"
11
+ @test_database_location = "#@current_directory/data/test_main.db"
12
+ @expected_message = 'Exporting...'.colorize(:green) + "\n" + 'Finished.'.colorize(:green) + "\n"
13
+ end
14
+
15
+ def teardown
16
+ Dir["#@default_output_dir/*"].each do |f|
17
+ if File.extname(f) == '.zip'
18
+ File.delete f
19
+ elsif File.directory?(f) && f !~ /custom_location/
20
+ FileUtils.rm_rf f
21
+ end
22
+ end
23
+ end
24
+
25
+ def test_it_raises_a_ArgumentError_if_called_with_an_empty_list_of_arguments
26
+ command = Runoff::Commandline::All.new( archive: true, adapter: 'TxtAdapter' )
27
+
28
+ assert_raises ArgumentError do
29
+ assert_output 'Exporting...' do
30
+ command.execute []
31
+ end
32
+ end
33
+ end
34
+
35
+ def test_it_exports_all_chat_history_as_a_zip_archive_and_saves_it_in_the_default_location
36
+ command = Runoff::Commandline::All.new( archive: true, adapter: 'TxtAdapter' )
37
+ ENV['HOME'] = @default_output_dir
38
+
39
+ Runoff::Location.stub :default_skype_data_location, @test_database_location do
40
+ assert_output @expected_message do
41
+ command.execute ['white_mike']
42
+ end
43
+ end
44
+
45
+ assert_equal 1, Dir["#@default_output_dir/*.zip"].count
46
+ end
47
+
48
+ def test_it_exports_all_chat_history_as_a_folder_in_the_default_location_if_a_no_archive_option_is_set
49
+ command = Runoff::Commandline::All.new( archive: false, adapter: 'TxtAdapter' )
50
+ ENV['HOME'] = @default_output_dir
51
+
52
+ Runoff::Location.stub :default_skype_data_location, @test_database_location do
53
+ assert_output @expected_message do
54
+ command.execute ['white_mike']
55
+ end
56
+ end
57
+
58
+ assert_equal 1, Dir["#@default_output_dir/skype_chat_history"].count;
59
+ end
60
+
61
+ def test_it_exports_all_chat_history_as_a_zip_archive_from_a_custom_location_and_saves_it_in_the_default_location
62
+ test_custom_database_location = "#@current_directory/data/custom_location/test_main.db"
63
+
64
+ command = Runoff::Commandline::All.new( archive: true, adapter: 'TxtAdapter', from: test_custom_database_location )
65
+ ENV['HOME'] = @default_output_dir
66
+
67
+ Runoff::Location.stub :default_skype_data_location, @test_database_location do
68
+ assert_output @expected_message do
69
+ command.execute ['white_mike']
70
+ end
71
+ end
72
+
73
+ assert_equal 1, Dir["#@default_output_dir/*.zip"].count
74
+ end
75
+
76
+ def test_it_exports_all_chat_history_as_a_zip_archive_and_saves_it_in_a_custom_location
77
+ custom_output_location = "#@current_directory/data/custom_output_location"
78
+
79
+ command = Runoff::Commandline::All.new( archive: true, adapter: 'TxtAdapter', destination: custom_output_location )
80
+ ENV['HOME'] = @default_output_dir
81
+
82
+ Runoff::Location.stub :default_skype_data_location, @test_database_location do
83
+ assert_output @expected_message do
84
+ command.execute ['white_mike']
85
+ end
86
+ end
87
+
88
+ assert_equal 1, Dir["#@default_output_dir/custom_output_location/*.zip"].count
89
+ end
90
+ end
@@ -0,0 +1,49 @@
1
+ require 'minitest/autorun'
2
+ require 'runoff/commandline/some'
3
+ require 'colorize'
4
+ require 'fileutils'
5
+
6
+ class CommandSomeTest < MiniTest::Unit::TestCase
7
+ def setup
8
+ @current_directory = File.expand_path(File.dirname(__FILE__))
9
+
10
+ @default_output_dir = "#@current_directory/data"
11
+ @test_database_location = "#@current_directory/data/test_main.db"
12
+ @expected_message = 'Exporting...'.colorize(:green) + "\n" + 'Finished.'.colorize(:green) + "\n"
13
+ end
14
+
15
+ def teardown
16
+ Dir["#@default_output_dir/*"].each do |f|
17
+ if File.extname(f) == '.zip'
18
+ File.delete f
19
+ elsif File.directory?(f) && f !~ /custom_location/
20
+ FileUtils.rm_rf f
21
+ end
22
+ end
23
+ end
24
+
25
+ def test_it_raises_a_ArgumentError_if_called_with_an_empty_list_of_arguments
26
+ command = Runoff::Commandline::Some.new( archive: true, adapter: 'TxtAdapter' )
27
+
28
+ assert_raises ArgumentError do
29
+ assert_output 'Exporting...' do
30
+ command.execute []
31
+ end
32
+ end
33
+ end
34
+
35
+ def test_it_exports_specified_chat_history_as_a_zip_archive_and_saves_it_in_the_default_location
36
+ command = Runoff::Commandline::Some.new( archive: true, adapter: 'TxtAdapter' )
37
+ ENV['HOME'] = @default_output_dir
38
+
39
+ Runoff::Location.stub :default_skype_data_location, @test_database_location do
40
+ assert_output @expected_message do
41
+ command.stub :prompt_for_chatnames, [1] do
42
+ command.execute ['white_mike']
43
+ end
44
+ end
45
+ end
46
+
47
+ assert_equal 1, Dir["#@default_output_dir/*.zip"].count
48
+ end
49
+ end
Binary file
@@ -0,0 +1,78 @@
1
+ require 'minitest/autorun'
2
+ require 'runoff'
3
+
4
+ describe Runoff::Location do
5
+ it 'must return the default Skype database location on Windows 8.1' do
6
+ skype_username = 'skype_username'
7
+ path = "/AppData/Local/Packages/Microsoft.SkypeApp_124nb4j3654/LocalState/#{skype_username}/main.db"
8
+
9
+ Dir.stub :[], ['/AppData/Local/Packages/Microsoft.SkypeApp_124nb4j3654'] do
10
+ Runoff::Location.get_default_skype_data_location_on_windows_8(skype_username)
11
+ .must_equal(path)
12
+ end
13
+ end
14
+
15
+ it 'must convert a path string from Windows style to Unix style' do
16
+ original_path = 'C:\Users\Neo\main.db'
17
+ expected_path = '/Users/Neo/main.db'
18
+
19
+ Runoff::Location.format_windows_path(original_path).must_equal(expected_path)
20
+ end
21
+
22
+ it 'must build a path for a directory, where the files are supposed to be saved, if the destination option is specified' do
23
+ path = '/Users/Neo/backups'
24
+ options = { destination: path }
25
+
26
+ File.stub :exist?, true do
27
+ Runoff::Location.get_export_path(options).must_equal("#{path}/skype_chat_history")
28
+ end
29
+ end
30
+
31
+ it 'must build a path for a directory, where the files are supposed to be saved, if the destination option is not specified' do
32
+ options = { destination: nil }
33
+ ENV['HOME'] = '/Users/Neo'
34
+
35
+ File.stub :exist?, true do
36
+ Runoff::Location.get_export_path(options).must_equal("#{ENV['HOME']}/skype_chat_history")
37
+ end
38
+ end
39
+
40
+ it 'must return the default Skype database location for Windows Desktop app' do
41
+ RbConfig::CONFIG['host_os'] = 'mingw'
42
+ ENV['APPDATA'] = 'C:\Users\Neo\AppData\Roaming'
43
+
44
+ skype_username = 'skype_username'
45
+ expected_path = "/Users/Neo/AppData/Roaming/Skype/#{skype_username}/main.db"
46
+
47
+ File.stub :exist?, true do
48
+ Runoff::Location.default_skype_data_location(skype_username).must_equal(expected_path)
49
+ end
50
+ end
51
+
52
+ it 'must return the default Skype database location on Linux' do
53
+ RbConfig::CONFIG['host_os'] = 'linux'
54
+ ENV['HOME'] = '/home/Neo'
55
+
56
+ skype_username = 'skype_username'
57
+ expected_path = "#{ENV['HOME']}/.Skype/#{skype_username}/main.db"
58
+
59
+ Runoff::Location.default_skype_data_location(skype_username).must_equal(expected_path)
60
+ end
61
+
62
+ it 'must return the default Skype database location on Mac OS' do
63
+ RbConfig::CONFIG['host_os'] = 'darvin'
64
+ ENV['HOME'] = '/Users/Neo'
65
+
66
+ skype_username = 'skype_username'
67
+ expected_path = "#{ENV['HOME']}/Library/Application Support/Skype/#{skype_username}/main.db"
68
+
69
+ Runoff::Location.default_skype_data_location(skype_username).must_equal(expected_path)
70
+ end
71
+
72
+ it 'must return the path that are specified in the from option' do
73
+ path = '/Users/Neo/Desktop/main.db'
74
+ options = { from: path }
75
+
76
+ Runoff::Location.get_database_path('Neo', options).must_equal(path)
77
+ end
78
+ end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: runoff
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aigars Dzerviniks
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-04 00:00:00.000000000 Z
11
+ date: 2014-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: commander
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: sequel
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -96,16 +82,23 @@ files:
96
82
  - Rakefile
97
83
  - bin/runoff
98
84
  - lib/runoff.rb
99
- - lib/runoff/commands/all.rb
100
- - lib/runoff/commands/command.rb
101
- - lib/runoff/commands/some.rb
85
+ - lib/runoff/adapters/adapter.rb
86
+ - lib/runoff/adapters/json_adapter.rb
87
+ - lib/runoff/adapters/txt_adapter.rb
88
+ - lib/runoff/chat.rb
89
+ - lib/runoff/commandline/all.rb
90
+ - lib/runoff/commandline/command.rb
91
+ - lib/runoff/commandline/none.rb
92
+ - lib/runoff/commandline/some.rb
102
93
  - lib/runoff/file_writer.rb
103
94
  - lib/runoff/location.rb
104
- - lib/runoff/skype_data_format.rb
105
95
  - lib/runoff/version.rb
106
96
  - runoff.gemspec
107
- - test/skype_data_format_test.rb
108
- - test/test_db.sqlite
97
+ - test/command_all_test.rb
98
+ - test/command_some_test.rb
99
+ - test/data/custom_location/test_main.db
100
+ - test/data/test_main.db
101
+ - test/location_test.rb
109
102
  homepage: https://github.com/aigarsdz/runoff
110
103
  licenses:
111
104
  - MIT
@@ -126,10 +119,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
126
119
  version: '0'
127
120
  requirements: []
128
121
  rubyforge_project:
129
- rubygems_version: 2.0.3
122
+ rubygems_version: 2.2.2
130
123
  signing_key:
131
124
  specification_version: 4
132
125
  summary: Tool to export Skype chat history from the SQLite database to text files
133
126
  test_files:
134
- - test/skype_data_format_test.rb
135
- - test/test_db.sqlite
127
+ - test/command_all_test.rb
128
+ - test/command_some_test.rb
129
+ - test/data/custom_location/test_main.db
130
+ - test/data/test_main.db
131
+ - test/location_test.rb
@@ -1,22 +0,0 @@
1
- require 'sequel'
2
-
3
- module Runoff
4
- # Commands that can be executed by the application.
5
- module Commands
6
- class All < Command
7
- # Public: Exports all Skyoe chat history.
8
- #
9
- # args - an array containing skype username
10
- # options - a hash containing user provided options
11
- #
12
- # Examples
13
- #
14
- # All.process ['username'], { from: '~/main.db' }
15
- def self.process(args, options = {})
16
- file_writer, export_path = self.get_file_writer_components args, options
17
-
18
- file_writer.export_database Runoff::SkypeDataFormat.new, export_path, options.archive
19
- end
20
- end
21
- end
22
- end
@@ -1,19 +0,0 @@
1
- module Runoff
2
- # Commands that can be executed by the application.
3
- module Commands
4
- # The base class for every runoff command.
5
- class Command
6
- def self.get_file_writer_components(args, options)
7
- if args.empty? && !options.from
8
- raise ArgumentError.new 'Error: You must specify the Skype username or a --from option'
9
- end
10
-
11
- main_db_path = Runoff::Location.get_database_path args[0], options
12
- export_path = Runoff::Location.get_export_path options
13
- db_handler = Sequel.sqlite main_db_path
14
-
15
- return Runoff::FileWriter.new(db_handler), export_path
16
- end
17
- end
18
- end
19
- end
@@ -1,38 +0,0 @@
1
- require 'sequel'
2
- require 'set'
3
-
4
- module Runoff
5
- # Commands that can be executed by the application.
6
- module Commands
7
- class Some < Command
8
- # Public: Exports a specified part of Skyoe chat history.
9
- #
10
- # args - an array containing skype username
11
- # options - a hash containing user provided options
12
- #
13
- # Examples
14
- #
15
- # Some.process ['username'], { from: '~/main.db' }
16
- def self.process(args, options = {})
17
- file_writer, export_path = self.get_file_writer_components args, options
18
- format = Runoff::SkypeDataFormat.new
19
-
20
- file_writer.export_database_partially format, export_path, options.archive do |w, d|
21
- chatname_dataset = d.select(:chatname)
22
- chatnames = Set.new # Set is necessary to filter out duplicate chatnames.
23
-
24
- chatname_dataset.each { |row| chatnames.add format.normalize(row[:chatname]) }
25
-
26
- # In order to use indices we need an array.
27
- w.selected_entries = chatnames.to_a
28
-
29
- w.selected_entries.each_with_index { |e, i| puts "[#{i}] #{e}" }
30
-
31
- puts # Ensure a blank line.
32
- # Return the user response back to the FileWriter class.
33
- next ask("Specify which chats to export separating each number with a comma:")
34
- end
35
- end
36
- end
37
- end
38
- end