duty_free 1.0.8 → 1.0.10

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.
@@ -97,7 +97,7 @@ module DutyFree
97
97
 
98
98
  # ===================================
99
99
  # Epic require patch
100
- def self._patch_require(module_filename, folder_matcher, search_text, replacement_text, autoload_symbol = nil)
100
+ def self._patch_require(module_filename, folder_matcher, replacements, autoload_symbol = nil, is_bundler = false)
101
101
  mod_name_parts = module_filename.split('.')
102
102
  extension = case mod_name_parts.last
103
103
  when 'rb', 'so', 'o'
@@ -120,10 +120,17 @@ module DutyFree
120
120
  Dir.mkdir(new_part) unless Dir.exist?(new_part)
121
121
  new_part
122
122
  end
123
- if ::DutyFree::Util._write_patched(folder_matcher, module_filename, extension, custom_require_dir, nil, search_text, replacement_text) &&
123
+ if ::DutyFree::Util._write_patched(folder_matcher, module_filename, extension, custom_require_dir, nil, replacements) &&
124
124
  !alp.include?(custom_require_dir)
125
125
  alp.unshift(custom_require_dir)
126
126
  end
127
+ elsif is_bundler
128
+ puts "Bundler hack"
129
+ require 'pry-byebug'
130
+ binding.pry
131
+ x = 5
132
+ # bin_path
133
+ # puts Bundler.require.inspect
127
134
  else
128
135
  unless (require_overrides = ::DutyFree::Util.instance_variable_get(:@_require_overrides))
129
136
  ::DutyFree::Util.instance_variable_set(:@_require_overrides, (require_overrides = {}))
@@ -134,19 +141,19 @@ module DutyFree
134
141
  # then required in place of the original.
135
142
 
136
143
  Kernel.module_exec do
137
- # class << self
138
144
  alias_method :orig_require, :require
139
- # end
140
145
  # To be most faithful to Ruby's normal behaviour, this should look like a public singleton
141
146
  define_method(:require) do |name|
147
+ # %%% Can get a message such as "ActionDispatch::Routing is not missing constant RouteSet! (NameError)"
148
+ # binding.pry if name.start_with?('action_dispatch/routing/route_') # || name == 'active_support/values/time_zone'
142
149
  if (require_override = ::DutyFree::Util.instance_variable_get(:@_require_overrides)[name])
143
- extension, folder_matcher, search_text, replacement_text, autoload_symbol = require_override
150
+ extension, folder_matcher, replacements, autoload_symbol = require_override
144
151
  patched_filename = "/patched_#{name.tr('/', '_')}#{extension}"
145
152
  if $LOADED_FEATURES.find { |f| f.end_with?(patched_filename) }
146
153
  false
147
154
  else
148
155
  is_replaced = false
149
- if (replacement_path = ::DutyFree::Util._write_patched(folder_matcher, name, extension, ::DutyFree::Util._custom_require_dir, patched_filename, search_text, replacement_text))
156
+ if (replacement_path = ::DutyFree::Util._write_patched(folder_matcher, name, extension, ::DutyFree::Util._custom_require_dir, patched_filename, replacements))
150
157
  is_replaced = Kernel.send(:orig_require, replacement_path)
151
158
  elsif replacement_path.nil?
152
159
  puts "Couldn't find #{name} to require it!"
@@ -159,12 +166,13 @@ module DutyFree
159
166
  end
160
167
  end
161
168
  end
162
- require_overrides[module_filename] = [extension, folder_matcher, search_text, replacement_text, autoload_symbol]
169
+ require_overrides[module_filename] = [extension, folder_matcher, replacements, autoload_symbol]
163
170
  end
164
171
  end
165
172
 
166
173
  def self._custom_require_dir
167
174
  unless (custom_require_dir = ::DutyFree::Util.instance_variable_get(:@_custom_require_dir))
175
+ require 'tmpdir'
168
176
  ::DutyFree::Util.instance_variable_set(:@_custom_require_dir, (custom_require_dir = Dir.mktmpdir))
