gs1 0.1.5 → 0.1.6

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: e9fad86db4d7ac36e936b37b63976378628f9b8d
4
- data.tar.gz: 1eeca0b54c00df6f1ff54108ad5aa799477c1df5
3
+ metadata.gz: 2f15d386486f27cc92e1ba01d9c3d03f0633c7ac
4
+ data.tar.gz: e4f0b5717132652127aafeeccdda4253d68a8201
5
5
  SHA512:
6
- metadata.gz: 6b2a4ec9172a6c31a435c8feb3a2e73a03e134269d1bf9f8fc2227a2074a404b06893e47f1053f91c17f497f4ed145385007b825c2b8f5e314a8f129a40dae5f
7
- data.tar.gz: 84e62c678aa22a165c63ccbb31ac89a69ce507ad49193127cd035f42482c86398fc5e14dc35b2d6518820750e97e9804a3e0227add72354d2bab4841780eef43
6
+ metadata.gz: 3d3f0d9472329eafe49f3bdbf6326672c442d74b8132f07d3a21d0083d7d617fd1309af82d24ba78bd3585d61b7f6daf5ecec8fcad5f72595a29d2024afdea16
7
+ data.tar.gz: 03d8e6d2faf2249b3077c38d67d90535f6a82d6d2fb1aec42c71e90cf9054497ab122a89014478840420c23f0c969ea1fa2c9316ebd933e623c5d395c5521c64
data/README.md CHANGED
@@ -22,6 +22,17 @@ Or install it yourself as:
22
22
 
23
23
  Follow the examples below to start using the gem.
24
24
 
25
+ ### Configuration
26
+
27
+ There are some configuration you can do before start using this lib.
28
+
29
+ ```ruby
30
+ GS1.configure do |config|
31
+ config.company_prefix = '123456789'
32
+ config.barcode_separator = '~' # Default is "\u001E"
33
+ end
34
+ ```
35
+
25
36
  ### Application identifiers
26
37
 
27
38
  To access the defined application identifier:
@@ -18,19 +18,19 @@ module GS1
18
18
  end
19
19
 
20
20
  class << self
21
- def from_scan!(barcode, separator: DEFAULT_SEPARATOR)
21
+ def from_scan!(barcode, separator: GS1.configuration.barcode_separator)
22
22
  new(scan_to_params!(barcode, separator: separator))
23
23
  end
24
24
 
25
- def from_scan(barcode, separator: DEFAULT_SEPARATOR)
25
+ def from_scan(barcode, separator: GS1.configuration.barcode_separator)
26
26
  new(scan_to_params(barcode, separator: separator))
27
27
  end
28
28
 
29
- def scan_to_params!(barcode, separator: DEFAULT_SEPARATOR)
29
+ def scan_to_params!(barcode, separator: GS1.configuration.barcode_separator)
30
30
  Tokenizer.new(barcode, separator: separator).to_params!
31
31
  end
32
32
 
33
- def scan_to_params(barcode, separator: DEFAULT_SEPARATOR)
33
+ def scan_to_params(barcode, separator: GS1.configuration.barcode_separator)
34
34
  Tokenizer.new(barcode, separator: separator).to_params
35
35
  end
36
36
  end
@@ -5,7 +5,7 @@ module GS1
5
5
  class Healthcare < Base
6
6
  define_records GTIN, ExpirationDate, Batch, SerialNumber
7
7
 
8
- def to_s(level: AIDCMarketingLevels::ENHANCED, separator: DEFAULT_SEPARATOR)
8
+ def to_s(level: AIDCMarketingLevels::ENHANCED, separator: GS1.configuration.barcode_separator)
9
9
  return unless valid?(level: level)
10
10
 
