dotstrings 0.2.0 → 0.4.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: 605ff8ed35fcfcbd91f2fc33a4b53e69b614aab962de1c32c1d616f957ee1916
4
- data.tar.gz: dddf00d461a893a4ff4e97c04fcc87617241e42189d0284b22e4ab98313ed25a
3
+ metadata.gz: e9c320ba9bef4d63af10c9c9fd3d91ad41da99b3663538f0b89ebdc6fc780d31
4
+ data.tar.gz: 24e762927cd739f3475a8a550a751f808dd81d73292e47f7449c657edeaa88db
5
5
  SHA512:
6
- metadata.gz: a47fbb334a228e6642d7d6b3250b81ab7c7e239be8fb6ca92ba7877dfe9eb6ac0d3ba2bebe4176477afa9b825e4ce61af4a8d555b83e2a29ed6c176a6d392c90
7
- data.tar.gz: fa824b2c3c59bee05bb64d59508a6bff112a551d37d6bbc198913433fc8b332a75a17af84a5524a944126fd6e38df8bef008615df303565e729556169eb21bc8
6
+ metadata.gz: f24ad0efde29b52a33f28cd0e434970bde12556d9236c632495eebdfd089ed550e54a41f67fb844c70677c53bbdaccdbb93f593cafd535062532f062297ab7b4
7
+ data.tar.gz: d61a17f762188162a431c6289b96e23d4374ca2a29a82e84b58e92bf097d79746a352418859e12c2d1dcf13c8844a53a9975360be6c0a98f65d046c16aba45cc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.4.0] - 2022-09-18
4
+ ### Added
5
+ * Added `DotStrings::File#each`, `DotStrings::File#length`, `DotStrings::File#count`, and `DotStrings::File#empty?` methods.
6
+ * Allow comparing `DotStrings::File` objects.
7
+
8
+ ## [v0.3.0] - 2022-08-07
9
+ ### Changed
10
+ * Improved unicode code point parsing and validation.
11
+ * Added `DotStrings::File#sort`, `DotStrings::File#sort!`, and `DotStrings::File#delete_if` methods.
12
+ * Improved documentation.
13
+
3
14
  ## [v0.2.0] - 2022-07-17
4
15
  ### Changed
5
16
  * Made some state transitions more strict.
@@ -13,6 +24,8 @@
13
24
  ### Added
14
25
  * Initial release.
15
26
 