169
177
  # So normal Ruby require will now pick this one up
170
178
  $LOAD_PATH.unshift(custom_require_dir)
@@ -178,11 +186,12 @@ module DutyFree
178
186
 
179
187
  # Returns the full path to the replaced filename, or
180
188
  # false if the file already exists, and nil if it was unable to write anything.
181
- def self._write_patched(folder_matcher, name, extension, dir, patched_filename, search_text, replacement_text)
189
+ def self._write_patched(folder_matcher, name, extension, dir, patched_filename, replacements)
182
190
  # See if our replacement file might already exist for some reason
183
191
  name = +"/#{name}" unless name.start_with?('/')
184
192
  name << extension unless name.end_with?(extension)
185
- return false if File.exist?(replacement_path = "#{dir}#{patched_filename || name}")
193
+ puts (replacement_path = "#{dir}#{patched_filename || name}")
194
+ return false if File.exist?(replacement_path)
186
195
 
187
196
  # Dredge up the original .rb file, doctor it, and then require it instead
188
197
  num_written = nil
@@ -193,9 +202,14 @@ module DutyFree
193
202
  orig_path = "#{path}#{name}"
194
203
  break if path.include?(folder_matcher) && (orig_as = File.open(orig_path))
195
204
  end
196
- if (orig_text = orig_as&.read)
197
- File.open(replacement_path, 'w') do |replacement|
198
- num_written = replacement.write(orig_text.gsub(search_text, replacement_text))
205
+ puts [folder_matcher, name].inspect
206
+ if (updated_text = orig_as&.read)
207
+ File.open(replacement_path, 'w') do |replaced_file|
208
+ replacements = [replacements] unless replacements.first.is_a?(Array)
209
+ replacements.each do |search_text, replacement_text|
210
+ updated_text.gsub!(search_text, replacement_text)
211
+ end
212
+ num_written = replaced_file.write(updated_text)
199
213
  end
200
214
  orig_as.close
201
215
  end
@@ -5,7 +5,7 @@ module DutyFree
5
5
  module VERSION
6
6
  MAJOR = 1
7
7
  MINOR = 0
8
- TINY = 8
8
+ TINY = 10
9
9
 
10
10
  # PRE is nil unless it's a pre-release (beta, RC, etc.)
11
11
  PRE = nil
data/lib/duty_free.rb CHANGED
@@ -50,15 +50,15 @@ if ActiveRecord.version < ::Gem::Version.new('3.2') &&
50
50
  # Remove circular reference for "now"
51
51
  ::DutyFree::Util._patch_require(
52
52
  'active_support/values/time_zone.rb', '/activesupport',
53
- ' def parse(str, now=now)',
54
- ' def parse(str, now=now())'
53
+ [' def parse(str, now=now)',
54
+ ' def parse(str, now=now())']
55
55
  )
56
56
  # Remove circular reference for "reflection" for ActiveRecord 3.1
57
57
  if ActiveRecord.version >= ::Gem::Version.new('3.1')
58
58
  ::DutyFree::Util._patch_require(
59
59
  'active_record/associations/has_many_association.rb', '/activerecord',
60
- 'reflection = reflection)',
61
- 'reflection = reflection())',
60
+ ['reflection = reflection)',
61
+ 'reflection = reflection())'],
62
62
  :HasManyAssociation # Make sure the path for this guy is available to be autoloaded
63
63
  )
64
64
  end
@@ -129,9 +129,21 @@ end
129
129
  # Major compatibility fixes for ActiveRecord < 4.2
130
130
  # ================================================
131
131
  ActiveSupport.on_load(:active_record) do
