unicode-data 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 84da723c97e50af6ce0b53902b81511f5333949d847d047046e070c9f640fe28
4
+ data.tar.gz: 477cb2a6ddf74b0b8ec8af07f3db680a205ef272b439da8978bc0b548fe20946
5
+ SHA512:
6
+ metadata.gz: fb06a0321db3ffe4fbbeb459c9554bd65ef21bcf525a088778d7171234944fef797e64ee1ad9aefd6d5f3e31ca956e87c1d28ada4b730cb7f018ff72b6390f36
7
+ data.tar.gz: 5e10c86ca002f5240a3b80b026d32545c7a49b570270ae3b7875edb489907e060eedd9fdbc4ac90a5b65146db0a893e1b2534ce7d0f35781c8e23cc03e32f461
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # Ignore all of the derived data
11
+ /lib/unicode/data/derived.txt
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,24 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ unicode-data (0.1.0)
5
+ rubyzip
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ minitest (5.14.4)
11
+ rake (13.0.6)
12
+ rubyzip (2.3.2)
13
+
14
+ PLATFORMS
15
+ x86_64-darwin-19
16
+
17
+ DEPENDENCIES
18
+ bundler
19
+ minitest
20
+ rake
21
+ unicode-data!
22
+
23
+ BUNDLED WITH
24
+ 2.2.24
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021-present Kevin Newton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Unicode::Data
2
+
3
+ A Ruby wrapping for the unicode character data set.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem "unicode-data"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install unicode-data
20
+
21
+ ## Usage
22
+
23
+ When this gem is installed, it will automatically download the unicode character data set to a temporary zip file and generate a list of properties from that zip file. You can use this information (under `lib/unicode/data/derived.txt`) to implement, for example, a regular expression engine that can respect the unicode semantics defined by the [unicode technical standard](https://unicode.org/reports/tr18/). At the moment the list of properties generated includes:
24
+
25
+ * General Categories
26
+ * Blocks
27
+ * Ages
28
+ * Scripts
29
+ * Script Extensions
30
+ * Core Properties (Math, Alphabetic, Lowercase, Case_Ignorable, etc.)
31
+ * Prop List Properties (White_Space, Bidi_Control, Terminal_Punctuation, etc.)
32
+
33
+ This lines up to almost all of the [Onigmo](https://github.com/k-takata/Onigmo/blob/master/doc/UnicodeProps.txt) unicode support (and a lot more), with the exception of:
34
+
35
+ * POSIX brackets
36
+ * Emoji
37
+
38
+ ## Development
39
+
40
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
41
+
42
+ 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).
43
+
44
+ ## Contributing
45
+
46
+ Bug reports and pull requests are welcome on GitHub at https://github.com/kddnewton/unicode-data.
47
+
48
+ ## License
49
+
50
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../../../lib", __dir__)
4
+ $LOAD_PATH.unshift(lib)
5
+
6
+ require "bundler/gem_tasks"
7
+ require "rake/clean"
8
+ require "rake/testtask"
9
+ require "unicode/data"
10
+
11
+ CLEAN.include(File.join(lib, "unicode/data/derived.txt"))
12
+
13
+ namespace :ext do
14
+ load "ext/unicode/data/Rakefile"
15
+ end
16
+
17
+ Rake::TestTask.new(:test) do |t|
18
+ t.libs << "test"
19
+ t.libs << "lib"
20
+ t.test_files = FileList["test/**/*_test.rb"]
21
+ end
22
+
23
+ task default: :test
24
+
25
+ namespace :"unicode:data" do
26
+ desc "Generate all of the neccesary derived files"
27
+ task :generate do
28
+ Unicode::Data.generate
29
+ end
30
+
31
+ desc "Validate all of the necessary derived files"
32
+ task :validate do
33
+ Unicode::Data.validate
34
+ end
35
+ end
data/bin/console ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "unicode/data"
5
+
6
+ require "irb"
7
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.expand_path("../../../lib", __dir__))
4
+ require "unicode/data"
5
+
6
+ desc "Download the unicode data file necessary for the current version of Ruby"
7
+ task :default do
8
+ Unicode::Data.generate
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "unicode/data/version"
4
+
5
+ module Unicode
6
+ module Data
7
+ def self.generate
8
+ require "unicode/data/generate"
9
+ Generate.call
10
+ end
11
+
12
+ def self.validate
13
+ require "unicode/data/validate"
14
+ Validate.call
15
+ end
16
+
17
+ def self.properties
18
+ @properties ||=
19
+ File.readlines(File.expand_path("data/derived.txt", __dir__), chomp: true).to_h do |line|
20
+ line.split(/\s+/, 2)
21
+ end
22
+ end
23
+
24
+ def self.property?(query, value)
25
+ properties[query].split(",").any? do |segment|
26
+ case segment
27
+ when /^(\d+)$/
28
+ $1.to_i == value.ord
29
+ when /^(\d+)..(\d+)$/
30
+ ($1.to_i..$2.to_i).cover?(value.ord)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+ require "open-uri"
5
+ require "zip"
6
+
7
+ module Unicode
8
+ module Data
9
+ class Generate
10
+ class PropertyValueAliases
11
+ attr_reader :aliases
12
+
13
+ def initialize(aliases)
14
+ @aliases = aliases
15
+ end
16
+
17
+ def keys
18
+ aliases.keys
19
+ end
20
+
21
+ def find(property, value)
22
+ term = value.gsub(/[- ]/, "_")
23
+
24
+ aliases[property].find do |alias_set|
25
+ alias_set.any? { |alias_value| alias_value.casecmp(term) == 0 }
26
+ end
27
+ end
28
+ end
29
+
30
+ attr_reader :zipfile, :outfile, :logger
31
+
32
+ def initialize(zipfile, outfile, logger: Logger.new(STDOUT))
33
+ @zipfile = zipfile
34
+ @outfile = outfile
35
+ @logger = logger
36
+ end
37
+
38
+ def generate
39
+ property_aliases = read_property_aliases
40
+ property_value_aliases = PropertyValueAliases.new(read_property_value_aliases)
41
+
42
+ generate_general_categories
43
+ generate_blocks(property_value_aliases)
44
+ generate_ages(property_value_aliases)
45
+ generate_scripts(property_value_aliases)
46
+ generate_script_extensions(property_value_aliases)
47
+ generate_core_properties(property_aliases, property_value_aliases)
48
+ generate_prop_list_properties(property_aliases, property_value_aliases)
49
+ end
50
+
51
+ def self.call
52
+ unicode_version = RbConfig::CONFIG["UNICODE_VERSION"]
53
+
54
+ URI.open("https://www.unicode.org/Public/#{unicode_version}/ucd/UCD.zip") do |file|
55
+ Zip::File.open_buffer(file) do |zipfile|
56
+ File.open(File.join(__dir__, "derived.txt"), "w") do |outfile|
57
+ new(zipfile, outfile).generate
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def each_line(filepath)
66
+ zipfile.get_input_stream(filepath).each_line do |line|
67
+ line.tap(&:chomp!).gsub!(/\s*#.*$/, "")
68
+ yield line unless line.empty?
69
+ end
70
+ end
71
+
72
+ def read_property_aliases
73
+ [].tap do |aliases|
74
+ each_line("PropertyAliases.txt") do |line|
75
+ aliases << line.split(/\s*;\s*/).uniq
76
+ end
77
+ end
78
+ end
79
+
80
+ def read_property_value_aliases
81
+ {}.tap do |aliases|
82
+ each_line("PropertyValueAliases.txt") do |line|
83
+ type, *values = line.split(/\s*;\s*/)
84
+ (aliases[type] ||= []) << values.uniq
85
+ end
86
+ end
87
+ end
88
+
89
+ GeneralCategory = Struct.new(:name, :abbrev, :aliased, :subsets, keyword_init: true)
90
+
91
+ # https://www.unicode.org/reports/tr44/#General_Category_Values
92
+ def generate_general_categories
93
+ properties = {}
94
+
95
+ zipfile.get_input_stream("PropertyValueAliases.txt").each_line do |line|
96
+ if line.start_with?("# General_Category") .. line.start_with?("# @missing")
97
+ match = /^gc ; (?<abbrev>[^\s]+)\s+; (?<name>[^\s]+)\s+(?:; (?<aliased>[^\s]+)\s+)?(?:\# (?<subsets>[^\s]+))?/.match(line)
98
+ next if match.nil?
99
+
100
+ properties[match[:abbrev]] =
101
+ GeneralCategory.new(
102
+ name: match[:name],
103
+ abbrev: match[:abbrev],
104
+ aliased: match[:aliased],
105
+ subsets: match[:subsets]&.split(" | ")
106
+ )
107
+ end
108
+ end
109
+
110
+ general_categories = read_property_codepoints("extracted/DerivedGeneralCategory.txt")
111
+ general_category_codepoints = {}
112
+
113
+ general_categories.each do |abbrev, codepoints|
114
+ general_category = properties[abbrev]
115
+
116
+ queries = [abbrev, general_category.name]
117
+ queries << general_category.aliased if general_category.aliased
118
+ queries.map! { |value| "\\p{General_Category=#{value}}" }
119
+
120
+ if general_category.subsets
121
+ codepoints =
122
+ general_category.subsets.flat_map do |subset|
123
+ general_categories[subset]
124
+ end
125
+ end
126
+
127
+ general_category_codepoints[abbrev] = codepoints
128
+ write_queries(queries, codepoints)
129
+ end
130
+
131
+ # https://unicode.org/reports/tr18/#General_Category_Property
132
+ # There are a couple of special categories that are defined that we will
133
+ # handle here.
134
+ write_queries(["\\p{Any}"], [0..0x10FFFF])
135
+ write_queries(["\\p{Assigned}"], (0..0x10FFFF).to_a - general_category_codepoints["Cn"].flat_map { |codepoint| [*codepoint] })
136
+ write_queries(["\\p{ASCII}"], [0..0x7F])
137
+ end
138
+
139
+ def generate_blocks(property_value_aliases)
140
+ read_property_codepoints("Blocks.txt").each do |block, codepoints|
141
+ write_queries(
142
+ property_value_aliases.find("blk", block).map { |value| "\\p{Block=#{value}}" },
143
+ codepoints
144
+ )
145
+ end
146
+ end
147
+
148
+ # https://www.unicode.org/reports/tr44/#Character_Age
149
+ def generate_ages(property_value_aliases)
150
+ ages = read_property_codepoints("DerivedAge.txt").to_a
151
+ ages.each_with_index do |(version, _values), index|
152
+ # When querying by age, something that was added in 1.1 will also
153
+ # match at \p{age=2.0} query, so we need to get every value from all
154
+ # of the preceeding ages as well.
155
+ write_queries(
156
+ property_value_aliases.find("age", version).map { |value| "\\p{Age=#{value}}" },
157
+ ages[0..index].flat_map(&:last)
158
+ )
159
+ end
160
+ end
161
+
162
+ # https://www.unicode.org/reports/tr24/
163
+ def generate_scripts(property_value_aliases)
164
+ read_property_codepoints("Scripts.txt").each do |script, codepoints|
165
+ write_queries(
166
+ property_value_aliases.find("sc", script).map { |value| "\\p{Script=#{value}}" },
167
+ codepoints
168
+ )
169
+ end
170
+ end
171
+
172
+ def generate_script_extensions(property_value_aliases)
173
+ script_extensions = {}
174
+
175
+ read_property_codepoints("ScriptExtensions.txt").each do |script_extension_set, codepoints|
176
+ script_extension_set.split(" ").each do |script_extension|
177
+ script_extensions[script_extension] ||= []
178
+ script_extensions[script_extension] += codepoints
179
+ end
180
+ end
181
+
182
+ script_extensions.each do |script_extension, codepoints|
183
+ write_queries(
184
+ property_value_aliases.find("sc", script_extension)
185
+ .map { |value| "\\p{Script_Extensions=#{value}}" },
186
+ codepoints
187
+ )
188
+ end
189
+ end
190
+
191
+ def generate_core_properties(property_aliases, property_value_aliases)
192
+ read_property_codepoints("DerivedCoreProperties.txt").each do |property, codepoints|
193
+ property_alias_set =
194
+ property_aliases.find { |alias_set| alias_set.include?(property) }
195
+
196
+ property_value_alias_key =
197
+ (property_alias_set & property_value_aliases.keys).first
198
+
199
+ write_queries(["\\p{#{property_value_alias_key}=True}"], codepoints)
200
+ end
201
+ end
202
+
203
+ def generate_prop_list_properties(property_aliases, property_value_aliases)
204
+ read_property_codepoints("PropList.txt").each do |property, codepoints|
205
+ property_alias_set =
206
+ property_aliases.find { |alias_set| alias_set.include?(property) }
207
+
208
+ property_value_alias_key =
209
+ (property_alias_set & property_value_aliases.keys).first
210
+
211
+ write_queries(["\\p{#{property_value_alias_key}=True}"], codepoints)
212
+ end
213
+ end
214
+
215
+ def read_property_codepoints(filepath)
216
+ {}.tap do |properties|
217
+ each_line(filepath) do |line|
218
+ codepoint, property = line.split(/\s*;\s*/)
219
+ codepoint =
220
+ if codepoint.include?("..")
221
+ left, right = codepoint.split("..").map { |value| value.to_i(16) }
222
+ left..right
223
+ else
224
+ codepoint.to_i(16)
225
+ end
226
+
227
+ (properties[property] ||= []) << codepoint
228
+ end
229
+ end
230
+ end
231
+
232
+ def write_queries(queries, codepoints)
233
+ serialized =
234
+ codepoints
235
+ .flat_map { |codepoint| [*codepoint] }
236
+ .sort
237
+ .chunk_while { |prev, curr| curr - prev == 1 }
238
+ .map { |chunk| chunk.length > 1 ? "#{chunk[0]}..#{chunk[-1]}" : chunk[0] }
239
+ .join(",")
240
+
241
+ queries.each do |query|
242
+ logger.info("Generating #{query}")
243
+ outfile.puts("%-80s %s" % [query, serialized])
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ module Unicode
6
+ module Data
7
+ class Validate
8
+ module Mode
9
+ # Just test the first value in the range of characters
10
+ class First
11
+ def apply(values, &block)
12
+ block.call(values.first)
13
+ end
14
+ end
15
+
16
+ # Test a sample of 50 random values from the range of characters
17
+ class Sample
18
+ def apply(values, &block)
19
+ values.to_a.sample(50).each(&block)
20
+ end
21
+ end
22
+
23
+ # Test every value from the range of characters
24
+ class Full
25
+ def apply(values, &block)
26
+ values.each(&block)
27
+ end
28
+ end
29
+ end
30
+
31
+ attr_reader :logger, :mode, :surrogates
32
+
33
+ def initialize(logger: Logger.new(STDOUT), mode: ENV.fetch("MODE", "first"))
34
+ @logger = logger
35
+ @mode =
36
+ case mode
37
+ when "first" then Mode::First.new
38
+ when "sample" then Mode::Sample.new
39
+ when "full" then Mode::Full.new
40
+ else
41
+ raise ArgumentError, "invalid mode: #{mode}"
42
+ end
43
+
44
+ # This is a list of all of the surrogate characters that exist so that
45
+ # we can skip them when validating since they're not valid in UTF-8.
46
+ File.foreach(File.join(__dir__, "derived.txt"), chomp: true) do |line|
47
+ property, values = line.split(" ", 2)
48
+
49
+ if property.start_with?("\\p{General_Category=Surrogate}")
50
+ @surrogates = each_value(values, Mode::Full.new).to_a
51
+ break
52
+ end
53
+ end
54
+ end
55
+
56
+ def validate
57
+ File.foreach(File.join(__dir__, "derived.txt"), chomp: true) do |line|
58
+ property, values = line.split(/\s+/, 2)
59
+
60
+ # For general categories and scripts, we don't actually want the
61
+ # prefix in the property name, so here leave it out.
62
+ property.gsub!(/(General_Category|Script)=/, "")
63
+
64
+ # Ruby doesn't support Block= syntax, it expects you to instead have
65
+ # no property name and have the block name begin with In_.
66
+ property.gsub!(/Block=/, "In_")
67
+
68
+ # Ruby doesn't support boolean property querying with values, it only
69
+ # supports the plain property name.
70
+ property.gsub!(/=(Yes|Y|True|T)/, "")
71
+
72
+ pattern =
73
+ begin
74
+ /#{property}/
75
+ rescue RegexpError
76
+ # There are a fair amount of properties that we have in this gem
77
+ # that Ruby doesn't support natively. Things like aliases for the
78
+ # various blocks, script extensions, aliases for the ages, etc.
79
+ # In this case just rescue the error and move on since we can't
80
+ # validate against native.
81
+ logger.warn("Skipping #{property}")
82
+ next
83
+ end
84
+
85
+ logger.info("Validating #{property}")
86
+
87
+ each_value(values, mode) do |value|
88
+ next if surrogates.include?(value)
89
+ raise unless pattern.match?([value].pack("U"))
90
+ end
91
+ end
92
+ end
93
+
94
+ def self.call
95
+ new.validate
96
+ end
97
+
98
+ private
99
+
100
+ def each_value(values, mode, &block)
101
+ return enum_for(__method__, values, mode) unless block_given?
102
+
103
+ values.split(",").each do |value|
104
+ case value
105
+ when /^(\d+)$/
106
+ block.call($1.to_i)
107
+ when /^(\d+)..(\d+)$/
108
+ mode.apply($1.to_i..$2.to_i, &block)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Unicode
4
+ module Data
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,30 @@
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 "unicode/data/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "unicode-data"
9
+ spec.version = Unicode::Data::VERSION
10
+ spec.authors = ["Kevin Newton"]
11
+ spec.email = ["kddnewton@gmail.com"]
12
+
13
+ spec.summary = "A Ruby port of the unicode character data"
14
+ spec.homepage = "https://github.com/kddnewton/unicode-data"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+ spec.extensions = ["ext/unicode/data/Rakefile"]
24
+
25
+ spec.add_dependency "rubyzip"
26
+
27
+ spec.add_development_dependency "bundler"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "minitest"
30
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unicode-data
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Newton
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-08-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyzip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - kddnewton@gmail.com
72
+ executables: []
73
+ extensions:
74
+ - ext/unicode/data/Rakefile
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - LICENSE
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - ext/unicode/data/Rakefile
86
+ - lib/unicode/data.rb
87
+ - lib/unicode/data/generate.rb
88
+ - lib/unicode/data/validate.rb
89
+ - lib/unicode/data/version.rb
90
+ - unicode-data.gemspec
91
+ homepage: https://github.com/kddnewton/unicode-data
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.2.3
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: A Ruby port of the unicode character data
114
+ test_files: []