kandata 0.2.0 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 81234a446c42f891691c7c5d8130512c0a7eaa2b
4
- data.tar.gz: 03e57575d6a39b3f056f433fad9cb791ab21dafa
3
+ metadata.gz: 9841a7b354143f7fbdd512e7e1c84ee3cfe97622
4
+ data.tar.gz: 8f4a1bab20ab17cde5580e27ca0376edd6e53d42
5
5
  SHA512:
6
- metadata.gz: 87912e4ff0f911b3da0e8292877615cf5f8e1ad179d02f22f49ceb3e1eddde9b90965b37440a991aefa6131e94996888c6a6f278a918c79b88043537c82bb3ad
7
- data.tar.gz: 776f38b3353eab850e80c3af4d887d51947ad69f3329f72f8f8dfb9d225d47534c49229c73d48251e68b4169fb4f608850b571da60f6f129cbb5cf7f8d79ed07
6
+ metadata.gz: bb5391ce2ee2ac7112ce5320ebf7df616bee2d6baad69636c43342b50840148a465670796816713c7f1304bfc1e53170e6300d344852e75554e3ef5c0d086c2f
7
+ data.tar.gz: 5b29c796225cc9d6cf73b0fc70c77b5efc305c4c6a93ae2787dfe00d6be119b481b4440abf25148f76751e8496e5cef0a24ca210e55fdb276ce3ad4c991094ed
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Kandata
2
2
 