132
- # Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
133
- if ActiveRecord.version < ::Gem::Version.new('4.0')
134
- module ActiveRecord
132
+ # rubocop:disable Lint/ConstantDefinitionInBlock
133
+ module ActiveRecord
134
+ class Base
135
+ unless respond_to?(:execute_sql)
136
+ class << self
137
+ def execute_sql(sql, *param_array)
138
+ param_array = param_array.first if param_array.length == 1 && param_array.first.is_a?(Array)
139
+ connection.execute(send(:sanitize_sql_array, [sql] + param_array))
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ # Rails < 4.0 cannot do #find_by, #find_or_create_by, or do #pluck on multiple columns, so here are the patches:
146
+ if version < ::Gem::Version.new('4.0')
135
147
  # Normally find_by is in FinderMethods, which older AR doesn't have
136
148
  module Calculations
137
149
  def find_by(*args)
@@ -280,6 +292,7 @@ ActiveSupport.on_load(:active_record) do
280
292
  end
281
293
  end
282
294
  end
295
+ # rubocop:enable Lint/ConstantDefinitionInBlock
283
296
 
284
297
  # Rails < 4.2 is not innately compatible with Ruby 2.4 and later, and comes up with:
285
298
  # "TypeError: Cannot visit Integer" unless we patch like this:
@@ -322,6 +335,7 @@ ActiveSupport.on_load(:active_record) do
322
335
  # First part of arel_table_type stuff:
323
336
  # ------------------------------------
324
337
  # (more found below)
338
+ # was: ActiveRecord.version >= ::Gem::Version.new('3.2') &&
325
339
  if ActiveRecord.version < ::Gem::Version.new('5.0')
326
340
  # Used by Util#_arel_table_type
327
341
  module ActiveRecord
@@ -336,6 +350,17 @@ ActiveSupport.on_load(:active_record) do
336
350
  end
337
351
 
338
352
  include ::DutyFree::Extensions
353
+
354
+ unless ::DutyFree::Extensions::IS_AMOEBA
355
+ # Add amoeba-compatible support
356
+ module ActiveRecord
357
+ class Base
358
+ def self.amoeba(*args)
359
+ puts "Amoeba called from #{name} with #{args.inspect}"
360
+ end
361
+ end
362
+ end
363
+ end
339
364
  end
340
365
 
341
366
  # Do this earlier because stuff here gets mixed into JoinDependency::JoinAssociation and AssociationScope