11
11
  [
@@ -5,7 +5,7 @@ module GS1
5
5
  class Segment
6
6
  attr_reader :data, :separator
7
7
 
8
- def initialize(data, separator: DEFAULT_SEPARATOR)
8
+ def initialize(data, separator: GS1.configuration.barcode_separator)
9
9
  @data = data.chars
10
10
  @separator = separator
11
11
  end
@@ -6,7 +6,7 @@ module GS1
6
6
  class Tokenizer
7
7
  attr_reader :data, :separator, :params
8
8
 
9
- def initialize(data, separator: DEFAULT_SEPARATOR)
9
+ def initialize(data, separator: GS1.configuration.barcode_separator)
10
10
  @data = data
11
11
  @separator = separator
12
12
  @params = {}
@@ -15,7 +15,7 @@ module GS1
15
15
  module ClassMethods
16
16
  attr_reader :definitions
17
17
 
18
- DEFINITIONS = %i[check_digit date length].freeze
18
+ DEFINITIONS = %i[check_digit date date_month_based length].freeze
19
19
 
20
20
  def define(key, options = {})
21
21
  raise UnknownDefinition, "#{key} is not a valid definition" unless DEFINITIONS.include?(key)
@@ -35,6 +35,11 @@ module GS1
35
35
  {}
36
36
  end
37
37
 
38
+ # Currently no support for options.
39
+ def normalize_date_month_based_options(_options)
40
+ {}
41
+ end
42
+
38
43
  # Defaults barcode length to allowed length if not explicitly defined, only
39
44
  # if there is one significant allowed.
40
45
  def normalize_length_options(options)
@@ -48,7 +48,7 @@ module GS1
48
48
  # Source: https://www.gs1.org/sites/default/files/docs/barcodes/GS1_General_Specifications.pdf
49
49
  #
50
50
  class ExpirationDate < Record
51
- include Extensions::Date
51
+ include Extensions::DateMonthBased
52
52
 
53
53
  AI = AI::USE_BY
54
54
  end
@@ -0,0 +1,60 @@
1
+ require 'date'
2
+
3
+ module GS1
4
+ module Extensions
5
+ # Extension for a GS1 date. Ensures correct formating and validation.
6
+ #
7
+ # OBS! Month-based expiry
8
+ #
9
+ # Expiry dates for a batch of medicinal product are generally set by month, rather than day. If a batch
10
+ # expires in March 2021, for example, it expires at the very end of the month. From the 1st April 2021, the EMVS
11
+ # will report that the pack identifier has expired if it has not already been decommissioned for some other reason.
12
+ #
13
+ # Manufacturers represent expiry by month in one of two ways. They either set the expiry date to be the last day of
14
+ # the month or they set the day component of the date (DD) to '00'. Examples are shown below:
15
+ #
16
+ # Format A: 210331
17
+ # Format B: 210300
18
+ #
19
+ # These two formats effectively indicate the same expiry date. The first specifies a specific day (the last day of
20
+ # the month). The second specifies only the month of expiry.
21
+ #
22
+ # The format used in the barcodes must be exactly the same as the format used within the EMVS. If the manufacturer
23
+ # uploads batch data to the European Hub using one format, but prints the barcodes using the other, the dates will
24
+ # not match and the national system will raise 'potential falsified medicine' alerts. In this case, the packs
25
+ # cannot be supplied until the manufacturer corrects the data within the EMVS.
26
+ #
27
+ module DateMonthBased
28
+ def self.included(base)
29
+ base.define :date_month_based
30
+ base.define :length, barcode: 6
31
+ end
32
+
33
+ def initialize(date)
34
+ if date.respond_to?(:strftime)
35
+ super(date.strftime('%y%m%d'))
36
+ else
37
+ super(date)
38
+ end
39
+ end
40
+
41
+ def to_date
42
+ to_date_format_a || to_date_format_b
43
+ end
44
+
45
+ private
46
+
47
+ def to_date_format_a
48
+ ::Date.strptime(data, '%y%m%d')
49
+ rescue TypeError, ArgumentError
50
+ nil
51
+ end
52
+
53
+ def to_date_format_b
54
+ ::Date.strptime(data, '%y%m00').next_month.prev_day
55
+ rescue TypeError, ArgumentError
56
+ nil
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,2 +1,3 @@
1
1
  require 'gs1/extensions/date'
2
+ require 'gs1/extensions/date_month_based'
2
3
  require 'gs1/extensions/gtin'
@@ -9,6 +9,10 @@ module GS1
9
9
  errors << 'Invalid date' unless valid_date?
10
10
  end
11
11
 
12
+ def validate_date_month_based(_options = {})
13
+ errors << 'Invalid date' unless valid_month_based_date?
14
+ end
15
+
12
16
  def valid_date?
13
17
  return true if data.is_a?(::Date)
14
18
 
@@ -18,6 +22,31 @@ module GS1
18
22
  rescue TypeError, ArgumentError
19
23
  false
20
24
  end
25
+
26
+ def valid_month_based_date?
27
+ return true if data.is_a?(::Date)
28
+
29
+ valid_format_a?(data) || valid_format_b?(data)
30
+ end
31
+
32
+ def valid_format_a?(data)
33
+ valid_date_format?(data, '%y%m%d')
34
+ end
35
+
36
+ # Read more about this date format in the GS1::Extensions::MonthBasedDate class.
37
+ def valid_format_b?(data)
38
+ valid_date_format?(data, '%y%m00')
39
+ end
40
+
41
+ private
42
+
43
+ def valid_date_format?(data, pattern)
44
+ ::Date.strptime(data, pattern)
45
+
46
+ true
47
+ rescue TypeError, ArgumentError
48
+ false
49
+ end
21
50
  end
22
51
  end
23
52
  end
data/lib/gs1/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module GS1
2
- VERSION = '0.1.5'.freeze
2
+ VERSION = '0.1.6'.freeze
3
3
  end
data/lib/gs1.rb CHANGED
@@ -20,19 +20,28 @@ require 'gs1/barcode'
20
20
  #
21
21
  module GS1
22
22
  class << self
23
- def configure
23
+ def configuration
24
24
  @configuration ||= Configuration.new
25
-
26
- yield @configuration
27
25
  end
28
26
 
29
- attr_reader :configuration
27
+ def configure
28
+ if block_given?
29
+ yield configuration
30
+ else
31
+ configuration
32
+ end
33
+ end
30
34
  end
31
35
 
32
36
  # Configuration holds custom configuration parameters.
33
37
  #
34
38
  class Configuration
35
39
  attr_accessor :company_prefix
40
+ attr_writer :barcode_separator
41
+
42
+ def barcode_separator
43
+ @barcode_separator || GS1::Barcode::DEFAULT_SEPARATOR
44
+ end
36
45
  end
37
46
 
38
47
  AI_CLASSES = GS1::Record.descendants.each_with_object({}) do |klass, hash|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gs1
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Åhman
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-16 00:00:00.000000000 Z
11
+ date: 2018-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -143,6 +143,7 @@ files:
143
143
  - lib/gs1/expiration_date.rb
144
144
  - lib/gs1/extensions.rb
145
145
  - lib/gs1/extensions/date.rb
146
+ - lib/gs1/extensions/date_month_based.rb
146
147
  - lib/gs1/extensions/gtin.rb
147
148
  - lib/gs1/gtin.rb
148
149
  - lib/gs1/record.rb