cure 0.1.1 → 0.4.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.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +16 -3
  3. data/.tool-versions +1 -0
  4. data/Dockerfile +1 -1
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +25 -6
  7. data/README.md +59 -81
  8. data/docs/README.md +33 -0
  9. data/docs/about.md +219 -0
  10. data/docs/builder/add.md +52 -0
  11. data/docs/builder/black_white_list.md +83 -0
  12. data/docs/builder/copy.md +48 -0
  13. data/docs/builder/explode.md +70 -0
  14. data/docs/builder/main.md +43 -0
  15. data/docs/builder/remove.md +46 -0
  16. data/docs/examples/examples.md +164 -0
  17. data/docs/export/main.md +37 -0
  18. data/docs/extract/main.md +89 -0
  19. data/docs/metadata/main.md +29 -0
  20. data/docs/query/main.md +45 -0
  21. data/docs/sources/main.md +36 -0
  22. data/docs/transform/main.md +53 -0
  23. data/docs/validate/main.md +42 -0
  24. data/exe/cure +12 -37
  25. data/exe/cure.old +59 -0
  26. data/lib/cure/builder/base_builder.rb +151 -0
  27. data/lib/cure/builder/candidate.rb +56 -0
  28. data/lib/cure/cli/command.rb +105 -0
  29. data/lib/cure/cli/generate_command.rb +54 -0
  30. data/lib/cure/cli/new_command.rb +52 -0
  31. data/lib/cure/cli/run_command.rb +19 -0
  32. data/lib/cure/cli/templates/README.md.erb +1 -0
  33. data/lib/cure/cli/templates/gemfile.erb +5 -0
  34. data/lib/cure/cli/templates/gitignore.erb +181 -0
  35. data/lib/cure/cli/templates/new_template.rb.erb +31 -0
  36. data/lib/cure/cli/templates/tool-versions.erb +1 -0
  37. data/lib/cure/config.rb +151 -13
  38. data/lib/cure/coordinator.rb +108 -0
  39. data/lib/cure/database.rb +191 -0
  40. data/lib/cure/dsl/builder.rb +26 -0
  41. data/lib/cure/dsl/exporters.rb +45 -0
  42. data/lib/cure/dsl/extraction.rb +60 -0
  43. data/lib/cure/dsl/metadata.rb +33 -0
  44. data/lib/cure/dsl/queries.rb +36 -0
  45. data/lib/cure/dsl/source_files.rb +36 -0
  46. data/lib/cure/dsl/template.rb +131 -0
  47. data/lib/cure/dsl/transformations.rb +95 -0
  48. data/lib/cure/dsl/validator.rb +22 -0
  49. data/lib/cure/export/base_processor.rb +194 -0
  50. data/lib/cure/export/manager.rb +24 -0
  51. data/lib/cure/extract/base_processor.rb +47 -0
  52. data/lib/cure/extract/csv_lookup.rb +43 -0
  53. data/lib/cure/extract/extractor.rb +80 -0
  54. data/lib/cure/extract/filter.rb +118 -0
  55. data/lib/cure/extract/named_range.rb +94 -0
  56. data/lib/cure/extract/named_range_processor.rb +128 -0
  57. data/lib/cure/extract/variable.rb +25 -0
  58. data/lib/cure/extract/variable_processor.rb +57 -0
  59. data/lib/cure/generator/base_generator.rb +61 -0
  60. data/lib/cure/generator/case_generator.rb +32 -0
  61. data/lib/cure/generator/character_generator.rb +41 -0
  62. data/lib/cure/generator/erb_generator.rb +21 -0
  63. data/lib/cure/generator/eval_generator.rb +34 -0
  64. data/lib/cure/generator/faker_generator.rb +31 -0
  65. data/lib/cure/generator/guid_generator.rb +21 -0
  66. data/lib/cure/generator/hex_generator.rb +21 -0
  67. data/lib/cure/generator/imports.rb +16 -0
  68. data/lib/cure/generator/number_generator.rb +21 -0
  69. data/lib/cure/generator/placeholder_generator.rb +26 -0
  70. data/lib/cure/generator/proc_generator.rb +21 -0
  71. data/lib/cure/generator/redact_generator.rb +22 -0
  72. data/lib/cure/generator/static_generator.rb +21 -0
  73. data/lib/cure/generator/variable_generator.rb +26 -0
  74. data/lib/cure/helpers/file_helpers.rb +50 -0
  75. data/lib/cure/helpers/object_helpers.rb +17 -0
  76. data/lib/cure/helpers/perf_helpers.rb +30 -0
  77. data/lib/cure/helpers/string.rb +54 -0
  78. data/lib/cure/launcher.rb +125 -0
  79. data/lib/cure/log.rb +10 -3
  80. data/lib/cure/planner.rb +136 -0
  81. data/lib/cure/strategy/append_strategy.rb +28 -0
  82. data/lib/cure/strategy/base_strategy.rb +98 -0
  83. data/lib/cure/strategy/contain_strategy.rb +51 -0
  84. data/lib/cure/strategy/end_with_strategy.rb +52 -0
  85. data/lib/cure/strategy/full_strategy.rb +28 -0
  86. data/lib/cure/strategy/history/history_cache.rb +82 -0
  87. data/lib/cure/strategy/imports.rb +12 -0
  88. data/lib/cure/strategy/match_strategy.rb +48 -0
  89. data/lib/cure/strategy/prepend_strategy.rb +28 -0
  90. data/lib/cure/strategy/regex_strategy.rb +55 -0
  91. data/lib/cure/strategy/split_strategy.rb +58 -0
  92. data/lib/cure/strategy/start_with_strategy.rb +53 -0
  93. data/lib/cure/transformation/candidate.rb +47 -36
  94. data/lib/cure/transformation/transform.rb +29 -71
  95. data/lib/cure/validator/base_rule.rb +78 -0
  96. data/lib/cure/validator/candidate.rb +54 -0
  97. data/lib/cure/validator/manager.rb +21 -0
  98. data/lib/cure/validators.rb +71 -0
  99. data/lib/cure/version.rb +1 -1
  100. data/lib/cure.rb +19 -6
  101. data/templates/dsl_example.rb +48 -0
  102. data/templates/empty_template.rb +31 -0
  103. metadata +161 -23
  104. data/lib/cure/csv_helpers.rb +0 -6
  105. data/lib/cure/export/exporter.rb +0 -49
  106. data/lib/cure/file_helpers.rb +0 -38
  107. data/lib/cure/generator/base.rb +0 -148
  108. data/lib/cure/main.rb +0 -63
  109. data/lib/cure/object_helpers.rb +0 -27
  110. data/lib/cure/strategy/base.rb +0 -223
  111. data/templates/aws_cur_template.json +0 -143
  112. data/templates/example_template.json +0 -38