3
- [![CircleCI](https://circleci.com/gh/tamano/kandata.svg?style=svg)](https://circleci.com/gh/tamano/kandata)
3
+ [![CircleCI](https://circleci.com/gh/eLicenseSystems/kandata.svg?style=svg)](https://circleci.com/gh/eLicenseSystems/kandata)
4
4
  [![Gem Version](https://badge.fury.io/rb/kandata.svg)](https://badge.fury.io/rb/kandata)
5
5
 
6
6
  Kandataは、TSVファイルを自分のローカルのMySQLデータベース内に簡単にロードするためのツールです。
@@ -16,6 +16,22 @@ MySQLサーバーがローカルで動作している必要があります。
16
16
 
17
17
  ## 利用方法
18
18
 
19
+ ### Rubyスクリプト内から利用する場合
20
+
21
+ ```ruby
22
+ require 'kandata'
23
+
24
+ # TSVからデータ生成
25
+ # ローカルのMySQLサーバーのKandataデータベース内にuserテーブルを作成して、TSVでデータを流し込む
26
+ my_user = Kandata.build_from_tsv('./user.tsv')
27
+
28
+ # my_user はActiveRecordをextendしたクラスそのものなので、
29
+ # Railsのモデルクラスのように使って色々出来る。
30
+ # ex) my_user.where(last_login: nil).each do |v| v.disactivate end
31
+ ```
32
+
33
+ ### コマンドラインから実行する場合
34
+
19
35
  以下のように実行します。
20
36
 
21
37
  $ kandata load_tsv TSVファイル名 [--force]
@@ -37,7 +53,7 @@ MySQLサーバーがローカルで動作している必要があります。
37
53
  - 各カラムへのINDEXの追加には対応していません。必要な場合は自分でSQLで`CREATE INDEX`してください。
38
54
 
39
55
  ## 今後の予定
40
- - 作成したテーブルに応じたActiveRecordモデルを自動生成できるようにする。
56
+ - ~作成したテーブルに応じたActiveRecordモデルを自動生成できるようにする。~ **done**
41
57
  - `require`で非RailsなRubyスクリプトで読み込んで使えるように。
42
58
  - 作成したテーブルのバックアップなどの機能を追加する。
43
59
 
@@ -16,8 +16,8 @@ class Kandata
16
16
  end
17
17
 
18
18
  # テーブルを作成し、テーブルにデータを読み込み、テーブル名を戻す
19
- def self.load_tsv(filename, force_load)
19
+ def self.load_tsv(filename, force_load, headers = nil)
20
20
  client = Kandata::Database.new
21
- client.load_tsv(filename, force_load)
21
+ client.load_tsv(filename, force_load, headers)
22
22
  end
23
23
  end
@@ -3,6 +3,7 @@ require 'mysql2'
3
3
 
4
4
  class Kandata
5
5
  # ActiveRecordを使わないでKandataデータベースにアクセスするためのクラス
6
+ # ignore :reek:InstanceVariableAssumption:
6
7
  class Database < Mysql2::Client
7
8
  def initialize
8
9
  create_database_if_not_exists
@@ -15,8 +16,8 @@ class Kandata
15
16
  end
16
17
 
17
18
  # ignore :reek:ControlParameter:
18
- def load_tsv(filename, force_update)
19
- @tsv = Kandata::TsvFile.new(filename)
19
+ def load_tsv(filename, force_update, headers = nil)
20
+ @tsv = Kandata::TsvFile.new(filename, headers)
20
21
 
21
22
  drop_table if force_update
22
23
  create_table
@@ -30,18 +31,17 @@ class Kandata
30
31
  # ignore :reek:FeatureEnvy:
31
32
  def create_database_if_not_exists
32
33
  client = Mysql2::Client.new(Kandata::Database::ConnectionInfo.host_and_user)
33
- database_name = Kandata::Database::ConnectionInfo.database_name
34
34
 
35
35
  begin
36
- client.query("SHOW CREATE DATABASE #{database_name}")
36
+ client.query("SHOW CREATE DATABASE `#{database_name}`")
37
37
  rescue Mysql2::Error => error
38
38
  raise error unless error.message == "Unknown database '#{database_name}'"
39
- client.query("CREATE DATABASE #{database_name}")
39
+ client.query("CREATE DATABASE `#{database_name}`")
40
40
  end
41
41
  end
42
42
 
43
43
  def drop_table
44
- query("DROP TABLE IF EXISTS #{full_table_name}")
44
+ query("DROP TABLE IF EXISTS `#{database_name}`.`#{table_name}`")
45
45
  end
46
46
 
47
47
  def create_table
@@ -50,21 +50,30 @@ class Kandata
50
50
  # 後で追加するため一旦削除
51
51
  columns.delete('id')
52
52
 
53
- columns_text = columns.map { |column| "#{column} VARCHAR(128)" }.join(',')
53
+ columns_text = columns.map { |column| "#{column} TEXT" }.join(',')
54
54
  columns_text = 'id INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,' + columns_text
55
55
 
56
- query("CREATE TABLE #{full_table_name} (#{columns_text})")
56
+ query("CREATE TABLE `#{database_name}`.`#{table_name}` (#{columns_text})")
57
57
  end
58
58
 
59
59
  def load_data
60
- sql = "LOAD DATA LOCAL INFILE '#{@tsv.filename}' INTO TABLE #{full_table_name} IGNORE 1 LINES "
61
- sql += "(#{@tsv.headers.join(',')})"
60
+ sql = "LOAD DATA LOCAL INFILE '#{@tsv.filename}' INTO TABLE `#{database_name}`.`#{table_name}` IGNORE 1 LINES "
61
+ sql += "(#{@tsv.headers.map { |column| "`#{column}`" }.join(',')})"
62
62
 
63
63
  query(sql)
64
64
  end
65
65
 
66
66
  def full_table_name
67
- "#{Kandata::Database::ConnectionInfo.database_name}.#{@tsv.table_name}"
67
+ "#{database_name}.#{table_name}"
68
+ end
69
+
70
+ # ignore :reek:UtilityFunction:
71
+ def database_name
72
+ Kandata::Database::ConnectionInfo.database_name
73
+ end
74
+
75
+ def table_name
76
+ @tsv.table_name if @tsv.present?
68
77
  end
69
78
  end
70
79
  end
@@ -2,9 +2,11 @@
2
2
 
3
3
  class Kandata
4
4
  class Database
5
+ # ignore :reek:Attribute:
5
6
  class ConnectionInfo
6
7
  class << self
7
- attr_reader :host, :user_name, :database_name
8
+ attr_reader :host, :user_name
9
+ attr_accessor :database_name
8
10
  end
9
11
 
10
12
  @host = 'localhost'
@@ -1,28 +1,37 @@
1
1
  # frozen_string_literal: true
2
+ require 'tempfile'
3
+ require 'nkf'
4
+
2
5
  class Kandata
3
6
  # 読み込み用TSVファイル
4
7
  class TsvFile
5
- attr_reader :filename, :headers
8
+ attr_reader :filename, :table_name, :headers
9
+
10
+ RESERVED_COLUMNS = ['type'].freeze
11
+
12
+ def initialize(filename, custom_headers = nil)
13
+ @table_name = File.basename(filename).sub('.tsv', '')
14
+ validate_table_name
6
15
 
7
- def initialize(filename)
8
- @filename = filename
9
- validate_filename
16
+ # TempFileにファイル内容をコピーしてパスを返す
17
+ @filename = copy_to_tempfile(filename)
18
+
19
+ @headers = custom_headers
20
+ @headers ||= File.open(@filename, 'r').first(&:readline).chomp.split("\t")
10
21
 
11
- @headers = File.open(@filename, 'r').first(&:readline).chomp.split("\t")
12
22
  validate_headers
13
23
  end
14
24
 
15
- def validate_filename
16
- raise 'テーブル名に使用できないファイル名です' unless table_name.match?(/^[0-9a-zA-Z$_]+$/)
25
+ def validate_table_name
26
+ raise 'テーブル名に使用できないファイル名です' unless @table_name.match?(/^[0-9a-zA-Z$_]+$/)
17
27
  true
18
28
  end
19
29
 
20
30
  def validate_headers
21
- duplicated_columns = @headers.group_by { |value| value }.reject { |_key, value| value.one? }.keys
22
31
  raise "以下のカラムが複数存在しています #{duplicated_columns}" unless duplicated_columns.empty?
23
32
 
24
- illegal_named_columns = @headers.reject { |value| value.match?(/^[0-9a-zA-Z$_]+$/) }
25
- raise "以下のカラム名は使用できません #{illegal_named_columns}" unless illegal_named_columns.empty?
33
+ raise "以下のカラム名は使用できません #{invalid_columns}" unless invalid_columns.empty?
34
+ raise "以下のカラム名は使用できません #{RESERVED_COLUMNS}" if include_reserved_name?
26
35
 
27
36
  # 現時点では、ミス防止のため、先頭以外にidカラムがあるとエラーとして処理する
28
37
  raise '先頭以外にidカラムがあります' if include_id_column? && @headers[0] != 'id'
@@ -30,12 +39,29 @@ class Kandata
30
39
  true
31
40
  end
32
41
 
33
- def table_name
34
- File.basename(@filename).sub('.tsv', '')
42
+ def duplicated_columns
43
+ @headers.group_by { |value| value }.reject { |_key, value| value.one? }.keys
44
+ end
45
+
46
+ def invalid_columns
47
+ @headers.reject { |value| value.match?(/^[0-9a-zA-Z$_]+$/) }
48
+ end
49
+
50
+ def include_reserved_name?
51
+ RESERVED_COLUMNS.any? { |name| @headers.include?(name) }
35
52
  end
36
53
 
37
54
  def include_id_column?
38
55
  @headers.include?('id')
39
56
  end
57
+
58
+ private
59
+
60
+ def copy_to_tempfile(filename)
61
+ Tempfile.open(basename: self.class.name) do |file|
62
+ file.write(NKF.nkf('-w', File.open(filename, 'r').read))
63
+ file.path
64
+ end
65
+ end
40
66
  end
41
67
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  class Kandata
3
- VERSION = '0.2.0'
3
+ VERSION = '0.3.0'
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kandata
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuya TAMANO
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-22 00:00:00.000000000 Z
11
+ date: 2018-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler