gs1 1.1.0 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16ce4a66d335b1d7fa0ac83b3fdc3064390b0a3ffe74d09734b9876cdd305449
4
- data.tar.gz: ab4df39306570b00b5a0da26030f7c8011358f359e86c51920290e1d645685b6
3
+ metadata.gz: 8e902d0a871c447d704c90970bb54e21cb6608a34ef77ef8fca7515b605bd833
4
+ data.tar.gz: 874725a50d5a89582e06f2bee8adce07a98f7583c79fd10026fb75cd75fa8e1b
5
5
  SHA512:
6
- metadata.gz: 7615850e8245725560bf4af80f68d107292a83c24e9c6b0e3c6a0f7042e2c632c23664cc37f947ed46332aada6dc3ef496319c5017bd07d5a7bc05cbca87002c
7
- data.tar.gz: 0c7196620e4531ee06a32944a9cf52a9b2fa84f1012ca27210ba9084e7ed01f8edbeca4facd1aea3e487cfa368b8b2fb2434d3fecded4cc10f7013a7b9960d61
6
+ metadata.gz: 876b0b4d0923c166a38affb96b274f9fe799fddf15d0ac75fdd472e2cc02907816e336fb300e2881ecdf191f4a02599b44abf2f4c9f46f3a8c878a8204a4de21
7
+ data.tar.gz: 50bb2bcd58750950edd5bc2dcfc512d4c9e09af725809a467f3432ab7a69b754d167930111c73d6d194f74a886def551b8e350516e3e93c13d0015b37eca9cc7
data/CHANGELOG.md CHANGED
@@ -1,15 +1,54 @@
1
- ### 1.1.0 - 2021-08-23
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
2
3
 
3
- * enhancements
4
- * Adds support to calculating checksum for GSIN
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
6
 
6
- ### 1.0.0 - 2019-08-30
7
+ ## [Unreleased]
7
8
 
8
- * breaking
9
- * Returns `nil` unless no attribute for barcode segment
9
+ ## [2.0.0] - 2025-04-22
10
+ ### Changed
11
+ - Minimum supported version of Ruby is now 3.0.5 (breaking change)
10
12
 
11
- * enhancements
12
- * Add errors to a barcode
13
+ - Extra elements in barcodes are now ignored (breaking change). Set the
14
+ `ignore_extra_barcode_elements` configuration option to `false` to restore
15
+ previous behavior
13
16
 
14
- * bug fixes
15
- * Fix `nil` argument for barcode
17
+ - AI classes are lazy loaded now (possibly breaking change)
18
+
19
+ ### Added
20
+ - Add a new option (`ignore_extra_barcode_elements`) to configure if extra
21
+ elements in barcodes should be reported as an error or ignored. By default,
22
+ ignore
23
+
24
+ - Added the `GS1.ai_classes` method. This contains all AI classes. This is the
25
+ same as `GS1::AI_CLASSES` from previous versions, buy lazy loaded
26
+
27
+ - All application identifiers (as of 2025-01-30) are now recognized
28
+
29
+ ### Removed
30
+ - Removed the `GS1::AI_CLASSES` constant. Use `GS1.ai_classes` instead
31
+ - The following classes have been removed: `GS1::Batch` and `GS1::SerialNumber`.
32
+ They have been replaced with generated anonymous classes.
33
+
34
+ ## [1.1.0] - 2021-08-23
35
+
36
+ ### Added
37
+ - Adds support to calculating checksum for GSIN
38
+
39
+ ## [1.0.0] - 2019-08-30
40
+
41
+ ### Changed
42
+ - Returns `nil` unless no attribute for barcode segment (breaking change)
43
+
44
+ ### Added
45
+ - Add errors to a barcode
46
+
47
+ ### Fixed
48
+ - Fix `nil` argument for barcode
49
+
50
+ [Unreleased]: https://github.com/apoex/gs1/compare/v2.0.0...HEAD
51
+
52
+ [2.0.0]: https://github.com/apoex/gs1/compare/v1.1.0...v2.0.0
53
+ [1.1.0]: https://github.com/apoex/gs1/compare/v1.0.0...v1.1.0
54
+ [1.0.0]: https://github.com/apoex/gs1/releases/tag/v1.0.0
data/README.md CHANGED
@@ -30,6 +30,7 @@ There are some configuration you can do before start using this lib.
30
30
  GS1.configure do |config|
31
31
  config.company_prefix = '123456789'
32
32
  config.barcode_separator = '~' # Default is "\u001E"
33
+ config.ignore_extra_barcode_elements = false # Default is true
33
34
  end
34
35
  ```
35
36
 
@@ -77,18 +78,79 @@ GS1::Barcode::Healthcare.from_scan("01034531200000111719112510ABCD1234\u001E2110
77
78
 
78
79
  ## Development
79
80
 
80
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
81
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
82
+ `rake spec` to run the tests. You can also run `bin/console` for an interactive
83
+ prompt that will allow you to experiment.
81
84
 
82
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
85
+ To install this gem onto your local machine, run `bundle exec rake install`. To
86
+ release a new version, update the version number in `version.rb`, and then run
87
+ `bundle exec rake release`, which will create a git tag for the version, push
88
+ git commits and tags, and push the `.gem` file to
89
+ [rubygems.org](https://rubygems.org).
90
+
91
+ ### Creating a Release
92
+
93
+ #### Update the Changelog
94
+
95
+ 1. Rename the [Unreleased] section to match the version that is being released
96
+ 1. Add the current date in the section from previous step. See existing entries
97
+ 1. Add a new section for the `Unreleased` section at the top
98
+ 1. Add a new reference link for the new section at the bottom of the changelog
99
+ 1. Update the `Unreleased` reference link at the bottom of the changelog
100
+
101
+ #### Publish New Version of Gem
102
+
103
+ To publish a new version of this gem, follow the steps below:
104
+
105
+ 1. Update the version in the [version.rb](lib/gs1/version.rb) file
106
+ 1. Run `bundle` to update Gemfile.lock
107
+ 1. Commit and push the changes
108
+
109
+ 1. Run `rake release:initiate`. This initiates a new release by creating a Git
110
+ tag with the version specified in [version.rb](lib/gs1/version.rb). It will
111
+ then push the Git tag. This will trigger a job in the CI pipeline, which
112
+ will build the gem and publish it to https://rubygems.org. It will also
113
+ create a GitHub Release, in draft mode
114
+
115
+ 1. Check the GitHub Release and then publish it
83
116
 
84
117
  ## Contributing
85
118
 
86
- Bug reports and pull requests are welcome on GitHub at https://github.com/apoex/gs1. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
119
+ Bug reports and pull requests are welcome on GitHub at
120
+ https://github.com/apoex/gs1. This project is intended to be a safe, welcoming
121
+ space for collaboration, and contributors are expected to adhere to the
122
+ [Contributor Covenant](http://contributor-covenant.org) code of conduct.
123
+
124
+ ### Changelog
125
+
126
+ The changelog is maintained in the [CHANGELOG.md](CHANGELOG.md) file, following
127
+ the [Keep a Changelog] format. The changelog is updated incrementally. That is,
128
+ for every new feature or bugfix, add an entry to the changelog. New entries are
129
+ added below the [Unreleased] section, with an appropriate sub header.
130
+
131
+ ### Update Known Application Identifiers
132
+
133
+ 1. Download the latest version of the [GS1 syntax dictionary](gs1_syntax_dictionary)
134
+
135
+ 1. Place the syntax dictionary in the
136
+ [gs1-syntax-dictionary.txt](gs1-syntax-dictionary.txt) file
137
+
138
+ 1. Run the script to generate the AI classes from the syntax
139
+ dictionary: [`./bin/generate_ai_classes`](bin/generate_ai_classes)
140
+
141
+ 1. Commit all changed files
87
142
 
88
143
  ## License
89
144
 
90
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
145
+ The gem is available as open source under the terms of the [MIT
146
+ License](https://opensource.org/licenses/MIT).
91
147
 
92
148
  ## Code of Conduct
93
149
 
94
- Everyone interacting in the Gs1 project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/apoex/gs1/blob/master/CODE_OF_CONDUCT.md).
150
+ Everyone interacting in the Gs1 project’s codebases, issue trackers, chat rooms
151
+ and mailing lists is expected to follow the [code of
152
+ conduct](https://github.com/apoex/gs1/blob/master/CODE_OF_CONDUCT.md).
153
+
154
+ [gs1_syntax_dictionary]: https://ref.gs1.org/tools/gs1-barcode-syntax-resource/syntax-dictionary
155
+ [Keep a Changelog]: https://keepachangelog.com/en/1.1.0
156
+ [Unreleased]: https://github.com/apoex/gs1/blob/master/CHANGELOG.md#unreleased
data/gs1.gemspec CHANGED
@@ -19,24 +19,21 @@ Gem::Specification.new do |spec|
19
19
  spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
20
  else
21
21
  raise 'RubyGems 2.0 or newer is required to protect against ' \
22
- 'public gem pushes.'
22
+ 'public gem pushes.'
23
23
  end
24
24
 
25
25
  # Specify which files should be added to the gem when it is released.
26
26
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
27
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ `git ls-files -z`
29
+ .split("\x0")
30
+ .filter { _1.start_with?('lib') } +
31
+ %w[gs1.gemspec CHANGELOG.md CODE_OF_CONDUCT.md LICENSE.txt README.md]
29
32
  end
30
33
  spec.bindir = 'exe'
31
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
35
  spec.require_paths = ['lib']
33
- spec.required_ruby_version = '>= 2.3.0'
36
+ spec.required_ruby_version = '>= 3.0.5'
34
37
 
35
- spec.add_development_dependency 'bundler'
36
- spec.add_development_dependency 'byebug', '~> 10.0'
37
- spec.add_development_dependency 'rake', '>= 10.0'
38
- spec.add_development_dependency 'rb-readline', '~> 0.1'
39
- spec.add_development_dependency 'rspec', '~> 3.0'
40
- spec.add_development_dependency 'rubocop', '~> 0.49.0'
41
- spec.add_development_dependency 'simplecov', '~> 0.1'
38
+ spec.metadata['rubygems_mfa_required'] = 'true'
42
39
  end
@@ -0,0 +1,33 @@
1
+ module GS1
2
+ module Barcode
3
+ class AttributeValidator
4
+ def self.for(configuration:)
5
+ attribute_record_validator = if configuration.ignore_extra_barcode_elements
6
+ AttributeValidators::IgnoringRecordValidator
7
+ else
8
+ AttributeValidators::RecordValidator
9
+ end
10
+
11
+ new(attribute_record_validator: attribute_record_validator.new)
12
+ end
13
+
14
+ def initialize(attribute_record_validator: AttributeValidators::IgnoringRecordValidator.new)
15
+ @attribute_record_validator = attribute_record_validator
16
+ end
17
+
18
+ def validate_data(barcode, attribute_name)
19
+ return unless barcode.instance_variable_get("@#{attribute_name}")
20
+
21
+ barcode.errors[attribute_name] << Error.new(:already_defined, persistent: true)
22
+ end
23
+
24
+ def validate_record(barcode, attribute_name, &block)
25
+ attribute_record_validator.validate(barcode, attribute_name, &block)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :attribute_record_validator
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ module GS1
2
+ module Barcode
3
+ module AttributeValidators
4
+ class IgnoringRecordValidator < RecordValidator
5
+ private
6
+
7
+ def on_error(_barcode, _attribute_name); end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module GS1
2
+ module Barcode
3
+ module AttributeValidators
4
+ class RecordValidator
5
+ def validate(barcode, attribute_name)
6
+ barcode.class.records.find { |r| r.underscore_name == attribute_name }.tap do |record|
7
+ if record
8
+ yield record
9
+ next
10
+ end
11
+
12
+ on_error(barcode, attribute_name)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def on_error(barcode, attribute_name)
19
+ barcode.errors[attribute_name] << Error.new(:unknown_attribute, persistent: true)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -5,15 +5,16 @@ module GS1
5
5
  class Base
6
6
  include Definitions
7
7
 
8
- def initialize(options = {})
9
- options.each do |(attribute_name, data)|
10
- validate_attribute_data(attribute_name)
11
- validate_attribute_record(attribute_name) do |record|
12
- instance_variable_set("@#{attribute_name}", record.new(data))
8
+ def initialize(attributes = {}, options = {})
9
+ attribute_validator = options[:attribute_validator] || AttributeValidator.new
10
+ attributes.each do |(name, data)|
11
+ attribute_validator.validate_data(self, name)
12
+ attribute_validator.validate_record(self, name) do |record|
13
+ instance_variable_set("@#{name}", record.new(data))
13
14
  end
14
15
  end
15
16
 
16
- @params_order = options.to_h.keys
17
+ @params_order = attributes.to_h.keys
17
18
  end
18
19
 
19
20
  def errors
@@ -21,20 +22,33 @@ module GS1
21
22
  end
22
23
 
23
24
  class << self
24
- def from_scan!(barcode, separator: GS1.configuration.barcode_separator)
25
- new(scan_to_params!(barcode, separator: separator))
25
+ def for(attributes, configuration:)
26
+ attribute_validator = AttributeValidator.for(configuration: configuration)
27
+ new(attributes, attribute_validator: attribute_validator)
26
28
  end
27
29
 
28
- def from_scan(barcode, separator: GS1.configuration.barcode_separator)
29
- new(scan_to_params(barcode, separator: separator))
30
+ def from_scan!(barcode, separator: GS1.configuration.barcode_separator, ai_classes: GS1.ai_classes)
31
+ self.for(
32
+ scan_to_params!(barcode, separator: separator, ai_classes: ai_classes),
33
+ configuration: GS1.configuration
34
+ )
30
35
  end
31
36
 
32
- def scan_to_params!(barcode, separator: GS1.configuration.barcode_separator)
33
- Tokenizer.new(barcode, separator: separator).to_params!
37
+ def from_scan(barcode, separator: GS1.configuration.barcode_separator, ai_classes: GS1.ai_classes)
38
+ self.for(
39
+ scan_to_params(barcode, separator: separator, ai_classes: ai_classes),
40
+ configuration: GS1.configuration
41
+ )
34
42
  end
35
43
 
36
- def scan_to_params(barcode, separator: GS1.configuration.barcode_separator)
37
- Tokenizer.new(barcode, separator: separator).to_params
44
+ def scan_to_params!(barcode, separator: GS1.configuration.barcode_separator, ai_classes: GS1.ai_classes)
45
+ Tokenizer.new(barcode, separator: separator, ai_classes: ai_classes)
46
+ .to_params!
47
+ end
48
+
49
+ def scan_to_params(barcode, separator: GS1.configuration.barcode_separator, ai_classes: GS1.ai_classes)
50
+ Tokenizer.new(barcode, separator: separator, ai_classes: ai_classes)
51
+ .to_params
38
52
  end
39
53
  end
40
54
  end
@@ -5,7 +5,6 @@ module GS1
5
5
  module Definitions
6
6
  def self.included(base)
7
7
  base.extend ClassMethods
8
- base.send :include, InstanceMethods
9
8
  end
10
9
 
11
10
  # Adding defintion class methods.
@@ -22,27 +21,6 @@ module GS1
22
21
  end
23
22
  end
24
23
  end
25
-
26
- # Adding defintion instance methods.
27
- #
28
- module InstanceMethods
29
- def validate_attribute_data(attribute_name)
30
- return unless instance_variable_get("@#{attribute_name}")
31
-
32
- errors[attribute_name] << Error.new(:already_defined, persistent: true)
33
- end
34
-
35
- def validate_attribute_record(attribute_name)
36
- self.class.records.find { |r| r.underscore_name == attribute_name }.tap do |record|
37
- if record
38
- yield record
39
- next
40
- end
41
-
42
- errors[attribute_name] << Error.new(:unknown_attribute, persistent: true)
43
- end
44
- end
45
- end
46
24
  end
47
25
  end
48
26
  end
@@ -24,8 +24,8 @@ module GS1
24
24
  end
25
25
 
26
26
  def messages
27
- errors.each_with_object({}) do |(attribute_name, errors), hash|
28
- hash[attribute_name] = errors.uniq.map(&:human_message)
27
+ errors.transform_values do |errors|
28
+ errors.uniq.map(&:human_message)
29
29
  end
30
30
  end
31
31
 
@@ -57,7 +57,7 @@ module GS1
57
57
  attribute = public_send(attribute_name)
58
58
 
59
59
  if attribute.nil?
60
- errors[attribute_name] << Error.new(:blank)
60
+ errors[attribute_name] << Error.new(:missing)
61
61
  elsif !attribute.valid?
62
62
  errors[attribute_name] << Error.new(:invalid)
63
63
  end
@@ -5,9 +5,10 @@ module GS1
5
5
  class Segment
6
6
  attr_reader :data, :separator
7
7
 
8
- def initialize(data, separator: GS1.configuration.barcode_separator)
8
+ def initialize(data, separator: GS1.configuration.barcode_separator, ai_classes: GS1.ai_classes)
9
9
  @data = data.to_s.chars
10
10
  @separator = separator
11
+ @ai_classes = ai_classes
11
12
  end
12
13
 
13
14
  def to_params
@@ -66,6 +67,8 @@ module GS1
66
67
 
67
68
  private
68
69
 
70
+ attr_reader :ai_classes
71
+
69
72
  def ai_variants
70
73
  @ai_variants ||= []
71
74
  end
@@ -73,9 +76,9 @@ module GS1
73
76
  def process_ai_variants(shifts)
74
77
  return unless can_shift_ai_data?(shifts)
75
78
 
76
- ai_variants << ai_variants.last.to_s + data.shift(shifts).join
79
+ ai_variants << (ai_variants.last.to_s + data.shift(shifts).join)
77
80
 
78
- AI_CLASSES[ai_variants.last]
81
+ ai_classes[ai_variants.last]
79
82
  end
80
83
 
81
84
  def can_shift_ai_data?(shifts)
@@ -6,10 +6,11 @@ module GS1
6
6
  class Tokenizer
7
7
  attr_reader :data, :separator, :params
8
8
 
9
- def initialize(data, separator: GS1.configuration.barcode_separator)
9
+ def initialize(data, separator: GS1.configuration.barcode_separator, ai_classes: GS1.ai_classes)
10
10
  @data = data
11
11
  @separator = separator
12
12
  @params = []
13
+ @ai_classes = ai_classes
13
14
  end
14
15
 
15
16
  def to_params
@@ -22,7 +23,11 @@ module GS1
22
23
 
23
24
  private
24
25
 
26
+ attr_reader :ai_classes
27
+
28
+ # rubocop:disable Style/OptionalBooleanParameter
25
29
  def segment_to_params(input, bang = false)
30
+ # rubocop:enable Style/OptionalBooleanParameter
26
31
  segment_from_input(input, bang) do |segment|
27
32
  next if segment.to_params.empty?
28
33
 
@@ -37,7 +42,7 @@ module GS1
37
42
  end
38
43
 
39
44
  def segment_from_input(input, bang)
40
- Segment.new(input, separator: separator).tap do |segment|
45
+ Segment.new(input, separator: separator, ai_classes: ai_classes).tap do |segment|
41
46
  segment.validate! if bang
42
47
  yield segment if block_given?
43
48
  end
data/lib/gs1/barcode.rb CHANGED
@@ -1,3 +1,6 @@
1
+ require 'gs1/barcode/attribute_validator'
2
+ require 'gs1/barcode/attribute_validators/record_validator'
3
+ require 'gs1/barcode/attribute_validators/ignoring_record_validator'
1
4
  require 'gs1/barcode/definitions'
2
5
  require 'gs1/barcode/error'
3
6
  require 'gs1/barcode/errors'
@@ -14,7 +14,7 @@ module GS1
14
14
  if date.respond_to?(:strftime)
15
15
  super(date.strftime('%y%m%d'))
16
16
  else
17
- super(date)
17
+ super
18
18
  end
19
19
  end
20
20
 
@@ -34,7 +34,7 @@ module GS1
34
34
  if date.respond_to?(:strftime)
35
35
  super(date.strftime('%y%m%d'))
36
36
  else
37
- super(date)
37
+ super
38
38
  end
39
39
  end
40
40