27
+ [v0.4.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.4.0
28
+ [v0.3.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.3.0
16
29
  [v0.2.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.2.0
17
30
  [v0.1.1]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.1.1
18
31
  [v0.1.0]: https://github.com/raymondjavaxx/dotstrings/releases/tag/v0.1.0
data/README.md CHANGED
@@ -7,6 +7,7 @@ A parser for Apple *strings* files (`.strings`) written in Ruby. Some of the fea
7
7
  * An API for creating strings files programmatically.
8
8
  * Handles Unicode and escaped characters.
9
9
  * Helpful error messages: know which line and column fail to parse and why.
10
+ * Well [tested](test) and [documented](https://www.rubydoc.info/gems/dotstrings/DotStrings).
10
11
 
11
12
  ## Installing
12
13
 
@@ -19,12 +20,12 @@ $ gem install dotstrings
19
20
  Or by adding the following entry to your [Gemfile](https://guides.cocoapods.org/using/a-gemfile.html), then running `$ bundle install`.
20
21
 
21
22
  ```ruby
22
- gem "dotstrings"
23
+ gem 'dotstrings'
23
24
  ```
24
25
 
25
26
  ## Usage
26
27
 
27
- You can load `.strings` files using the `DotString.parse()` utility method. This method returns a `DotStrings::File` object or raises an exception if the file is invalid.
28
+ You can load `.strings` files using the `DotString.parse()` utility method. This method returns a `DotStrings::File` object or raises an exception if the file cannot be parsed.
28
29
 
29
30
  ```ruby
30
31
  file = DotStrings.parse_file('en-US/Localizable.strings')
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DotStrings
4
+ ##
5
+ # Class for errors raised by the parser.
4
6
  class ParsingError < RuntimeError
5
7
  end
6
8
  end
@@ -5,13 +5,48 @@ require 'dotstrings/errors'
5
5
  require 'dotstrings/item'
6
6
 
7
7
  module DotStrings
8
+ ##
9
+ # Represents a .strings file.
10
+ #
11
+ # It provides methods to parse .strings, as well as methods for accessing and
12
+ # manipulating localized string items.
8
13
  class File
14
+ ##
15
+ # All items in the file.
9
16
  attr_reader :items
10
17
 
11
18
  def initialize(items = [])
12
19
  @items = items
13
20
  end
14
21
 
22
+ ##
23
+ # Returns a new File with the items sorted using the given comparator block.
24
+ #
25
+ # If no block is given, the items will be sorted by key.
26
+ def sort(&block)
27
+ new_file = dup
28
+ new_file.sort!(&block)
29
+ end
30
+
31
+ ##
32
+ # Sort the items using the given block.
33
+ #
34
+ # If no block is given, the items will be sorted by key.
35
+ def sort!(&block)
36
+ @items.sort!(&block || ->(a, b) { a.key <=> b.key })
37
+ self
38
+ end
39
+
40
+ ##
41
+ # Parses a file from the given IO object.
42
+ #
43
+ # @example
44
+ # io = Zlib::GzipReader.open('path/to/en.lproj/Localizable.strings.gz')
45
+ # file = DotStrings::File.parse(io)
46
+ #
47
+ # @param io [IO] The IO object to parse.
48
+ # @return [DotStrings::File] The parsed file.
49
+ # @raise [DotStrings::ParsingError] if the file could not be parsed.
15
50
  def self.parse(io)
16
51
  items = []
17
52
 
@@ -22,32 +57,130 @@ module DotStrings
22
57
  File.new(items)
23
58
  end
24
59
 
60
+ ##
61
+ # Parses the file at the given path.
62
+ #
63
+ # @example
64
+ # file = DotStrings::File.parse_file('path/to/en.lproj/Localizable.strings')
65
+ #
66
+ # @param path [String] The path to the file to parse.
67
+ # @return [DotStrings::File] The parsed file.
68
+ # @raise [DotStrings::ParsingError] if the file could not be parsed.
25
69
  def self.parse_file(path)
26
70
  ::File.open(path, 'r') do |file|
27
71
  parse(file)
28
72
  end
29
73
  end
30
74
 
75
+ ##
76
+ # Returns all keys in the file.
31
77
  def keys
32
78
  @items.map(&:key)
33
79
  end
34
80
 
81
+ ##
82
+ # Returns an item by key, if it exists, otherwise nil.
83
+ #
84
+ # @example
85
+ # item = file['button.title']
86
+ # unless item.nil?
87
+ # puts item.value # => 'Submit'
88
+ # end
89
+ #
90
+ # @param key [String] The key of the item to return.
91
+ # @return [DotStrings::Item] The item, if it exists.
35
92
  def [](key)
36
93
  @items.find { |item| item.key == key }
37
94
  end
38
95
 
96
+ ##
97
+ # Appends an item to the file.
98
+ #
99
+ # @example
100
+ # file << DotStrings::Item.new(key: 'button.title', value: 'Submit')
101
+ #
102
+ # @param item [DotStrings::Item] The item to append.
103
+ # @return [DotStrings::Item] The item that was appended.
39
104
  def <<(item)
40
105
  @items << item
106
+ self
41
107
  end
42
108
 
109
+ ##
110
+ # Appends an item to the file.
111
+ #
112
+ # @example
113
+ # file.append(DotStrings::Item.new(key: 'button.title', value: 'Submit'))
43
114
  def append(item)
44
115
  self << item
45
116
  end
46
117
 
118
+ ##
119
+ # Deletes an item by key.
47
120
  def delete(key)
48
121
  @items.delete_if { |item| item.key == key }
49
122
  end
50
123
 
124
+ ##
125
+ # Deletes all items for which the block returns true.
126
+ #
127
+ # @example
128
+ # file.delete_if { |item| item.key.start_with?('button.') }
129
+ def delete_if(&block)
130
+ @items.delete_if(&block)
131
+ self
132
+ end
133
+
134
+ ##
135
+ # Calls the given block once for each item in the file.
136
+ #
137
+ # @param block [Proc] The block to call.
138
+ # @example
139
+ # file.each do |item|
140
+ # puts "#{item.key} > #{item.value}"
141
+ # end
142
+ def each(&block)
143
+ @items.each(&block)
144
+ self
145
+ end
146
+
147
+ ##
148
+ # Returns the number of items in the file.
149
+ def length
150
+ @items.length
151
+ end
152
+
153
+ ##
154
+ # Returns the number of items in the file.
155
+ #
156
+ # If a block is given, it will count the number of items for which the block returns true.
157
+ #
158
+ # @example
159
+ # file.count # => 10
160
+ # file.count { |item| item.key.start_with?('button.') } # => 3
161
+ def count(&block)
162
+ @items.count(&block)
163
+ end
164
+
165
+ ##
166
+ # Returns `true` if the file doen't contain any items.
167
+ def empty?
168
+ @items.empty?
169
+ end
170
+
171
+ def ==(other)
172
+ eql?(other)
173
+ end
174
+
175
+ def eql?(other)
176
+ other.is_a?(self.class) && @items.eql?(other.items)
177
+ end
178
+
179
+ ##
180
+ # Serializes the file to a string.
181
+ #
182
+ # @param escape_single_quotes [Boolean] whether to escape single quotes.
183
+ # @param comments [Boolean] whether to include comments.
51
184
  def to_s(escape_single_quotes: false, comments: true)
52
185
  result = []
53
186
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DotStrings
4
+ ##
5
+ # Represents a localized string item.
4
6
  class Item
5
7
  attr_reader :comment, :key, :value
6
8
 
@@ -10,6 +12,22 @@ module DotStrings
10
12
  @value = value
11
13
  end
12
14
 
15
+ def ==(other)
16
+ eql?(other)
17
+ end
18
+
19
+ def eql?(other)
20
+ other.is_a?(Item) &&
21
+ comment == other.comment &&
22
+ key == other.key &&
23
+ value == other.value
24
+ end
25
+
26
+ ##
27
+ # Serializes the item to string.
28
+ #
29
+ # @param escape_single_quotes [Boolean] Whether to escape single quotes.
30
+ # @param include_comment [Boolean] Whether to include the comment.
13
31
  def to_s(escape_single_quotes: false, include_comment: true)
14
32
  result = []
15
33
 
@@ -4,6 +4,9 @@ require 'dotstrings/errors'
4
4
 
5
5
  module DotStrings
6
6
  # rubocop:disable Metrics/ClassLength
7
+
8
+ ##
9
+ # Parser for .strings files.
7
10
  class Parser
8
11
  # Special tokens
9
12
  TOK_SLASH = '/'
@@ -36,8 +39,6 @@ module DotStrings
36
39
  STATE_UNICODE_SURROGATE = 11
37
40
  STATE_UNICODE_SURROGATE_U = 12
38
41
 
39
- attr_reader :items
40
-
41
42
  def initialize
42
43
  @state = STATE_START
43
44
  @temp_state = nil
@@ -59,11 +60,16 @@ module DotStrings
59
60
  @column = 1
60
61
  end
61
62
 
63
+ ##
64
+ # Specifies a block to be called when a new item is parsed.
62
65
  def on_item(&block)
63
66
  @item_block = block
64
67
  end
65
68
 
66
69
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength
70
+
71
+ ##
72
+ # Feeds data to the parser.
67
73
  def <<(data)
68
74
  data.each_char do |ch|
69
75
  case @state
@@ -157,6 +163,7 @@ module DotStrings
157
163
  update_position(ch)
158
164
  end
159
165
  end
166
+
160
167
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/BlockLength
161
168
 
162
169
  private
@@ -203,30 +210,51 @@ module DotStrings
203
210
  end
204
211
  end
205
212
 
206
- # rubocop:disable Style/GuardClause
213
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
207
214
  def parse_unicode(ch, &block)
208
215
  raise_error("Unexpected character '#{ch}', expecting a hex digit") unless ch =~ TOK_HEX_DIGIT
209
216
 
210
217
  @unicode_buffer << ch
211
218
 
212
- if @unicode_buffer.length == 4
213
- codepoint = @unicode_buffer.join.hex
219
+ # Check if we have enough digits to form a codepoint.
220
+ return if @unicode_buffer.length < 4
214
221
 
215
- if codepoint >= 0xD800 && codepoint <= 0xDBFF
216
- @high_surrogate = codepoint
217
- @state = STATE_UNICODE_SURROGATE
218
- elsif codepoint >= 0xDC00 && codepoint <= 0xDFFF
219
- character = ((@high_surrogate - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000
220
- block.call(character.chr('UTF-8'))
221
- else
222
- block.call(codepoint.chr('UTF-8'))
222
+ codepoint = @unicode_buffer.join.hex
223
+
224
+ if codepoint >= 0xD800 && codepoint <= 0xDBFF
225
+ unless @high_surrogate.nil?
226
+ raise_error(
227
+ 'Found a high surrogate code point after another high surrogate'
228
+ )
223
229
  end
224
230
 
225
- # Clear buffer after codepoint is parsed
226
- @unicode_buffer.clear
231
+ @high_surrogate = codepoint
232
+ @state = STATE_UNICODE_SURROGATE
233
+ elsif codepoint >= 0xDC00 && codepoint <= 0xDFFF
234
+ if @high_surrogate.nil?
235
+ raise_error(
236
+ 'Found a low surrogate code point before a high surrogate'
237
+ )
238
+ end
239
+
240
+ character = ((@high_surrogate - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000
241
+ @high_surrogate = nil
242
+
243
+ block.call(character.chr('UTF-8'))
244
+ else
245
+ unless @high_surrogate.nil?
246
+ raise_error(
247
+ "Invalid unicode codepoint '\\U#{codepoint.to_s(16).upcase}' after a high surrogate code point"
248
+ )
249
+ end
250
+
251
+ block.call(codepoint.chr('UTF-8'))
227
252
  end
253
+
254
+ # Clear buffer after codepoint is parsed
255
+ @unicode_buffer.clear
228
256
  end
229
- # rubocop:enable Style/GuardClause
257
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
230
258
 
231
259
  def update_position(ch)
232
260
  @offset += 1
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DotStrings
4
- VERSION = '0.2.0'
4
+ VERSION = '0.4.0'
5
5
  end
data/lib/dotstrings.rb CHANGED
@@ -6,10 +6,24 @@ require 'dotstrings/item'
6
6
  require 'dotstrings/errors'
7
7
 
8
8
  module DotStrings
9
+ ##
10
+ # Parses a file from the given IO object.
11
+ #
12
+ # This is a convenience method for {DotStrings::File.parse}.
13
+ #
14
+ # @param io [IO] The IO object to parse.
15
+ # @return [DotStrings::File] The parsed file.
9
16
  def self.parse(io)
10
17
  File.parse(io)
11
18
  end
12
19
 
20
+ ##
21
+ # Parses a .strings file at the given path.
22
+ #
23
+ # This is a convenience method for {DotStrings::File.parse_file}.
24
+ #
25
+ # @param path [String] The path to the .strings file to parse.
26
+ # @return [DotStrings::File] The parsed file.
13
27
  def self.parse_file(path)
14
28
  File.parse_file(path)
15
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dotstrings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ramon Torres
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-17 00:00:00.000000000 Z
11
+ date: 2022-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  description: Parse and create .strings files used in localization of iOS and macOS
98
112
  apps.
99
113
  email:
@@ -102,39 +116,15 @@ executables: []
102
116
  extensions: []
103
117
  extra_rdoc_files: []
104
118
  files:
105
- - ".editorconfig"
106
- - ".github/workflows/ci.yml"
107
- - ".gitignore"
108
- - ".rubocop.yml"
109
119
  - CHANGELOG.md
110
- - Gemfile
111
- - Gemfile.lock
112
120
  - LICENSE
113
121
  - README.md
114
- - Rakefile
115
- - dotstrings.gemspec
116
122
  - lib/dotstrings.rb
117
123
  - lib/dotstrings/errors.rb
118
124
  - lib/dotstrings/file.rb
119
125
  - lib/dotstrings/item.rb
120
126
  - lib/dotstrings/parser.rb
121
127
  - lib/dotstrings/version.rb
122
- - test/fixtures/escaped_backslashes.strings
123
- - test/fixtures/escaped_carriage_returns.strings
124
- - test/fixtures/escaped_new_lines.strings
125
- - test/fixtures/escaped_nil.strings
126
- - test/fixtures/escaped_quotes.strings
127
- - test/fixtures/escaped_single_quotes.strings
128
- - test/fixtures/escaped_tabs.strings
129
- - test/fixtures/escaped_unicode.strings
130
- - test/fixtures/utf16be_bom.strings
131
- - test/fixtures/utf16le_bom.strings
132
- - test/fixtures/utf8_bom.strings
133
- - test/fixtures/valid.strings
134
- - test/helper.rb
135
- - test/test_dotstrings.rb
136
- - test/test_file.rb
137
- - test/test_item.rb
138
128
  homepage: https://github.com/raymondjavaxx/dotstrings
139
129
  licenses:
140
130
  - MIT
@@ -155,24 +145,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
145
  - !ruby/object:Gem::Version
156
146
  version: '0'
157
147
  requirements: []
158
- rubygems_version: 3.0.1
148
+ rubyforge_project:
149
+ rubygems_version: 2.7.6
159
150
  signing_key:
160
151
  specification_version: 4
161
152
  summary: Parse and create .strings files used in localization of iOS and macOS apps.
162
- test_files:
163
- - test/fixtures/escaped_backslashes.strings
164
- - test/fixtures/escaped_carriage_returns.strings
165
- - test/fixtures/escaped_new_lines.strings
166
- - test/fixtures/escaped_nil.strings
167
- - test/fixtures/escaped_quotes.strings
168
- - test/fixtures/escaped_single_quotes.strings
169
- - test/fixtures/escaped_tabs.strings
170
- - test/fixtures/escaped_unicode.strings
171
- - test/fixtures/utf16be_bom.strings
172
- - test/fixtures/utf16le_bom.strings
173
- - test/fixtures/utf8_bom.strings
174
- - test/fixtures/valid.strings
175
- - test/helper.rb
176
- - test/test_dotstrings.rb
177
- - test/test_file.rb
178
- - test/test_item.rb
153
+ test_files: []
data/.editorconfig DELETED
@@ -1,13 +0,0 @@
1
- # EditorConfig is awesome: https://EditorConfig.org
2
-
3
- # top-most EditorConfig file
4
- root = true
5
-
6
- [*]
7
- end_of_line = lf
8
- insert_final_newline = true
9
-
10
- [{*.rb,Gemfile,Rakefile,*.yml}]
11
- charset = utf-8
12
- indent_style = space
13
- indent_size = 2
@@ -1,33 +0,0 @@
1
- name: CI
2
-
3
- on:
4
- push:
5
- branches: "*"
6
- pull_request:
7
- branches: "*"
8
-
9
- jobs:
10
- test:
11
- runs-on: ubuntu-latest
12
- strategy:
13
- matrix:
14
- ruby-version:
15
- - "2.5"
16
- - "2.6"
17
- - "2.7"
18
- - "3.0"
19
- - "3.1"
20
-
21
- steps:
22
- - uses: actions/checkout@v2
23
- - name: Set up Ruby
24
- uses: ruby/setup-ruby@v1
25
- with:
26
- ruby-version: ${{ matrix.ruby-version }}
27
- bundler-cache: true # runs 'bundle install' and caches installed gems automatically
28
- - name: Lint
29
- run: bundle exec rake rubocop
30
- - name: Run tests
31
- run: bundle exec rake
32
- - name: Install
33
- run: bundle exec rake install
data/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- pkg
2
- .vscode
data/.rubocop.yml DELETED
@@ -1,41 +0,0 @@
1
- require:
2
- - rubocop-rake
3
- - rubocop-minitest
4
-
5
- AllCops:
6
- TargetRubyVersion: 2.5
7
- NewCops: enable
8
-
9
- Style/StringLiterals:
10
- Enabled: true
11
- EnforcedStyle: single_quotes
12
-
13
- Layout/FirstHashElementIndentation:
14
- Enabled: true
15
- EnforcedStyle: consistent
16
-
17
- Layout/FirstArrayElementIndentation:
18
- Enabled: true
19
- EnforcedStyle: consistent
20
-
21
- Metrics/ClassLength:
22
- Enabled: true
23
- Max: 150
24
-
25
- Naming/MethodParameterName:
26
- Enabled: false
27
-
28
- Metrics/MethodLength:
29
- Enabled: false
30
-
31
- Metrics/AbcSize:
32
- Enabled: false
33
-
34
- Style/Documentation:
35
- Enabled: false
36
-
37
- Style/ParallelAssignment:
38
- Enabled: false
39
-
40
- Minitest/AssertInDelta:
41
- Enabled: false
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gemspec
data/Gemfile.lock DELETED
@@ -1,49 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- dotstrings (0.2.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- ast (2.4.2)
10
- minitest (5.15.0)
11
- parallel (1.22.1)
12
- parser (3.1.2.0)
13
- ast (~> 2.4.1)
14
- rainbow (3.1.1)
15
- rake (13.0.6)
16
- regexp_parser (2.5.0)
17
- rexml (3.2.5)
18
- rubocop (1.28.2)
19
- parallel (~> 1.10)
20
- parser (>= 3.1.0.0)
21
- rainbow (>= 2.2.2, < 4.0)
22
- regexp_parser (>= 1.8, < 3.0)
23
- rexml
24
- rubocop-ast (>= 1.17.0, < 2.0)
25
- ruby-progressbar (~> 1.7)
26
- unicode-display_width (>= 1.4.0, < 3.0)
27
- rubocop-ast (1.17.0)
28
- parser (>= 3.1.1.0)
29
- rubocop-minitest (0.19.1)
30
- rubocop (>= 0.90, < 2.0)
31
- rubocop-rake (0.6.0)
32
- rubocop (~> 1.0)
33
- ruby-progressbar (1.11.0)
34
- unicode-display_width (2.2.0)
35
-
36
- PLATFORMS
37
- ruby
38
-
39
- DEPENDENCIES
40
- bundler
41
- dotstrings!
42
- minitest (~> 5.14)
43
- rake
44
- rubocop
45
- rubocop-minitest
46
- rubocop-rake
47
-
48
- BUNDLED WITH
49
- 2.2.18
data/Rakefile DELETED
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rake/testtask'
5
- require 'rubocop/rake_task'
6
-
7
- Rake::TestTask.new
8
- RuboCop::RakeTask.new
9
-
10
- task default: :test
data/dotstrings.gemspec DELETED
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require './lib/dotstrings/version'
4
-
5
- Gem::Specification.new do |s|
6
- s.name = 'dotstrings'
7
- s.version = DotStrings::VERSION
8
- s.platform = Gem::Platform::RUBY
9
- s.authors = ['Ramon Torres']
10
- s.email = ['raymondjavaxx@gmail.com']
11
- s.homepage = 'https://github.com/raymondjavaxx/dotstrings'
12
- s.description = s.summary = 'Parse and create .strings files used in localization of iOS and macOS apps.'
13
- s.files = `git ls-files`.split("\n")
14
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
16
- s.require_paths = ['lib']
17
- s.license = 'MIT'
18
-
19
- s.add_development_dependency 'bundler'
20
- s.add_development_dependency 'minitest', '~> 5.14'
21
- s.add_development_dependency 'rake'
22
- s.add_development_dependency 'rubocop'
23
- s.add_development_dependency 'rubocop-minitest'
24
- s.add_development_dependency 'rubocop-rake'
25
-
26
- s.required_ruby_version = '>= 2.5.0'
27
- s.metadata['rubygems_mfa_required'] = 'true'
28
- end
@@ -1,2 +0,0 @@
1
- /* Escaped carriage backslashes */
2
- "some\\key" = "some\\value";
@@ -1,2 +0,0 @@
1
- /* Escaped carriage returns */
2
- "some\rkey" = "some\rvalue";
@@ -1,2 +0,0 @@
1
- /* Escaped new lines */
2
- "some\nkey" = "some\nvalue";
@@ -1,2 +0,0 @@
1
- /* Escaped nil */
2
- "key\0" = "value\0";
@@ -1,2 +0,0 @@
1
- /* Escaped quotes */
2
- "some \"key\"" = "some \"value\"";
@@ -1,2 +0,0 @@
1
- /* Escaped quotes */
2
- "some \'key\'" = "some \'value\'";
@@ -1,2 +0,0 @@
1
- /* Escaped tabs */
2
- "some\tkey" = "some\tvalue";
@@ -1,2 +0,0 @@
1
- /* Unicode characters. Key is a dollar sign, value is: ⚡👻 */
2
- "\U0024" = "\U26A1\UD83D\UDC7B";
Binary file
Binary file
@@ -1,2 +0,0 @@
1
- /* Comment */
2
- "key" = "value";
@@ -1,8 +0,0 @@
1
- // Single line comment
2
- "key 1" = "value 1";
3
-
4
- /* Multi line
5
- comment */
6
- "key 2" = "value 2";
7
-
8
- "key 3" = "value 3";
data/test/helper.rb DELETED
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- gem 'minitest'
4
-
5
- require 'minitest/pride'
6
- require 'minitest/autorun'
7
-
8
- require_relative '../lib/dotstrings'
@@ -1,107 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- class TestDotStrings < MiniTest::Test
6
- def test_parse_can_parse_valid_files
7
- file = DotStrings.parse_file('test/fixtures/valid.strings')
8
-
9
- assert_equal 3, file.items.size
10
-
11
- assert_equal 'Single line comment', file.items[0].comment
12
- assert_equal 'key 1', file.items[0].key
13
- assert_equal 'value 1', file.items[0].value
14
-
15
- assert_equal "Multi line\ncomment", file.items[1].comment
16
- assert_equal 'key 2', file.items[1].key
17
- assert_equal 'value 2', file.items[1].value
18
- end
19
-
20
- def test_can_parse_file_with_escaped_quotes
21
- file = DotStrings.parse_file('test/fixtures/escaped_quotes.strings')
22
-
23
- assert_equal 1, file.items.size
24
- assert_equal 'some "key"', file.items[0].key
25
- assert_equal 'some "value"', file.items[0].value
26
- end
27
-
28
- def test_can_parse_file_with_escaped_single_quotes
29
- file = DotStrings.parse_file('test/fixtures/escaped_single_quotes.strings')
30
-
31
- assert_equal 1, file.items.size
32
- assert_equal 'some \'key\'', file.items[0].key
33
- assert_equal 'some \'value\'', file.items[0].value
34
- end
35
-
36
- def test_can_parse_file_with_escaped_tabs
37
- file = DotStrings.parse_file('test/fixtures/escaped_tabs.strings')
38
-
39
- assert_equal 1, file.items.size
40
- assert_equal "some\tkey", file.items[0].key
41
- assert_equal "some\tvalue", file.items[0].value
42
- end
43
-
44
- def test_can_parse_files_with_escaped_carriage_returns
45
- file = DotStrings.parse_file('test/fixtures/escaped_carriage_returns.strings')
46
-
47
- assert_equal 1, file.items.size
48
- assert_equal "some\rkey", file.items[0].key
49
- assert_equal "some\rvalue", file.items[0].value
50
- end
51
-
52
- def test_can_parse_files_with_escaped_nil
53
- file = DotStrings.parse_file('test/fixtures/escaped_nil.strings')
54
-
55
- assert_equal 1, file.items.size
56
- assert_equal "key\0", file.items[0].key
57
- assert_equal "value\0", file.items[0].value
58
- end
59
-
60
- def test_can_parse_files_with_escaped_new_lines
61
- file = DotStrings.parse_file('test/fixtures/escaped_new_lines.strings')
62
-
63
- assert_equal 1, file.items.size
64
- assert_equal "some\nkey", file.items[0].key
65
- assert_equal "some\nvalue", file.items[0].value
66
- end
67
-
68
- def test_can_parse_files_with_escaped_backslashes
69
- file = DotStrings.parse_file('test/fixtures/escaped_backslashes.strings')
70
-
71
- assert_equal 1, file.items.size
72
- assert_equal 'some\\key', file.items[0].key
73
- assert_equal 'some\\value', file.items[0].value
74
- end
75
-
76
- def test_can_parse_files_with_escaped_unicode
77
- file = DotStrings.parse_file('test/fixtures/escaped_unicode.strings')
78
-
79
- assert_equal 1, file.items.size
80
- assert_equal '$', file.items[0].key
81
- assert_equal '⚡👻', file.items[0].value
82
- end
83
-
84
- def test_can_parse_utf16le_files_with_bom
85
- file = DotStrings.parse_file('test/fixtures/utf16le_bom.strings')
86
-
87
- assert_equal 1, file.items.size
88
- assert_equal 'key', file.items[0].key
89
- assert_equal 'value', file.items[0].value
90
- end
91
-
92
- def test_can_parse_utf16be_files_with_bom
93
- file = DotStrings.parse_file('test/fixtures/utf16be_bom.strings')
94
-
95
- assert_equal 1, file.items.size
96
- assert_equal 'key', file.items[0].key
97
- assert_equal 'value', file.items[0].value
98
- end
99
-
100
- def test_can_parse_utf8_files_with_bom
101
- file = DotStrings.parse_file('test/fixtures/utf8_bom.strings')
102
-
103
- assert_equal 1, file.items.size
104
- assert_equal 'key', file.items[0].key
105
- assert_equal 'value', file.items[0].value
106
- end
107
- end
data/test/test_file.rb DELETED
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- class TestFile < MiniTest::Test
6
- def test_delete
7
- items = [
8
- DotStrings::Item.new(key: 'key 1', value: 'value 1'),
9
- DotStrings::Item.new(key: 'key 2', value: 'value 2'),
10
- DotStrings::Item.new(key: 'key 3', value: 'value 3')
11
- ]
12
-
13
- file = DotStrings::File.new(items)
14
-
15
- file.delete('key 2')
16
- assert_equal 2, file.items.size
17
- assert_equal ['key 1', 'key 3'], file.keys
18
- end
19
-
20
- def test_access_by_key
21
- items = [
22
- DotStrings::Item.new(key: 'key 1', value: 'value 1'),
23
- DotStrings::Item.new(key: 'key 2', value: 'value 2'),
24
- DotStrings::Item.new(key: 'key 3', value: 'value 3')
25
- ]
26
-
27
- file = DotStrings::File.new(items)
28
-
29
- assert_equal 'value 1', file['key 1'].value
30
- assert_equal 'value 2', file['key 2'].value
31
- assert_equal 'value 3', file['key 3'].value
32
- end
33
-
34
- def test_append
35
- file = DotStrings::File.new
36
- file.append(DotStrings::Item.new(key: 'key 1', value: 'value 1'))
37
- assert_equal 1, file.items.size
38
- end
39
-
40
- def test_to_string
41
- file = DotStrings::File.new([
42
- DotStrings::Item.new(comment: 'Comment 1', key: 'key 1', value: 'value 1'),
43
- DotStrings::Item.new(comment: 'Comment 2', key: 'key 2', value: 'value 2'),
44
- DotStrings::Item.new(comment: 'Comment 3', key: 'key 3', value: '👻'),
45
- DotStrings::Item.new(comment: 'Comment 4', key: "\"'\t\n\r\0", value: "\"'\t\n\r\0")
46
- ])
47
-
48
- expected = <<~'END_OF_DOCUMENT'
49
- /* Comment 1 */
50
- "key 1" = "value 1";
51
-
52
- /* Comment 2 */
53
- "key 2" = "value 2";
54
-
55
- /* Comment 3 */
56
- "key 3" = "👻";
57
-
58
- /* Comment 4 */
59
- "\"'\t\n\r\0" = "\"'\t\n\r\0";
60
- END_OF_DOCUMENT
61
-
62
- assert_equal expected, file.to_s
63
- end
64
-
65
- def test_to_string_no_comments
66
- file = DotStrings::File.new([
67
- DotStrings::Item.new(comment: 'Comment 1', key: 'key 1', value: 'value 1'),
68
- DotStrings::Item.new(comment: 'Comment 2', key: 'key 2', value: 'value 2')
69
- ])
70
-
71
- expected = <<~'END_OF_DOCUMENT'
72
- "key 1" = "value 1";
73
-
74
- "key 2" = "value 2";
75
- END_OF_DOCUMENT
76
-
77
- assert_equal expected, file.to_s(comments: false)
78
- end
79
-
80
- def test_to_string_can_escape_single_quotes
81
- items = [
82
- DotStrings::Item.new(comment: 'Comment', key: "key'", value: "value'")
83
- ]
84
-
85
- file = DotStrings::File.new(items)
86
-
87
- expected = <<~'END_OF_DOCUMENT'
88
- /* Comment */
89
- "key\'" = "value\'";
90
- END_OF_DOCUMENT
91
-
92
- assert_equal expected, file.to_s(escape_single_quotes: true)
93
- end
94
- end
data/test/test_item.rb DELETED
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'helper'
4
-
5
- class TestFile < MiniTest::Test
6
- def test_to_s
7
- item = DotStrings::Item.new(comment: 'Comment', key: 'key 1', value: 'value 1')
8
- assert_equal "/* Comment */\n\"key 1\" = \"value 1\";", item.to_s
9
- end
10
-
11
- def test_to_s_with_nil_comment
12
- item = DotStrings::Item.new(comment: nil, key: 'key 1', value: 'value 1')
13
- assert_equal '"key 1" = "value 1";', item.to_s
14
- end
15
- end