@@ -0,0 +1,349 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/active_record'
5
+ require 'fancy_gets'
6
+
7
+ module DutyFree
8
+ # Auto-generates an IMPORT_TEMPLATE entry for a model
9
+ class ModelGenerator < ::Rails::Generators::Base
10
+ include FancyGets
11
+ # include ::Rails::Generators::Migration
12
+
13
+ # # source_root File.expand_path('templates', __dir__)
14
+ # class_option(
15
+ # :with_changes,
16
+ # type: :boolean,
17
+ # default: false,
18
+ # desc: 'Add IMPORT_TEMPLATE to model'
19
+ # )
20
+
21
+ desc 'Adds an appropriate IMPORT_TEMPLATE entry into a model of your choosing so that' \
22
+ ' DutyFree can perform exports, and with the Pro version, the same template also' \
23
+ ' does imports.'
24
+
25
+ def df_model_template
26
+ # %%% If Apartment is active, ask which schema they want
27
+
28
+ # Load all models
29
+ Rails.configuration.eager_load_namespaces.select { |ns| ns < Rails::Application }.each(&:eager_load!)
30
+
31
+ # Generate a list of viable models that can be chosen
32
+ longest_length = 0
33
+ model_info = Hash.new { |h, k| h[k] = {} }
34
+ tableless = Hash.new { |h, k| h[k] = [] }
35
+ models = ActiveRecord::Base.descendants.reject do |m|
36
+ trouble = if m.abstract_class?
37
+ true
38
+ elsif !m.table_exists?
39
+ tableless[m.table_name] << m.name
40
+ ' (No Table)'
41
+ else
42
+ this_f_keys = (model_info[m][:f_keys] = m.reflect_on_all_associations.select { |a| a.macro == :belongs_to }) || []
43
+ column_names = (model_info[m][:column_names] = m.columns.map(&:name) - [m.primary_key, 'created_at', 'updated_at', 'deleted_at'] - this_f_keys.map(&:foreign_key))
44
+ if column_names.empty? && this_f_keys && !this_f_keys.empty?
45
+ fk_message = ", although #{this_f_keys.length} foreign keys"
46
+ " (No columns#{fk_message})"
47
+ end
48
+ end
49
+ # puts "#{m.name}#{trouble}" if trouble&.is_a?(String)
50
+ trouble
51
+ end
52
+ models.sort! do |a, b| # Sort first to separate namespaced stuff from the rest, then alphabetically
53
+ is_a_namespaced = a.name.include?('::')
54
+ is_b_namespaced = b.name.include?('::')
55
+ if is_a_namespaced && !is_b_namespaced
56
+ 1
57
+ elsif !is_a_namespaced && is_b_namespaced
58
+ -1
59
+ else
60
+ a.name <=> b.name
61
+ end
62
+ end
63
+ models.each do |m| # Find longest name in the list for future use to show lists on the right side of the screen
64
+ # Strangely this can't be inlined since it assigns to "len"
65
+ if longest_length < (len = m.name.length)
66
+ longest_length = len
67
+ end
68
+ end
69
+
70
+ model_name = ARGV[0]&.camelize
71
+ unless (starting = models.find { |m| m.name == model_name })
72
+ puts "#{"Couldn't find #{model_name}. " if model_name}Pick a model to start from:"
73
+ starting = gets_list(
74
+ list: models,
75
+ on_select: proc do |item|
76
+ selected = item[:selected] || item[:focused]
77
+ this_model_info = model_info[selected]
78
+ selected.name + " (#{(this_model_info[:column_names] + this_model_info[:f_keys].map(&:name).map(&:upcase)).join(', ')})"
79
+ end
80
+ )
81
+ end
82
+ puts "\nThinking..."
83
+
84
+ # %%% Find out how many hops at most we can go from this model
85
+ max_hm_nav = starting.suggest_template(-1, true, false)
86
+ max_bt_nav = starting.suggest_template(-1, false, false)
87
+ hops_with_hm, num_hm_hops_tables = calc_num_hops([[starting, max_hm_nav[:all]]], models)
88
+ hops, num_hops_tables = calc_num_hops([[starting, max_bt_nav[:all]]], models)
89
+ # print "\b" * 11
90
+ unless hops_with_hm.length == hops.length
91
+ starting_name = starting.name
92
+ unless (is_hm = ARGV[1]&.downcase)
93
+ puts "Navigate from #{starting_name} using:\n#{'=' * (21 + starting_name.length)}"
94
+ is_hm = gets_list(
95
+ ["Only belongs_to (max of #{hops.length} hops and #{num_hops_tables} tables)",
96
+ "has_many as well as belongs_to (max of #{hops_with_hm.length} hops and #{num_hm_hops_tables} tables)"]
97
+ )
98
+ end
99
+ is_hm = is_hm.start_with?('has_many') || is_hm[0] == 'y'
100
+ hops = hops_with_hm if is_hm
101
+ end
102
+
103
+ unless (num_hops = ARGV[2]&.to_i)
104
+ puts "\nNow, how many hops total would you like to navigate?"
105
+ index = 0
106
+ cumulative = 0
107
+ hops_list = ['0'] + hops.map { |h| "#{index += 1} (#{cumulative += h.length} linkages)" }
108
+ num_hops = gets_list(
109
+ list: hops_list,
110
+ on_select: proc do |value|
111
+ associations = Hash.new { |h, k| h[k] = 0 }
112
+ index = (value[:selected] || value[:focused]).split(' ').first.to_i - 1
113
+ layer = hops[index] if index >= 0
114
+ layer ||= []
115
+ layer.each { |i| associations[i.last] += 1 }
116
+ associations.each { |k, v| associations.delete(k) if v == 1 }
117
+ layer.map do |l|
118
+ associations.keys.include?(l.last) ? "#{l.first.name.demodulize} #{l.last}" : l.last
119
+ end.join(', ')
120
+ # y = model_info[data[:focused].name]
121
+ # data[:focused].name + " (#{(y[:column_names] + y[:f_keys].map(&:name).map(&:upcase)).join(', ')})"
122
+ # layer.inspect
123
+ end
124
+ ).split(' ').first.to_i
125
+ end
126
+
127
+ print "Navigating from #{starting_name}" if model_name
128
+ puts "\nOkay, #{num_hops} hops#{', including has_many,' if is_hm} it is!"
129
+ # Grab the console output from this:
130
+ original_stdout = $stdout
131
+ $stdout = StringIO.new
132
+ starting.suggest_template(num_hops, is_hm)
133
+ output = $stdout
134
+ $stdout = original_stdout
135
+ filename = nil
136
+ output.rewind
137
+ lines = output.each_line.each_with_object([]) do |line, s|
138
+ if line == "\n"
139
+ # Do nothing
140
+ elsif filename
141
+ s << line
142
+ elsif line.start_with?('# Place the following into ')
143
+ filename = line[27..-1]&.split(':')&.first
144
+ end
145
+ s
146
+ end
147
+
148
+ model_file = File.open(filename, 'r+')
149
+ insert_at = nil
150
+ starting_name = starting.name.demodulize
151
+ loop do
152
+ break if model_file.eof?
153
+
154
+ line_parts = model_file.readline.strip.gsub(/ +/, ' ').split(' ')
155
+ if line_parts.first == 'class' && line_parts[1] && (line_parts[1] == starting_name || line_parts[1].end_with?("::#{starting_name}"))
156
+ insert_at = model_file.pos
157
+ break
158
+ end
159
+ end
160
+ line = nil
161
+ import_template_blocks = []
162
+ import_template_block = nil
163
+ indentation = nil
164
+ # See if there's already any IMPORT_TEMPLATE entries in the model file.
165
+ # If there already is just one, we will comment it out if needs be before adding a fresh one.
166
+ loop do
167
+ break if model_file.eof?
168
+
169
+ line_parts = (line = model_file.readline).strip.split(/[\s=]+/)
170
+ indentation ||= line[0...(/\S/ =~ line)]
171
+ case line_parts[-2..-1]
172
+ when ['IMPORT_TEMPLATE', '{']
173
+ import_template_blocks << import_template_block if import_template_block
174
+ import_template_block = [model_file.pos - line.length, nil, line.strip[0] == '#', []]
175
+ when ['#', '------------------------------------------']
176
+ import_template_block[1] = model_file.pos if import_template_block # && import_template_block[1].nil?
177
+ end
178
+ next unless import_template_block
179
+
180
+ # Collect all the lines of any existing block
181
+ import_template_block[3] << line
182
+ # Cap this one if it's done
183
+ if import_template_block[1]
184
+ import_template_blocks << import_template_block
185
+ import_template_block = nil
186
+ end
187
+ end
188
+ import_template_blocks << import_template_block if import_template_block
189
+ comments = nil
190
+ is_add_cr = nil
191
+ if import_template_blocks.length > 1
192
+ # %%% maybe in the future: remove any older commented ones
193
+ puts 'Found multiple existing import template blocks. Will not attempt to automatically add yet another.'
194
+ insert_at = nil
195
+ elsif import_template_blocks.length == 1
196
+ # Get set up to add the new block after the existing one
197
+ insert_at = (import_template_block = import_template_blocks.first)[1]
198
+ if insert_at.nil?
199
+ puts "Found what looked like the start of an existing IMPORT_TEMPLATE block, but couldn't determine where it ends. Will not attempt to automatically add anything."
200
+ elsif import_template_block[2] # Already commented
201
+ is_add_cr = true
202
+ else # Needs to be commented
203
+ # Find what kind and how much indentation is present from the first commented line
204
+ indentation = import_template_block[3].first[0...(/\S/ =~ import_template_block[3].first)]
205
+ comments = import_template_block[3].map { |l| "#{l[0...indentation.length]}# #{l[indentation.length..-1]}" }
206
+ end
207
+ # else # Must be no IMPORT_TEMPLATE block yet
208
+ # insert_at = model_file.pos
209
+ end
210
+ if insert_at.nil?
211
+ puts "Please edit #{filename} manually and add this code:\n\n#{lines.join}"
212
+ else
213
+ is_good = ARGV[3]&.downcase&.start_with?('y')
214
+ args = [starting_name,
215
+ is_hm ? 'has_many' : 'no',
216
+ num_hops]
217
+ args << 'yes' if is_good
218
+ args = args.each_with_object(+'') do |v, s|
219
+ s << " #{v}"
220
+ s
221
+ end
222
+ lines.unshift("# Added #{DateTime.now.strftime('%b %d, %Y %I:%M%P')} by running `bin/rails g duty_free:model#{args}`\n")
223
+ # add a new one afterwards
224
+ print is_good ? 'Will' : 'OK to'
225
+ print "#{" comment #{comments.length} existing lines and" if comments} add #{lines.length} new lines to #{filename}"
226
+ puts is_good ? '.' : '?'
227
+ if is_good || gets_list(%w[Yes No]) == 'Yes'
228
+ # Store rest of file
229
+ model_file.pos = insert_at
230
+ rest_of_file = model_file.read
231
+ if comments
232
+ model_file.pos = import_template_block[0]
233
+ model_file.write("#{comments.join}\n")
234
+ puts "Commented #{comments.length} existing lines"
235
+ else
236
+ model_file.pos = insert_at
237
+ end
238
+ model_file.write("\n") if is_add_cr
239
+ model_file.write(lines.map { |l| "#{indentation}#{l}" }.join)
240
+ model_file.write(rest_of_file)
241
+ end
242
+ end
243
+ model_file.close
244
+ end
245
+
246
+ private
247
+
248
+ # def calc_num_hops(all, num = 0)
249
+ # max_num = num
250
+ # all.each do |item|
251
+ # if item.is_a?(Hash)
252
+ # item.each do |k, v|
253
+ # # puts "#{k} - #{num}"
254
+ # this_num = calc_num_hops(item[k], num + 1)
255
+ # max_num = this_num if this_num > max_num
256
+ # end
257
+ # end
258
+ # end
259
+ # max_num
260
+ # end
261
+
262
+ # Breadth first approach
263
+ def calc_num_hops(this_layer, models = nil)
264
+ seen_it = {}
265
+ layers = []
266
+ loop do
267
+ this_keys = []
268
+ next_layer = []
269
+ this_layer.each do |grouping|
270
+ klass = grouping.first
271
+ # binding.pry #unless klass.is_a?(Class)
272
+ grouping.last.each do |item|
273
+ next unless item.is_a?(Hash) && !seen_it.include?([klass, (k, v = item.first).first])
274
+
275
+ seen_it[[klass, k]] = nil
276
+ this_keys << [klass, k]
277
+ this_klass = klass.reflect_on_association(k)&.klass
278
+ if this_klass.nil? # Perhaps it's polymorphic
279
+ polymorphics = klass.reflect_on_all_associations.each_with_object([]) do |r, s|
280
+ prefix = "#{r.name}_"
281
+ if r.polymorphic? && k.to_s.start_with?(prefix)
282
+ suffix = k.to_s[prefix.length..-1]
283
+ possible_klass = models.find { |m| m.name.underscore == suffix }
284
+ s << [suffix, possible_klass] if possible_klass
285
+ end
286
+ s
287
+ end
288
+ # binding.pry if polymorphics.length != 1
289
+ this_klass = polymorphics.first&.last
290
+ end
291
+ next_layer << [this_klass, v.select { |ip| ip.is_a?(Hash) }] if this_klass
292
+ end
293
+ end
294
+ layers << this_keys unless this_keys.empty?
295
+ break if next_layer.empty?
296
+
297
+ this_layer = next_layer
298
+ end
299
+ # puts "#{k} - #{num}"
300
+ [layers, seen_it.keys.map(&:first).uniq.length]
301
+ end
302
+
303
+ # # MySQL 5.6 utf8mb4 limit is 191 chars for keys used in indexes.
304
+ # def item_type_options
305
+ # opt = { null: false }
306
+ # opt[:limit] = 191 if mysql?
307
+ # ", #{opt}"
308
+ # end
309
+
310
+ # def migration_version
311
+ # return unless (major = ActiveRecord::VERSION::MAJOR) >= 5
312
+
313
+ # "[#{major}.#{ActiveRecord::VERSION::MINOR}]"
314
+ # end
315
+
316
+ # # Class names of MySQL adapters.
317
+ # # - `MysqlAdapter` - Used by gems: `mysql`, `activerecord-jdbcmysql-adapter`.
318
+ # # - `Mysql2Adapter` - Used by `mysql2` gem.
319
+ # def mysql?
320
+ # [
321
+ # 'ActiveRecord::ConnectionAdapters::MysqlAdapter',
322
+ # 'ActiveRecord::ConnectionAdapters::Mysql2Adapter'
323
+ # ].freeze.include?(ActiveRecord::Base.connection.class.name)
324
+ # end
325
+
326
+ # # Even modern versions of MySQL still use `latin1` as the default character
327
+ # # encoding. Many users are not aware of this, and run into trouble when they
328
+ # # try to use DutyFree in apps that otherwise tend to use UTF-8. Postgres, by
329
+ # # comparison, uses UTF-8 except in the unusual case where the OS is configured
330
+ # # with a custom locale.
331
+ # #
332
+ # # - https://dev.mysql.com/doc/refman/5.7/en/charset-applications.html
333
+ # # - http://www.postgresql.org/docs/9.4/static/multibyte.html
334
+ # #
335
+ # # Furthermore, MySQL's original implementation of UTF-8 was flawed, and had
336
+ # # to be fixed later by introducing a new charset, `utf8mb4`.
337
+ # #
338
+ # # - https://mathiasbynens.be/notes/mysql-utf8mb4
339
+ # # - https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html
340
+ # #
341
+ # def versions_table_options
342
+ # if mysql?
343
+ # ', { options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci" }'
344
+ # else
345
+ # ''
346
+ # end
347
+ # end
348
+ end
349
+ end
@@ -1,5 +1,5 @@
1
- # This migration creates the `versions` table, the only schema PT requires.
2
- # All other migrations PT provides are optional.
1
+ # This migration creates the `versions` table, the only schema DF requires.
2
+ # All other migrations DF provides are optional.
3
3
  class CreateVersions < ActiveRecord::Migration<%= migration_version %>
4
4
 
5
5
  # The largest text column available in all supported RDBMS is
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: duty_free
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8
4
+ version: 1.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lorin Thwaits
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-06 00:00:00.000000000 Z
11
+ date: 2024-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -17,9 +17,6 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '3.0'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '6.2'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
@@ -27,9 +24,6 @@ dependencies:
27
24
  - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '3.0'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '6.2'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: appraisal
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -214,6 +208,7 @@ files:
214
208
  - lib/duty_free/version_number.rb
215
209
  - lib/generators/duty_free/USAGE
216
210
  - lib/generators/duty_free/install_generator.rb
211
+ - lib/generators/duty_free/model_generator.rb
217
212
  - lib/generators/duty_free/templates/add_object_changes_to_versions.rb.erb
218
213
  - lib/generators/duty_free/templates/create_versions.rb.erb
219
214
  homepage: https://github.com/lorint/duty_free
@@ -235,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
235
230
  - !ruby/object:Gem::Version
236
231
  version: 1.3.6
237
232
  requirements: []
238
- rubygems_version: 3.2.3
233
+ rubygems_version: 3.1.6
239
234
  signing_key:
240
235
  specification_version: 4
241
236
  summary: Import and Export Data