noid-rails 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +36 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +24 -0
  5. data/.rubocop_todo.yml +107 -0
  6. data/.travis.yml +11 -0
  7. data/CONTRIBUTING.md +159 -0
  8. data/Gemfile +39 -0
  9. data/LICENSE +15 -0
  10. data/README.md +214 -0
  11. data/Rakefile +23 -0
  12. data/app/models/minter_state.rb +16 -0
  13. data/db/migrate/20160610010003_create_minter_states.rb +16 -0
  14. data/db/migrate/20161021203429_rename_minter_state_random_to_rand.rb +7 -0
  15. data/lib/generators/noid/rails/install_generator.rb +22 -0
  16. data/lib/generators/noid/rails/seed_generator.rb +37 -0
  17. data/lib/noid/rails/config.rb +35 -0
  18. data/lib/noid/rails/engine.rb +10 -0
  19. data/lib/noid/rails/minter/base.rb +65 -0
  20. data/lib/noid/rails/minter/db.rb +82 -0
  21. data/lib/noid/rails/minter/file.rb +65 -0
  22. data/lib/noid/rails/minter.rb +5 -0
  23. data/lib/noid/rails/rspec.rb +67 -0
  24. data/lib/noid/rails/service.rb +26 -0
  25. data/lib/noid/rails/version.rb +7 -0
  26. data/lib/noid-rails.rb +29 -0
  27. data/lib/tasks/noid_tasks.rake +59 -0
  28. data/noid-rails.gemspec +31 -0
  29. data/spec/models/minter_state_spec.rb +41 -0
  30. data/spec/spec_helper.rb +40 -0
  31. data/spec/support/minterstate_table.rb +13 -0
  32. data/spec/support/shared_examples/minter.rb +39 -0
  33. data/spec/test_app_templates/lib/generators/test_app_generator.rb +16 -0
  34. data/spec/unit/config_spec.rb +45 -0
  35. data/spec/unit/db_minter_spec.rb +88 -0
  36. data/spec/unit/file_minter_spec.rb +54 -0
  37. data/spec/unit/noid_spec.rb +43 -0
  38. data/spec/unit/rspec_spec.rb +90 -0
  39. data/spec/unit/service_spec.rb +32 -0
  40. metadata +225 -0
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RenameMinterStateRandomToRand < ActiveRecord::Migration[4.2]
4
+ def change
5
+ rename_column :minter_states, :random, :rand
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noid
4
+ module Rails
5
+ # Generates the migrations into the host application
6
+ class InstallGenerator < ::Rails::Generators::Base
7
+ source_root ::File.expand_path('../templates', __FILE__)
8
+
9
+ desc <<~DESCRIPTION
10
+ Copies DB migrations
11
+ DESCRIPTION
12
+
13
+ def banner
14
+ say_status('info', 'Installing noid-rails', :blue)
15
+ end
16
+
17
+ def migrations
18
+ rake 'noid_rails_engine:install:migrations'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noid
4
+ module Rails
5
+ # Initializes the database with a noid namespace
6
+ class SeedGenerator < ::Rails::Generators::Base
7
+ source_root ::File.expand_path('../templates', __FILE__)
8
+ argument :namespace, type: :string, default: Noid::Rails.config.namespace
9
+ argument :template, type: :string, default: Noid::Rails.config.template
10
+
11
+ desc <<~DESCRIPTION
12
+ Seeds DB from Noid::Rails.config (or command-line overrides)
13
+ DESCRIPTION
14
+
15
+ def banner
16
+ say_status('info', "Initializing database table for namespace:template of '#{namespace}:#{template}'", :blue)
17
+ end
18
+
19
+ def checks
20
+ if namespace != Noid::Rails.config.namespace
21
+ say_status('warn', 'Be sure to use an initializer to do ' \
22
+ "'Noid::Rails.config.namespace = #{namespace}'", :red)
23
+ end
24
+ return if template == Noid::Rails.config.template
25
+ say_status('warn', 'Be sure to use an initializer to do ' \
26
+ "Noid::Rails.config.template = #{template}'", :red)
27
+ end
28
+
29
+ def seed_row
30
+ MinterState.seed!(
31
+ namespace: namespace,
32
+ template: template
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noid
4
+ module Rails
5
+ # Configuration parameters for creating identifiers
6
+ class Config
7
+ attr_writer :template, :statefile, :namespace, :minter_class, :identifier_in_use
8
+
9
+ def template
10
+ @template ||= '.reeddeeddk'
11
+ end
12
+
13
+ def statefile
14
+ @statefile ||= '/tmp/minter-state'
15
+ end
16
+
17
+ def namespace
18
+ @namespace ||= 'default'
19
+ end
20
+
21
+ def minter_class
22
+ @minter_class ||= Minter::File
23
+ end
24
+
25
+ # A check to guarantee the identifier is not already in use. When true,
26
+ # the minter will continue to cycle through ids until it finds one that
27
+ # returns false
28
+ def identifier_in_use
29
+ @identifier_in_use = lambda do |_id|
30
+ false
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ module Noid
6
+ module Rails
7
+ class Engine < ::Rails::Engine
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'noid'
4
+
5
+ module Noid
6
+ module Rails
7
+ module Minter
8
+ # @abstract the base class for minters
9
+ class Base < ::Noid::Minter
10
+ ##
11
+ # @param template [#to_s] a NOID template
12
+ # @see Noid::Template
13
+ def initialize(template = default_template)
14
+ super(template: template.to_s)
15
+ end
16
+
17
+ ##
18
+ # Sychronously mint a new identifier.
19
+ #
20
+ # @return [String] the minted identifier
21
+ def mint
22
+ Mutex.new.synchronize do
23
+ loop do
24
+ pid = next_id
25
+ return pid unless identifier_in_use?(pid)
26
+ end
27
+ end
28
+ end
29
+
30
+ ##
31
+ # @return [Hash{Symbol => String, Object}] representation of the current minter state
32
+ def read
33
+ raise NotImplementedError, 'Implement #read in child class'
34
+ end
35
+
36
+ ##
37
+ # Updates the minter state to that of the `minter` parameter.
38
+ #
39
+ # @param minter [Minter::Base]
40
+ # @return [void]
41
+ def write!(_)
42
+ raise NotImplementedError, 'Implement #write! in child class'
43
+ end
44
+
45
+ private
46
+
47
+ def identifier_in_use?(id)
48
+ Noid::Rails.config.identifier_in_use.call(id)
49
+ end
50
+
51
+ ##
52
+ # @return [#to_s] the default template for this
53
+ def default_template
54
+ Noid::Rails.config.template
55
+ end
56
+
57
+ ##
58
+ # @return [String] a new identifier
59
+ def next_id
60
+ raise NotImplementedError, 'Implement #next_id in child class'
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'noid'
4
+
5
+ module Noid
6
+ module Rails
7
+ module Minter
8
+ # A minter backed by a database table. You would select this if you
9
+ # need to mint identifers on several distributed front-ends that do not
10
+ # share a common file system.
11
+ class Db < Base
12
+ def read
13
+ deserialize(instance)
14
+ end
15
+
16
+ def write!(minter)
17
+ serialize(instance, minter)
18
+ end
19
+
20
+ protected
21
+
22
+ # @param [MinterState] inst minter state to be converted
23
+ # @return [Hash{Symbol => String, Object}] minter state as a Hash, like #read
24
+ # @see #read, Noid::Rails::Minter::Base#read
25
+ def deserialize(inst)
26
+ filtered_hash = inst.as_json.slice('template', 'counters', 'seq', 'rand', 'namespace')
27
+ if filtered_hash['counters']
28
+ filtered_hash['counters'] = JSON.parse(filtered_hash['counters'],
29
+ symbolize_names: true)
30
+ end
31
+ filtered_hash.symbolize_keys
32
+ end
33
+
34
+ # @param [MinterState] inst a locked row/object to be updated
35
+ # @param [::Noid::Minter] minter state containing the updates
36
+ def serialize(inst, minter)
37
+ # namespace and template are the same, now update the other attributes
38
+ inst.update_attributes!(
39
+ seq: minter.seq,
40
+ counters: JSON.generate(minter.counters),
41
+ rand: Marshal.dump(minter.instance_variable_get(:@rand))
42
+ )
43
+ end
44
+
45
+ # Uses pessimistic lock to ensure the record fetched is the same one updated.
46
+ # Should be fast enough to avoid terrible deadlock.
47
+ # Must lock because of multi-connection context! (transaction is per connection -- not enough)
48
+ # The DB table will only ever have at most one row per namespace.
49
+ # The 'default' namespace row is inserted by `rails generate noid:rails:seed`
50
+ # or autofilled by instance below.
51
+ # If you want another namespace, edit your config initialzer to something like:
52
+ # Noid::Rails.config.namespace = 'druid'
53
+ # Noid::Rails.config.template = '.reeedek'
54
+ # and in your app run:
55
+ # bundle exec rails generate noid:rails:seed
56
+ def next_id
57
+ id = nil
58
+ MinterState.transaction do
59
+ locked = instance
60
+ minter = ::Noid::Minter.new(deserialize(locked))
61
+ id = minter.mint
62
+ serialize(locked, minter)
63
+ end
64
+ id
65
+ end
66
+
67
+ # @return [MinterState]
68
+ def instance
69
+ MinterState.lock.find_by!(
70
+ namespace: Noid::Rails.config.namespace,
71
+ template: Noid::Rails.config.template
72
+ )
73
+ rescue ActiveRecord::RecordNotFound
74
+ MinterState.seed!(
75
+ namespace: Noid::Rails.config.namespace,
76
+ template: Noid::Rails.config.template
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'noid'
4
+
5
+ module Noid
6
+ module Rails
7
+ module Minter
8
+ # A file based minter. This is a simple case.
9
+ class File < Base
10
+ attr_reader :statefile
11
+
12
+ def initialize(template = default_template, statefile = default_statefile)
13
+ @statefile = statefile
14
+ super(template)
15
+ end
16
+
17
+ def default_statefile
18
+ Noid::Rails.config.statefile
19
+ end
20
+
21
+ def read
22
+ with_file do |f|
23
+ state_for(f)
24
+ end
25
+ end
26
+
27
+ def write!(minter)
28
+ with_file do |f|
29
+ # Wipe prior contents so the new state can be written from the beginning of the file
30
+ f.truncate(0)
31
+ f.write(Marshal.dump(minter.dump))
32
+ end
33
+ end
34
+
35
+ protected
36
+
37
+ def with_file
38
+ ::File.open(statefile, 'a+b', 0o644) do |f|
39
+ f.flock(::File::LOCK_EX)
40
+ # Files opened in append mode seek to end of file
41
+ f.rewind
42
+ yield f
43
+ end
44
+ end
45
+
46
+ # rubocop:disable Security/MarshalLoad
47
+ def state_for(io_object)
48
+ Marshal.load(io_object.read)
49
+ rescue TypeError, ArgumentError
50
+ { template: template }
51
+ end
52
+ # rubocop:enable Security/MarshalLoad
53
+
54
+ def next_id
55
+ state = read
56
+ state[:template] &&= state[:template].to_s
57
+ minter = ::Noid::Minter.new(state) # minter w/in the minter, lives only for an instant
58
+ id = minter.mint
59
+ write!(minter)
60
+ id
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'minter/base'
4
+ require_relative 'minter/file'
5
+ require_relative 'minter/db'
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noid
4
+ module Rails
5
+ ##
6
+ # Provides a test minter conforming to the `Noid::Rails::Minter`
7
+ # interface for use in unit tests. The test minter is faster and avoids
8
+ # unexpected interactions with cleanup code commonly runs in test suites
9
+ # (e.g. database cleanup).
10
+ #
11
+ # Applications should reenable their production minter for integration tests
12
+ # when appropriate
13
+ #
14
+ # @example general use
15
+ # Noid::Rails::RSpec.disable_production_minter!
16
+ # # some unit tests with the test minter
17
+ # Noid::Rails::RSpec.enable_production_minter!
18
+ # # some integration tests with the original minter
19
+ #
20
+ # @example using a custom test minter
21
+ # Noid::Rails::RSpec.disable_production_minter!(test_minter: Minter)
22
+ #
23
+ # @example use when included in RSpec config
24
+ # require 'noid/rails/rspec'
25
+ #
26
+ # RSpec.configure do |config|
27
+ # config.include(Noid::Rails::RSpec)
28
+ # end
29
+ #
30
+ # before(:suite) { disable_production_minter! }
31
+ # after(:suite) { enable_production_minter! }
32
+ #
33
+ module RSpec
34
+ DEFAULT_TEST_MINTER = Noid::Rails::Minter::File
35
+
36
+ ##
37
+ # Replaces the configured production minter with a test minter.
38
+ #
39
+ # @param test_minter [Class] a Noid::Rails::Minter implementation
40
+ # to use as a replacement minter
41
+ # @return [void]
42
+ def disable_production_minter!(test_minter: DEFAULT_TEST_MINTER)
43
+ return nil if @original_minter
44
+
45
+ @original_minter = Noid::Rails.config.minter_class
46
+
47
+ Noid::Rails.configure do |noid_config|
48
+ noid_config.minter_class = test_minter
49
+ end
50
+ end
51
+
52
+ ##
53
+ # Re-enables the original configured minter.
54
+ #
55
+ # @return [void]
56
+ def enable_production_minter!
57
+ return nil unless @original_minter
58
+
59
+ Noid::Rails.configure do |noid_config|
60
+ noid_config.minter_class = @original_minter
61
+ end
62
+
63
+ @original_minter = nil
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'noid'
4
+
5
+ module Noid
6
+ module Rails
7
+ # A service that validates and mints identifiers
8
+ class Service
9
+ attr_reader :minter
10
+
11
+ def initialize(minter = default_minter)
12
+ @minter = minter
13
+ end
14
+
15
+ delegate :valid?, to: :minter
16
+
17
+ delegate :mint, to: :minter
18
+
19
+ protected
20
+
21
+ def default_minter
22
+ Noid::Rails.config.minter_class.new
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noid
4
+ module Rails
5
+ VERSION = '3.0.0'
6
+ end
7
+ end
data/lib/noid-rails.rb ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'noid/rails/version'
4
+ require 'noid/rails/config'
5
+ require 'noid/rails/engine'
6
+ require 'noid/rails/service'
7
+ require 'noid/rails/minter'
8
+
9
+ module Noid
10
+ # A package to integrate Noid identifers with Rails projects
11
+ module Rails
12
+ class << self
13
+ def configure
14
+ yield config
15
+ end
16
+
17
+ def config
18
+ @config ||= Config.new
19
+ end
20
+
21
+ def treeify(identifier)
22
+ raise ArgumentError, 'Identifier must be a string of size > 0 in order to be treeified' if identifier.blank?
23
+ head = identifier.split('/').first
24
+ head.gsub!(/#.*/, '')
25
+ (head.scan(/..?/).first(4) + [identifier]).join('/')
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'noid-rails'
4
+ require 'noid'
5
+ require 'yaml'
6
+
7
+ namespace :noid do
8
+ namespace :rails do
9
+ namespace :migrate do
10
+ desc 'Migrate minter state file from YAML to Marshal'
11
+ task :yaml_to_marshal do
12
+ statefile = ENV.fetch('RAILS_NOID_STATEFILE', Noid::Rails.config.statefile)
13
+ raise "File not found: #{statefile}\nAborting" unless File.exist?(statefile)
14
+ puts "Migrating #{statefile} from YAML to Marshal serialization..."
15
+ File.open(statefile, 'a+b', 0o644) do |f|
16
+ f.flock(File::LOCK_EX)
17
+ f.rewind
18
+ begin
19
+ yaml_state = YAML.safe_load(f)
20
+ rescue Psych::SyntaxError
21
+ raise "File not valid YAML: #{statefile}\nAborting."
22
+ end
23
+ minter = Noid::Minter.new(yaml_state)
24
+ f.truncate(0)
25
+ new_state = Marshal.dump(minter.dump)
26
+ f.write(new_state)
27
+ end
28
+ puts 'Done!'
29
+ end
30
+
31
+ desc 'Migrate minter state from file to database'
32
+ task file_to_database: :environment do
33
+ statefile = ENV.fetch('RAILS_NOID_STATEFILE', Noid::Rails.config.statefile)
34
+ raise "File not found: #{statefile}\nAborting" unless File.exist?(statefile)
35
+ puts "Migrating #{statefile} to database..."
36
+ state = Noid::Rails::Minter::File.new.read
37
+ minter = Noid::Minter.new(state)
38
+ new_state = Noid::Rails::Minter::Db.new
39
+ new_state.write!(minter)
40
+ puts 'Done!'
41
+ end
42
+
43
+ desc 'Migrate minter state from database to file'
44
+ task database_to_file: :environment do
45
+ statefile = ENV.fetch('RAILS_NOID_STATEFILE', Noid::Rails.config.statefile)
46
+ if File.exist?(statefile)
47
+ raise "File already exists (delete it first if it's not valuable): " \
48
+ "#{statefile}\nAborting"
49
+ end
50
+ puts "Migrating minter state from database to #{statefile}..."
51
+ state = Noid::Rails::Minter::Db.new.read
52
+ minter = Noid::Minter.new(state)
53
+ new_state = Noid::Rails::Minter::File.new
54
+ new_state.write!(minter)
55
+ puts 'Done!'
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'noid/rails/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'noid-rails'
9
+ spec.version = Noid::Rails::VERSION
10
+ spec.authors = ['Michael J. Giarlo']
11
+ spec.email = ['leftwing@alumni.rutgers.edu']
12
+ spec.summary = 'Noid identifier services for Rails-based applications'
13
+ spec.description = 'Noid identifier services for Rails-based applications.'
14
+ spec.homepage = 'https://github.com/samvera/noid-rails'
15
+ spec.license = 'Apache2'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.test_files = spec.files.grep(%r{^spec/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'actionpack', '>= 5.0.0', '< 6'
22
+ spec.add_dependency 'noid', '~> 0.9'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.7'
25
+ spec.add_development_dependency 'engine_cart', '~> 1.0'
26
+ spec.add_development_dependency 'rake', '>= 11'
27
+ spec.add_development_dependency 'rspec', '~> 3.2'
28
+ spec.add_development_dependency 'rubocop', '~> 0.52.0'
29
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.20.1'
30
+ spec.add_development_dependency 'sqlite3'
31
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe MinterState, type: :model do
4
+ include MinterStateHelper
5
+ before { reset_minter_state_table }
6
+ after { reset_minter_state_table }
7
+
8
+ let(:state) { described_class.new }
9
+ let(:first) { described_class.first }
10
+
11
+ it 'db is seeded with first row' do
12
+ expect { first }.not_to raise_error
13
+ expect(first.namespace).to eq 'default'
14
+ expect(first.template).to eq '.reeddeeddk'
15
+ expect(first.seq).to eq 0
16
+ expect(described_class.group(:namespace).count).to eq('default' => 1)
17
+ end
18
+ describe 'validation' do
19
+ it 'blocks invalid template' do
20
+ expect { state.save! }.to raise_error(ActiveRecord::RecordInvalid) # empty
21
+ state.template = 'bad_template'
22
+ expect { state.save! }.to raise_error(ActiveRecord::RecordInvalid)
23
+ state.template = 'reeddddk' # close, but missing '.'
24
+ expect { state.save! }.to raise_error(ActiveRecord::RecordInvalid)
25
+ end
26
+ it 'allows valid template (edit)' do
27
+ first.template = '.reeddddk'
28
+ expect { first.save! }.not_to raise_error # OK!
29
+ end
30
+ it 'blocks new record in same namespace' do
31
+ state.template = '.reeddddk'
32
+ expect { state.save! }.to raise_error(ActiveRecord::RecordInvalid)
33
+ end
34
+ it 'allows new record in distinct namespace' do
35
+ state.template = '.reeddddk'
36
+ state.namespace = 'foobar'
37
+ expect { state.save! }.not_to raise_error # OK!
38
+ expect(described_class.group(:namespace).count).to eq('default' => 1, 'foobar' => 1)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV['RAILS_ENV'] ||= 'test'
4
+
5
+ def coverage_needed?
6
+ ENV['COVERAGE'] || ENV['TRAVIS']
7
+ end
8
+
9
+ if coverage_needed?
10
+ require 'simplecov'
11
+ require 'coveralls'
12
+ SimpleCov.root(File.expand_path('../..', __FILE__))
13
+ SimpleCov.formatter = Coveralls::SimpleCov::Formatter
14
+ SimpleCov.start('rails') do
15
+ add_filter '/.internal_test_app'
16
+ add_filter '/lib/generators'
17
+ add_filter '/spec'
18
+ add_filter '/lib/noid/rails/version.rb'
19
+ end
20
+ SimpleCov.command_name 'spec'
21
+ end
22
+
23
+ require 'engine_cart'
24
+ EngineCart.load_application!
25
+
26
+ require 'noid-rails'
27
+ require 'byebug' unless ENV['CI']
28
+
29
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each { |f| require f }
30
+
31
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
32
+ RSpec.configure do |config|
33
+ config.expect_with :rspec do |expectations|
34
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
+ end
36
+ config.mock_with :rspec do |mocks|
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+ config.filter_run_when_matching :focus
40
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MinterStateHelper
4
+ # Simple truncation is not enough, since we also need seed data
5
+ def reset_minter_state_table
6
+ MinterState.destroy_all
7
+ MinterState.create!(
8
+ namespace: 'default',
9
+ template: '.reeddeeddk',
10
+ seq: 0
11
+ )
12
+ end
13
+ end