@@ -0,0 +1,181 @@
1
+ # Created by https://www.toptal.com/developers/gitignore/api/rubymine,ruby
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=rubymine,ruby
3
+
4
+ .idea
5
+
6
+ /sandpit
7
+
8
+ /.bundle/
9
+ /.yardoc
10
+ /_yardoc/
11
+ /coverage/
12
+ /doc/
13
+ /pkg/
14
+ /spec/reports/
15
+ /tmp/
16
+
17
+ # rspec failure tracking
18
+ .rspec_status
19
+
20
+ ### Ruby ###
21
+ *.gem
22
+ *.rbc
23
+ /.config
24
+ /InstalledFiles
25
+ /spec/examples.txt
26
+ /test/tmp/
27
+ /test/version_tmp/
28
+
29
+ # Used by dotenv library to load environment variables.
30
+ # .env
31
+
32
+ # Ignore Byebug command history file.
33
+ .byebug_history
34
+
35
+ ## Specific to RubyMotion:
36
+ .dat*
37
+ .repl_history
38
+ build/
39
+ *.bridgesupport
40
+ build-iPhoneOS/
41
+ build-iPhoneSimulator/
42
+
43
+ ## Specific to RubyMotion (use of CocoaPods):
44
+ #
45
+ # We recommend against adding the Pods directory to your .gitignore. However
46
+ # you should judge for yourself, the pros and cons are mentioned at:
47
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
48
+ # vendor/Pods/
49
+
50
+ ## Documentation cache and generated files:
51
+ /.yardoc/
52
+ /rdoc/
53
+
54
+ ## Environment normalization:
55
+ /vendor/bundle
56
+ /lib/bundler/man/
57
+
58
+ # for a library or gem, you might want to ignore these files since the code is
59
+ # intended to run in multiple environments; otherwise, check them in:
60
+ # Gemfile.lock
61
+ # .ruby-version
62
+ # .ruby-gemset
63
+
64
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
65
+ .rvmrc
66
+
67
+ # Used by RuboCop. Remote config files pulled in from inherit_from directive.
68
+ # .rubocop-https?--*
69
+
70
+ ### RubyMine ###
71
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
72
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
73
+
74
+ # User-specific stuff
75
+ .idea/**/workspace.xml
76
+ .idea/**/tasks.xml
77
+ .idea/**/usage.statistics.xml
78
+ .idea/**/dictionaries
79
+ .idea/**/shelf
80
+
81
+ # AWS User-specific
82
+ .idea/**/aws.xml
83
+
84
+ # Generated files
85
+ .idea/**/contentModel.xml
86
+
87
+ # Sensitive or high-churn files
88
+ .idea/**/dataSources/
89
+ .idea/**/dataSources.ids
90
+ .idea/**/dataSources.local.xml
91
+ .idea/**/sqlDataSources.xml
92
+ .idea/**/dynamic.xml
93
+ .idea/**/uiDesigner.xml
94
+ .idea/**/dbnavigator.xml
95
+
96
+ # Gradle
97
+ .idea/**/gradle.xml
98
+ .idea/**/libraries
99
+
100
+ # Gradle and Maven with auto-import
101
+ # When using Gradle or Maven with auto-import, you should exclude module files,
102
+ # since they will be recreated, and may cause churn. Uncomment if using
103
+ # auto-import.
104
+ # .idea/artifacts
105
+ # .idea/compiler.xml
106
+ # .idea/jarRepositories.xml
107
+ # .idea/modules.xml
108
+ # .idea/*.iml
109
+ # .idea/modules
110
+ # *.iml
111
+ # *.ipr
112
+
113
+ # CMake
114
+ cmake-build-*/
115
+
116
+ # Mongo Explorer plugin
117
+ .idea/**/mongoSettings.xml
118
+
119
+ # File-based project format
120
+ *.iws
121
+
122
+ # IntelliJ
123
+ out/
124
+
125
+ # mpeltonen/sbt-idea plugin
126
+ .idea_modules/
127
+
128
+ # JIRA plugin
129
+ atlassian-ide-plugin.xml
130
+
131
+ # Cursive Clojure plugin
132
+ .idea/replstate.xml
133
+
134
+ # Crashlytics plugin (for Android Studio and IntelliJ)
135
+ com_crashlytics_export_strings.xml
136
+ crashlytics.properties
137
+ crashlytics-build.properties
138
+ fabric.properties
139
+
140
+ # Editor-based Rest Client
141
+ .idea/httpRequests
142
+
143
+ # Android studio 3.1+ serialized cache file
144
+ .idea/caches/build_file_checksums.ser
145
+
146
+ ### RubyMine Patch ###
147
+ # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
148
+
149
+ # *.iml
150
+ # modules.xml
151
+ # .idea/misc.xml
152
+ # *.ipr
153
+
154
+ # Sonarlint plugin
155
+ # https://plugins.jetbrains.com/plugin/7973-sonarlint
156
+ .idea/**/sonarlint/
157
+
158
+ # SonarQube Plugin
159
+ # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
160
+ .idea/**/sonarIssues.xml
161
+
162
+ # Markdown Navigator plugin
163
+ # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
164
+ .idea/**/markdown-navigator.xml
165
+ .idea/**/markdown-navigator-enh.xml
166
+ .idea/**/markdown-navigator/
167
+
168
+ # Cache file creation bug
169
+ # See https://youtrack.jetbrains.com/issue/JBR-2257
170
+ .idea/$CACHE_FILE$
171
+
172
+ # CodeStream plugin
173
+ # https://plugins.jetbrains.com/plugin/12206-codestream
174
+ .idea/codestream.xml
175
+
176
+ # End of https://www.toptal.com/developers/gitignore/api/rubymine,ruby
177
+
178
+ /input/*
179
+ !/input/.gitkeep
180
+ /output/*
181
+ !/output/.gitkeep
@@ -0,0 +1,31 @@
1
+ ## Empty template, uncomment the sections that you need!
2
+ #
3
+ # metadata do
4
+ # name ""
5
+ # version ""
6
+ # comments ""
7
+ # additional data: {
8
+ # # Any additional data can go here. For example:
9
+ # key: "value"
10
+ # }
11
+ # end
12
+ #
13
+ # extract do
14
+ # # ...
15
+ # end
16
+ #
17
+ # build do
18
+ # # ...
19
+ # end
20
+ #
21
+ # query do
22
+ # # ...
23
+ # end
24
+ #
25
+ # transform do
26
+ # # ...
27
+ # end
28
+ #
29
+ # export do
30
+ # # ...
31
+ # end
@@ -0,0 +1 @@
1
+ ruby 3.2.1
data/lib/cure/config.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "cure"
4
4
  require "json"
5
5
  require "singleton"
6
+ require "tempfile"
6
7
 
7
8
  module Cure
8
9
  module Configuration
@@ -14,30 +15,46 @@ module Cure
14
15
  conf
15
16
  end
16
17
 
18
+ # @param [Array<Cure::Configuration::CsvFileProxy>] source_files
19
+ # @param [Cure::Template] template
20
+ # @return [Config]
21
+ def create_config(source_files, template)
22
+ Config.new(source_files, template)
23
+ end
24
+
17
25
  # @param [Config] request_config
18
26
  def register_config(request_config)
19
27
  ConfigurationSource.instance.load_config(request_config)
20
28
  end
21
29
 
22
- # @return [Config]
23
- def create_config(source_file_location, template, output_dir)
24
- Config.new(source_file_location, template, output_dir)
25
- end
26
-
30
+ # If we are overloading here as a "data store" and "config store", we
31
+ # could break out variables and placeholders into their own singleton.
32
+ #
33
+ # This should be a kind of instance cache, which loads once per run,
34
+ # and junk can be jammed in there?
27
35
  class Config
28
- attr_accessor :source_file_location, :template, :output_dir
29
36
 
30
- # @param [String] source_file_location
31
- # @param [Hash] template
32
- # @param [String] output_dir
33
- def initialize(source_file_location, template, output_dir)
34
- @source_file_location = source_file_location
37
+ # @return Array<Cure::Configuration::CsvFileProxy>
38
+ attr_accessor :source_files
39
+
40
+ # @return [Cure::Template]
41
+ attr_accessor :template
42
+
43
+ # @param [Array<Cure::Configuration::CsvFileProxy>] source_files
44
+ # @param [Cure::Template] template
45
+ def initialize(source_files, template)
46
+ @source_files = source_files
35
47
  @template = template
36
- @output_dir = output_dir
37
48
  end
38
49
 
39
50
  def placeholders
40
- @template["placeholders"] || []
51
+ @template.transformations.placeholders || {}
52
+ end
53
+
54
+ def with_source_file(&block)
55
+ @source_files.each_with_index do |file, _idx|
56
+ file.with_file(&block)
57
+ end
41
58
  end
42
59
  end
43
60
 
@@ -52,5 +69,126 @@ module Cure
52
69
  @config = config
53
70
  end
54
71
  end
72
+
73
+ class CsvFileProxy
74
+
75
+ # @return [TrueClass,FalseClass]
76
+ attr_reader :valid
77
+ # @return [DefaultFileHandler]
78
+ attr_reader :csv_handler
79
+
80
+ # @return [CsvFileProxy]
81
+ def self.load_file(type, obj, ref_name)
82
+ handler =
83
+ case type
84
+ when :file
85
+ FileHandler.new(obj, ref_name)
86
+ when :file_contents
87
+ FileContentsHandler.new(obj, ref_name)
88
+ when :path, :pathname
89
+ PathnameHandler.new(obj, ref_name)
90
+ else
91
+ raise "Invalid file type handler [#{type}]. Supported: [:file, :file_contents, :path, :pathname]"
92
+ end
93
+
94
+ new(handler)
95
+ end
96
+
97
+ # @return [DefaultFileHandler]
98
+ def initialize(csv_handler)
99
+ @csv_handler = csv_handler
100
+ end
101
+
102
+ def description
103
+ @csv_handler.description
104
+ end
105
+
106
+ def with_file(&block)
107
+ @csv_handler.with_file(&block)
108
+ end
109
+ end
110
+
111
+ class DefaultFileHandler
112
+ attr_reader :type, :ref_name
113
+
114
+ def initialize(type, ref_name)
115
+ @type = type
116
+ @ref_name = ref_name
117
+ end
118
+
119
+ def with_file(&_block)
120
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
121
+ end
122
+
123
+ def description
124
+ raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
125
+ end
126
+ end
127
+
128
+ class PathnameHandler < DefaultFileHandler
129
+ attr_accessor :pathname
130
+
131
+ # @param [Pathname] pathname
132
+ def initialize(pathname, ref_name)
133
+ super(:pathname, ref_name)
134
+ @pathname = pathname
135
+ end
136
+
137
+ def with_file(&_block)
138
+ yield @pathname, @ref_name
139
+ end
140
+
141
+ def description
142
+ @pathname.to_s
143
+ end
144
+ end
145
+
146
+ class FileHandler < DefaultFileHandler
147
+ # @return [File]
148
+ attr_accessor :file
149
+
150
+ # @param [File] file
151
+ def initialize(file, ref_name)
152
+ super(:file, ref_name)
153
+ @file = file
154
+ end
155
+
156
+ def with_file(&_block)
157
+ yield @file, @ref_name
158
+
159
+ ensure
160
+ @file&.close
161
+ end
162
+
163
+ def description
164
+ File.basename(file)
165
+ end
166
+ end
167
+
168
+ class FileContentsHandler < DefaultFileHandler
169
+ # @return [String]
170
+ attr_accessor :file_contents
171
+
172
+ # @param [String] file_contents
173
+ def initialize(file_contents, ref_name)
174
+ super(:file_contents, ref_name)
175
+ @file_contents = file_contents
176
+ end
177
+
178
+ def with_file(&_block)
179
+ tmp_file = Tempfile.new('cure-temp')
180
+ tmp_file.write(@file_contents)
181
+ tmp_file.open
182
+
183
+ yield tmp_file, @ref_name
184
+
185
+ ensure
186
+ tmp_file.unlink
187
+ end
188
+
189
+ def description
190
+ "<content provided>"
191
+ end
192
+ end
55
193
  end
56
194
  end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "cure/log"
4
+ require "cure/config"
5
+ require "cure/database"
6
+ require "cure/helpers/file_helpers"
7
+ require "cure/helpers/perf_helpers"
8
+
9
+ require "cure/extract/extractor"
10
+ require "cure/transformation/transform"
11
+ require "cure/export/manager"
12
+
13
+ require "rcsv"
14
+
15
+ module Cure
16
+ # Coordinates the entire process:
17
+ # Extract -> Build -> Transform -> Export
18
+ class Coordinator
19
+ include Log
20
+ include Database
21
+ include Configuration
22
+ include Helpers::PerfHelpers
23
+
24
+ def process
25
+ # need to check config is init'd
26
+ result = nil
27
+ print_memory_usage do
28
+ print_time_spent do
29
+ # 1. Extract into SQLite
30
+ extract
31
+ # 2. Manipulate SQLite columns
32
+ build
33
+ # 3. Validate columns
34
+
35
+ # 4. Transform each row
36
+ with_tables.each do |table|
37
+ with_transformer(table) do |transformer|
38
+ with_exporters(table) do |exporters|
39
+ database_service.with_paged_result(table) do |row|
40
+ transformed_row = transformer.transform(row)
41
+ exporters.each do |exporter|
42
+ # 4. Export
43
+ exporter.process_row(transformed_row)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ result
53
+ end
54
+
55
+ private
56
+
57
+ def extract
58
+ log_info "Beginning the extraction process..."
59
+
60
+ extractor = Extract::Extractor.new({})
61
+
62
+ file_count = 0
63
+ config.with_source_file do |file, ref_name|
64
+ extractor.parse_csv(file, ref_name: ref_name)
65
+ file_count += 1
66
+ end
67
+
68
+ log_info "...extraction complete"
69
+ end
70
+
71
+ def build
72
+ log_info "Beginning the building process..."
73
+ candidates = config.template.builder.candidates
74
+ candidates.each(&:perform)
75
+
76
+ log_info "... building complete"
77
+ end
78
+
79
+ # @yieldreturn [Cure::Transformation::Transform]
80
+ def with_transformer(named_range, &block)
81
+ raise "No block passed" unless block
82
+
83
+ log_info "Beginning the transformation process..."
84
+ candidates = config.template.transformations.candidates.select { |c| c.named_range == named_range.to_s }
85
+ transformer = Cure::Transformation::Transform.new(candidates)
86
+ yield transformer
87
+
88
+ log_info "...transform complete"
89
+ end
90
+
91
+ # @yieldreturn [Cure::Export::Section]
92
+ def with_exporters(named_range, &block)
93
+ raise "No block passed" unless block
94
+
95
+ log_info "Beginning export process..."
96
+ processors = config.template.exporters.processors.select { |c| c.named_range.to_s == named_range.to_s }
97
+ manager = Cure::Export::Manager.new(named_range, processors)
98
+
99
+ manager.with_processors(&block)
100
+
101
+ log_info "...export complete"
102
+ end
103
+
104
+ def with_tables
105
+ config.template.exporters.processors.map { |e| e.named_range.to_sym }.uniq
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sequel"
4
+ require "sqlite3"
5
+ require "singleton"
6
+
7
+ require "cure/config"
8
+
9
+ module Cure
10
+ module Database
11
+ def database_service
12
+ database = DatabaseSource.instance.database_service
13
+ return database if database
14
+
15
+ init_database
16
+
17
+ DatabaseSource.instance.database_service
18
+ end
19
+
20
+ def init_database
21
+ DatabaseSource.instance.init_database
22
+ end
23
+ end
24
+
25
+ class DatabaseSource
26
+ include Singleton
27
+
28
+ attr_reader :database_service
29
+
30
+ def init_database
31
+ @database_service = DatabaseService.new
32
+ end
33
+ end
34
+
35
+ class DatabaseService
36
+ include Cure::Configuration
37
+
38
+ # @return [Sequel::SQLite::Database]
39
+ attr_reader :database
40
+
41
+ def initialize
42
+ @database = init_database
43
+ setup_db
44
+ end
45
+
46
+ def setup_db
47
+ # Load this from config defined by user?
48
+ @database.execute <<-SQL
49
+ PRAGMA journal_mode = OFF;
50
+ PRAGMA synchronous = 0;
51
+ PRAGMA cache_size = 1000000;
52
+ PRAGMA locking_mode = EXCLUSIVE;
53
+ PRAGMA temp_store = MEMORY;
54
+ SQL
55
+ end
56
+
57
+ # App Service calls
58
+ def find_variable(property_name)
59
+ @database.from(:variables).where(name: property_name).get(:value)
60
+ end
61
+
62
+ def find_translation(source_value)
63
+ @database.from(:translations).where(source_value: source_value).get(:value)
64
+ end
65
+
66
+ def all_translations
67
+ @database.from(:translations).all
68
+ end
69
+
70
+ # @param [Symbol,String] tbl_name
71
+ # @param [Array] columns
72
+ def create_table(tbl_name, columns, auto_increment: true)
73
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
74
+
75
+ @database.create_table tbl_name do
76
+ primary_key :_id, auto_increment: auto_increment
77
+ columns.each do |col_name|
78
+ column col_name.to_sym, String
79
+ end
80
+ end
81
+ end
82
+
83
+ # @param [Symbol,String] tbl_name
84
+ def truncate_table(tbl_name)
85
+ @database[tbl_name.to_sym].truncate
86
+ end
87
+
88
+ # @param [Symbol,String] tbl_name
89
+ def table_count(tbl_name)
90
+ @database[tbl_name.to_sym].count
91
+ end
92
+
93
+ def with_transaction(&block)
94
+ @database.transaction({}, &block)
95
+ end
96
+
97
+ # @param [Symbol,String] tbl_name
98
+ # @return [TrueClass, FalseClass]
99
+ def table_exist?(tbl_name)
100
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
101
+
102
+ @database.table_exists?(tbl_name)
103
+ end
104
+ alias table_exists? table_exist?
105
+
106
+ # @param [Symbol,String] tbl_name
107
+ # @param [Array<String>] row
108
+ def insert_row(tbl_name, row)
109
+ @database[tbl_name.to_sym].insert(row)
110
+ end
111
+
112
+ # @param [Symbol,String] tbl_name
113
+ # @param [Array<String>] rows
114
+ def insert_batched_rows(tbl_name, rows)
115
+ @database[tbl_name.to_sym].import(@database[tbl_name.to_sym].columns, rows)
116
+ end
117
+
118
+ def add_column(tbl_name, new_column, default: "")
119
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
120
+ new_column = new_column.to_sym if new_column.class != Symbol
121
+
122
+ @database.add_column(tbl_name, new_column, String, default: default)
123
+ end
124
+
125
+ def remove_column(tbl_name, remove_column)
126
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
127
+ remove_column = remove_column.to_sym if remove_column.class != Symbol
128
+
129
+ @database.drop_column tbl_name, remove_column
130
+ end
131
+
132
+ def list_columns(tbl_name)
133
+ @database[tbl_name.to_sym].columns
134
+ end
135
+
136
+ def rename_column(tbl_name, old_column, new_column)
137
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
138
+ old_column = old_column.to_sym if old_column.class != Symbol
139
+ new_column = new_column.to_sym if new_column.class != Symbol
140
+
141
+ @database.rename_column tbl_name, old_column, new_column
142
+ end
143
+
144
+ def copy_column(tbl_name, from_column, to_column)
145
+ tbl_name = tbl_name.to_sym if tbl_name.class != Symbol
146
+ from_column = from_column.to_sym if from_column.class != Symbol
147
+ to_column = to_column.to_sym if to_column.class != Symbol
148
+
149
+ add_column tbl_name, to_column
150
+ run("UPDATE #{tbl_name} SET #{to_column} = #{from_column}")
151
+ end
152
+
153
+ def run(query, opts={})
154
+ @database.run(query, opts)
155
+ end
156
+
157
+ # Can we decouple query from named range? Probably more difficult
158
+ # than it seems. But would be nice to create two queries that doesn't
159
+ # require two tables (named ranges).
160
+ def with_paged_result(tbl_name, chunk_size: 100, &block)
161
+ raise "No block given" unless block
162
+
163
+ query = config.template.queries.find(tbl_name)
164
+ if query
165
+ @database[query.query].order(:_id).paged_each(rows_per_fetch: chunk_size, &block)
166
+ else
167
+ @database[tbl_name.to_sym].order(:_id).paged_each(rows_per_fetch: chunk_size, &block)
168
+ end
169
+ end
170
+
171
+ def list_tables
172
+ tbl_arr = @database.tables
173
+ tbl_arr.delete(:variables)
174
+
175
+ if tbl_arr.include?(:translations)
176
+ tbl_arr.delete(:translations)
177
+ tbl_arr.push(:translations)
178
+ end
179
+
180
+ tbl_arr
181
+ end
182
+
183
+ private
184
+
185
+ def init_database
186
+ # Build from config
187
+ # This must clean the database if its not in memory
188
+ Sequel.connect("sqlite:/")
189
+ end
190
+ end
191
+ end