rescodegen 0.1.2 → 0.2.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
  SHA1:
3
- metadata.gz: a1cc0746cc5d75abad97618260d6ffde5db9d0d6
4
- data.tar.gz: 67019eadc6cbeea942d08a196076bd66cc25988e
3
+ metadata.gz: c59f55dbbfbcb78c9f1d27bc7d15d0dea733865c
4
+ data.tar.gz: a0b55eef2c05a946010250bcc67bb696bb30fe9f
5
5
  SHA512:
6
- metadata.gz: 8f697b4fdfed3a5ae98ab871538cdea35f349619146ce1a815d6c4e1e02601c1165d27ab04f826b216821c316c7dcf66eabe37e58b7aa8c9d0a9e72d5b734613
7
- data.tar.gz: 0ce98a4477650c9f99924dbcee497847cde935fba36675b01d924709f443fcd684532d495f5283f834da695a8e9350e9df32b6f8551fe7f6107eeaec503e45a8
6
+ metadata.gz: 61fb365a48ad5132662a7c82512691d453e36f64160f0279a834a3b8654055f53064f875ec00692ab13e58a0acdb1003c8372c1880a4198ac4e58ffa38527b7a
7
+ data.tar.gz: 7082b4311ae00f35d82d223d37b3c02b27cd1ece9375c82500c556c01e63bb1a2b0fe26e7ebfd941408efeac1c6e00d1386e6df44494fa858bdd4d4583aff0d2
@@ -1,15 +1,33 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rescodegen (0.1.2)
4
+ rescodegen (0.2.0)
5
+ plist (= 3.2.0)
5
6
 
6
7
  GEM
7
8
  remote: https://rubygems.org/
8
9
  specs:
10
+ codeclimate-test-reporter (0.5.0)
11
+ simplecov (>= 0.7.1, < 1.0.0)
12
+ diff-lcs (1.2.5)
9
13
  docile (1.1.5)
10
14
  json (1.8.3)
11
15
  minitest (5.8.4)
16
+ plist (3.2.0)
12
17
  rake (10.5.0)
18
+ rspec (3.4.0)
19
+ rspec-core (~> 3.4.0)
20
+ rspec-expectations (~> 3.4.0)
21
+ rspec-mocks (~> 3.4.0)
22
+ rspec-core (3.4.3)
23
+ rspec-support (~> 3.4.0)
24
+ rspec-expectations (3.4.0)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.4.0)
27
+ rspec-mocks (3.4.1)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.4.0)
30
+ rspec-support (3.4.1)
13
31
  simplecov (0.11.2)
14
32
  docile (~> 1.1.0)
15
33
  json (~> 1.8)
@@ -21,9 +39,11 @@ PLATFORMS
21
39
 
22
40
  DEPENDENCIES
23
41
  bundler (~> 1.11)
42
+ codeclimate-test-reporter (~> 0.5)
24
43
  minitest (~> 5.0)
25
44
  rake (~> 10.0)
26
45
  rescodegen!
46
+ rspec (~> 3.4)
27
47
  simplecov (~> 0.11)
28
48
 
29
49
  BUNDLED WITH
data/README.md CHANGED
@@ -1,39 +1,69 @@
1
+ <a href="https://codeclimate.com/github/seanhenry/rescodegen/coverage"><img src="https://codeclimate.com/github/seanhenry/rescodegen/badges/coverage.svg" /></a>
2
+ <a href="https://codeclimate.com/github/seanhenry/rescodegen"><img src="https://codeclimate.com/github/seanhenry/rescodegen/badges/gpa.svg" /></a>
1
3
  # rescodegen
2
- A command line tool for creating Swift and Objective-C code from localizable strings.
4
+ A command line tool for creating Swift and Objective-C code from singular Localizable.strings and plural Localizable.stringsdict.
3
5
  ## Installation
4
6
  `$ sudo gem install rescodegen`
5
7
 
6
8
  ## Usage
7
9
  ### Generate Code
8
- `$ rescodegen -l swift -o output_folder Localizable.strings`
10
+ `$ rescodegen -i Localizable.strings`
9
11
  ### Options
10
12
 
11
- |Option|Value|Description |
12
- |---|---|---|
13
- |-l|swift\|objc|The language of the generated code.|
14
- |-o|relative/path/to/output/directory|Where to generate the files.|
15
- |-p|e.g. SH|An optional prefix to apply to Objective-C types.|
13
+ |Option|Value|Required|Default|Description |
14
+ |---|---|---|---|---|
15
+ |`-i`|relative/path/to/Localizable.strings|✔️||The input file(s). Must be `.strings` or `.stringsdict` format. You may specify more than 1 file.|
16
+ |`-l`|swift or objc|❌|`swift`|The language of the generated code.|
17
+ |`-o`|relative/path/to/output/directory|❌|`.`|Where to save generated files.|
18
+ |`-p`|e.g. SH|❌||An optional prefix to apply to Objective-C types.|
16
19
  ## Example
17
20
  ### Localizable.strings
18
21
 
19
22
  ```
20
- /* The Ok button label displayed when an error occurs on the home screen. */
21
- "HomeScreen.Alert.OkButtonLabel" = "Ok";
23
+ ...
24
+ /* Error message to display when uploading an image fails. */
25
+ "Uploader.ErrorAlert.Message" = "The upload failed.";
26
+ ...
27
+ ```
28
+ ### Localizable.stringsdict
22
29
 
23
- /* The error message displayed by an alert when content could not be loaded. */
24
- "HomeScreen.Alert.LoadErrorMessage" = "There was an error loading your content.";
30
+ ```
31
+ ...
32
+ <key>Uploader.ProgressLabel.Text</key>
33
+ <dict>
34
+ <key>NSStringLocalizedFormatKey</key>
35
+ <string>Uploading %#@images@.</string>
36
+ <key>images</key>
37
+ <dict>
38
+ <key>NSStringFormatSpecTypeKey</key>
39
+ <string>NSStringPluralRuleType</string>
40
+ <key>NSStringFormatValueTypeKey</key>
41
+ <string>d</string>
42
+ <key>one</key>
43
+ <string>one image</string>
44
+ <key>other</key>
45
+ <string>%d images</string>
46
+ </dict>
47
+ </dict>
48
+ ...
25
49
  ```
26
50
  ### Generate Code
27
51
 
28
52
  ```
29
- $ rescodegen -l swift -o . Localizable.strings
30
- $ rescodegen -l objc -o . Localizable.strings
53
+ $ rescodegen -i Localizable.strings -i Localizable.stringsdict
54
+ $ rescodegen -i Localizable.strings -i Localizable.stringsdict -l objc
31
55
  ```
32
56
  ### Xcode
33
57
  Drag the generated files into Xcode and add them to your target.
34
58
  ### Swift
35
- Access localised strings using the `localizedString` property.
36
- `Strings.Singular.homeScreen_alert_loadErrorMessage.localizedString`
59
+ Access localized singular strings:
60
+ `Strings.Singular.uploader_errorAlert_message.localizedString` => "The upload failed."
61
+ Access localized plural strings:
62
+ `Strings.Plural.uploader_progressLabel_text.localizedString(1)` => "Uploading one image."
37
63
  ### Objective-C
38
- Access localised strings using the `LocalizedSingularString` function.
39
- `LocalizedSingularString(SingularString_homeScreen_alert_loadErrorMessage);`
64
+ Access localized singular strings:
65
+ `LocalizedSingularString(SingularString_uploader_errorAlert_message)` => "The upload failed."
66
+ Access localized plural strings:
67
+ `LocalizedPluralString(PluralString_uploader_progressLabel_text, 2)` => "Uploading 2 images."
68
+ ## Encoding
69
+ rescodegen currently only supports UTF-8 encoding.
data/Rakefile CHANGED
@@ -1,10 +1,19 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
- Rake::TestTask.new(:test) do |t|
4
+ task :test => [:unit, :acceptance] do
5
+
6
+ end
7
+
8
+ Rake::TestTask.new(:unit) do |t|
5
9
  t.libs << "test"
6
10
  t.libs << "lib"
7
11
  t.test_files = FileList['test/**/*_tests.rb']
8
12
  end
9
13
 
14
+ desc "Runs acceptance tests."
15
+ task :acceptance do
16
+ exec "rspec spec"
17
+ end
18
+
10
19
  task :default => :test
@@ -11,10 +11,13 @@ require 'optparse'
11
11
  require 'rescodegen/code_generator/swift_strings_generator'
12
12
  require 'rescodegen/code_generator/objc_header_strings_generator'
13
13
  require 'rescodegen/code_generator/objc_main_strings_generator'
14
- require 'rescodegen/key_generator/strings_key_generator'
14
+ require 'rescodegen/key_generator/key_generator'
15
+ require 'rescodegen/key_generator/code_safe_key_generator'
15
16
  require 'rescodegen/code_formatter/code_formatter'
17
+ require 'rescodegen/key_reader/strings_key_reader'
18
+ require 'rescodegen/key_reader/stringsdict_key_reader'
16
19
 
17
- options = { output: ".", language: "swift", prefix: "" }
20
+ options = { output: ".", language: "swift", prefix: "", input: [] }
18
21
 
19
22
  parser = OptionParser.new do |opts|
20
23
  opts.banner = "Usage: rescodegen [options] input_file"
@@ -29,27 +32,58 @@ parser = OptionParser.new do |opts|
29
32
  opts.on("-p", "--prefix=PREFIX", "Optional prefix for Objective-C types.") do |p|
30
33
  options[:prefix] = p
31
34
  end
35
+ opts.on("-i", "--input=file1,file2", Array, "Accepts .strings or .stringsdict files.") do |i|
36
+ options[:input] += i
37
+ end
32
38
  end
33
39
  parser.parse!
34
40
 
35
41
  abort "Invalid -l argument. Expects swift or objc." if !options[:language].match("swift|objc")
36
- input_file = ARGV.last
37
- abort "Missing input_file.\n\n#{parser.help}" if input_file.nil?
42
+ abort "Missing -i argument. Must have at least 1 input file." if options[:input].size == 0
38
43
  output_file = options[:output] + "/" + options[:prefix] + "Strings"
39
44
 
40
- def generate_swift_file(code_safe_keys, keys, output_file)
41
- File.write(output_file + ".swift", Rescodegen::SwiftStringsGenerator.new.generate(code_safe_keys, keys))
45
+ def generate_swift_file(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys, output_file)
46
+ File.write(output_file + ".swift", Rescodegen::SwiftStringsGenerator.new.generate(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys))
47
+ end
48
+
49
+ def generate_objc_files(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys, output_file, prefix)
50
+ File.write(output_file + ".h", Rescodegen::ObjcHeaderStringsGenerator.new(prefix).generate(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys))
51
+ File.write(output_file + ".m", Rescodegen::ObjcMainStringsGenerator.new(prefix).generate(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys))
52
+ end
53
+
54
+ def abort_bad_input_file
55
+ abort "Only currenly supports .strings and .stringsdict files."
42
56
  end
43
57
 
44
- def generate_objc_files(code_safe_keys, keys, output_file, prefix)
45
- File.write(output_file + ".h", Rescodegen::ObjcHeaderStringsGenerator.new(prefix).generate(code_safe_keys, keys))
46
- File.write(output_file + ".m", Rescodegen::ObjcMainStringsGenerator.new(prefix).generate(code_safe_keys, keys))
58
+ def generator_for_file(file, key_reader)
59
+ code_safe_key_generator = Rescodegen::CodeSafeKeyGenerator.new
60
+ generator = Rescodegen::KeyGenerator.new(key_reader, code_safe_key_generator, file)
47
61
  end
48
62
 
49
- generator = Rescodegen::StringsKeyGenerator.create_from_file(input_file)
50
- keys = generator.keys
51
- code_safe_keys = generator.code_safe_keys
63
+ abort_bad_input_file if options[:input].select { |file| file.end_with?(".strings") || file.end_with?(".stringsdict") }.empty?
64
+
65
+ singular_keys = []
66
+ singular_code_safe_keys = []
67
+ options[:input].select { |file| file.end_with?(".strings") }
68
+ .map { |file|
69
+ generator = generator_for_file(file, Rescodegen::StringsKeyReader.new)
70
+ singular_keys += generator.keys
71
+ singular_code_safe_keys += generator.code_safe_keys
72
+ }
73
+
74
+ plural_keys = []
75
+ plural_code_safe_keys = []
76
+ options[:input].select { |file| file.end_with?(".stringsdict") }
77
+ .map { |file|
78
+ generator = generator_for_file(file, Rescodegen::StringsdictKeyReader.new)
79
+ plural_keys += generator.keys
80
+ plural_code_safe_keys += generator.code_safe_keys
81
+ }
82
+
83
+ abort "The input file(s) did not contain any keys." if singular_keys.size == 0 && plural_keys.size == 0
84
+
52
85
  formatter = Rescodegen::CodeFormatter.new
53
- code_safe_keys = code_safe_keys.map { |k| formatter.format_string(k) }
54
- generate_swift_file(code_safe_keys, keys, output_file) if options[:language] == "swift"
55
- generate_objc_files(code_safe_keys, keys, output_file, options[:prefix]) if options[:language] == "objc"
86
+ singular_code_safe_keys = singular_code_safe_keys.map { |k| formatter.format_string(k) }
87
+ plural_code_safe_keys = plural_code_safe_keys.map { |k| formatter.format_string(k) }
88
+ generate_swift_file(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys, output_file) if options[:language] == "swift"
89
+ generate_objc_files(singular_code_safe_keys, singular_keys, plural_code_safe_keys, plural_keys, output_file, options[:prefix]) if options[:language] == "objc"
@@ -7,36 +7,49 @@ module Rescodegen
7
7
  @prefix = prefix
8
8
  end
9
9
 
10
- def generate(keys, values)
11
- super(keys, values)
12
- enum_name = prefix "SingularString"
10
+ def generate(singular_keys, singular_values, plural_keys, plural_values)
11
+ super(singular_keys, singular_values, plural_keys, plural_values)
13
12
  import_module("Foundation")
14
- .start_enum(enum_name, "NSInteger")
15
- .add_cases(enum_name, keys)
16
- .finish_enum
17
- .add_c_method("NSString*", prefix("LocalizedSingularString"), enum_name, "singularString")
18
- .newline
13
+ .singular_enum(singular_keys, singular_values)
14
+ .plural_enum(plural_keys, plural_values)
15
+ add_c_method("NSString*", prefix("LocalizedSingularString"), "#{singular_enum_name} singularString") if singular_keys.size != 0
16
+ add_c_method("NSString*", prefix("LocalizedPluralString"), "#{plural_enum_name} pluralString, ...") if plural_keys.size != 0
17
+ newline
19
18
  @output
20
19
  end
21
20
 
22
21
  protected
23
22
 
23
+ def singular_enum(keys, values)
24
+ return self if keys.size == 0
25
+ newline
26
+ .start_enum(singular_enum_name, "NSInteger")
27
+ .add_cases(singular_enum_name, keys)
28
+ .finish_enum
29
+ end
30
+
31
+ def plural_enum(keys, values)
32
+ return self if keys.size == 0
33
+ newline
34
+ .start_enum(plural_enum_name, "NSInteger")
35
+ .add_cases(plural_enum_name, keys)
36
+ .finish_enum
37
+ end
38
+
24
39
  def import_module(name)
25
40
  @output += "@import #{name};"
26
- newline.newline
27
- self
41
+ newline
28
42
  end
29
43
 
30
44
  def start_enum(name, type)
31
45
  indent
32
46
  @output += "typedef NS_ENUM(#{type}, #{name})"
33
47
  open_brackets
34
- self
35
48
  end
36
49
 
37
- def add_c_method(return_type, name, parameter_type, parameter_name)
50
+ def add_c_method(return_type, name, parameter_list)
38
51
  newline
39
- @output += "#{return_type} #{name}(#{parameter_type} #{parameter_name});"
52
+ @output += "#{return_type} #{name}(#{parameter_list});"
40
53
  self
41
54
  end
42
55
 
@@ -54,7 +67,14 @@ module Rescodegen
54
67
  indent
55
68
  @output += "};"
56
69
  newline
57
- self
70
+ end
71
+
72
+ def singular_enum_name
73
+ prefix "SingularString"
74
+ end
75
+
76
+ def plural_enum_name
77
+ prefix "PluralString"
58
78
  end
59
79
  end
60
80
  end
@@ -7,87 +7,115 @@ module Rescodegen
7
7
  @prefix = prefix
8
8
  end
9
9
 
10
- def generate(keys, values)
11
- super(keys, values)
12
- enum_name = prefix "SingularString"
10
+ def generate(singular_keys, singular_values, plural_keys, plural_values)
11
+ super(singular_keys, singular_values, plural_keys, plural_values)
13
12
  newline
14
13
  .import_header(prefix("Strings.h"))
15
14
  .newline
16
- .start_c_method("NSString*", prefix("LocalizedSingularString"), enum_name, "singularString")
17
- .start_switch("singularString")
18
- .add_cases(keys.map { |k| enum_name + "_" + k }, values)
19
- .close_brackets
20
- .close_brackets
21
- .newline
15
+ .add_singular_methods(singular_keys, singular_values)
16
+ .add_plural_methods(plural_keys, plural_values)
22
17
  @output
23
18
  end
24
19
 
25
- def import_header(name)
26
- @output += "#import \"#{name}\""
27
- self
28
- end
20
+ protected
29
21
 
30
- def start_c_method(return_type, name, parameter_type, parameter_name)
31
- newline
32
- @output += "#{return_type} #{name}(#{parameter_type} #{parameter_name})"
33
- open_brackets
34
- self
35
- end
22
+ def add_singular_methods(keys, values)
23
+ return self if keys.size == 0
24
+ enum_name = prefix "SingularString"
25
+ start_c_method("NSString*", prefix("LocalizedSingularString"), "#{enum_name} singularString")
26
+ .start_switch("singularString")
27
+ .add_cases(keys.map { |k| enum_name + "_" + k }, values)
28
+ .close_brackets
29
+ .close_brackets
30
+ end
36
31
 
37
- def start_switch(value)
38
- indent
39
- @output += "switch (#{value})"
40
- open_brackets
41
- self
42
- end
32
+ def add_plural_methods(keys, values)
33
+ return self if keys.size == 0
34
+ enum_name = prefix "PluralString"
35
+ start_c_method("NSString*", "NSStringFromPluralString", "#{enum_name} pluralString")
36
+ .start_switch("pluralString")
37
+ .add_cases(keys.map { |k| enum_name + "_" + k }, values)
38
+ .close_brackets
39
+ .close_brackets
40
+ .start_c_method("NSString*", prefix("LocalizedPluralString"), "#{enum_name} pluralString, ...")
41
+ .return_localized_plural_string
42
+ .close_brackets
43
+ end
44
+
45
+ def import_header(name)
46
+ @output += "#import \"#{name}\""
47
+ self
48
+ end
49
+
50
+ def start_c_method(return_type, name, parameter_list)
51
+ newline
52
+ @output += "#{return_type} #{name}(#{parameter_list})"
53
+ open_brackets
54
+ end
55
+
56
+ def start_switch(value)
57
+ indent
58
+ @output += "switch (#{value})"
59
+ open_brackets
60
+ end
61
+
62
+ def add_cases(keys, values)
63
+ i = 0
64
+ until i == keys.size
65
+ start_case(keys[i])
66
+ .return_value(localized_string(values[i]))
67
+ .finish_case
68
+ i += 1
69
+ end
70
+ add_default_case
71
+ end
43
72
 
44
- def add_cases(keys, values)
45
- i = 0
46
- until i == keys.size
47
- start_case(keys[i])
48
- .return_value(localized_string(values[i]))
73
+ def add_default_case
74
+ start_default_case
75
+ .return_value("@\"\"")
49
76
  .finish_case
50
- i += 1
51
77
  end
52
- add_default_case
53
- self
54
- end
55
78
 
56
- def add_default_case
57
- start_default_case
58
- .return_value("@\"\"")
59
- .finish_case
60
- end
79
+ def start_case(key)
80
+ indent
81
+ @output += "case #{key}:"
82
+ newline
83
+ increment_indent_level
84
+ end
61
85
 
62
- def start_case(key)
63
- indent
64
- @output += "case #{key}:"
65
- newline
66
- increment_indent_level
67
- end
86
+ def start_default_case
87
+ indent
88
+ @output += "default:"
89
+ newline
90
+ increment_indent_level
91
+ end
68
92
 
69
- def start_default_case
70
- indent
71
- @output += "default:"
72
- newline
73
- increment_indent_level
74
- self
75
- end
93
+ def finish_case
94
+ decrement_indent_level
95
+ end
76
96
 
77
- def finish_case
78
- decrement_indent_level
79
- self
80
- end
97
+ def return_value(value)
98
+ indent
99
+ @output += "return #{value};"
100
+ newline
101
+ end
81
102
 
82
- def return_value(value)
83
- indent
84
- @output += "return #{value};"
85
- newline
86
- self
87
- end
103
+ def localized_string(value)
104
+ return "NSLocalizedString(@\"#{value}\", @\"\")"
105
+ end
88
106
 
89
- def localized_string(value)
90
- return "NSLocalizedString(@\"#{value}\", @\"\")"
91
- end
107
+ def return_localized_plural_string
108
+ add_line("va_list args")
109
+ .add_line("va_start(args, pluralString)")
110
+ .add_line("NSString *string = [[NSString alloc] initWithFormat:NSStringFromPluralString(pluralString) locale:[NSLocale currentLocale] arguments:args]")
111
+ .add_line("va_end(args)")
112
+ .add_line("return string")
113
+ end
114
+
115
+ def add_line(line)
116
+ indent
117
+ @output += "#{line};"
118
+ newline
119
+ end
92
120
  end
93
121
  end
@@ -2,8 +2,9 @@
2
2
  module Rescodegen
3
3
  class StringsGenerator
4
4
 
5
- def generate(keys, values)
6
- raise "Expects keys and values of equal sizes" if keys.size != values.size
5
+ def generate(singular_keys, singular_values, plural_keys, plural_values)
6
+ raise "Expects keys and values of equal sizes" if singular_keys.size != singular_values.size || plural_keys.size != plural_values.size
7
+ raise "Expects at least one key and one value" if singular_keys.size == 0 && plural_keys.size == 0
7
8
  @output = ""
8
9
  @tab_level = 0
9
10
  end
@@ -23,7 +24,6 @@ module Rescodegen
23
24
  @output += " {"
24
25
  newline
25
26
  increment_indent_level
26
- self
27
27
  end
28
28
 
29
29
  def close_brackets
@@ -31,7 +31,6 @@ module Rescodegen
31
31
  indent
32
32
  @output += "}"
33
33
  newline
34
- self
35
34
  end
36
35
 
37
36
  def increment_indent_level
@@ -3,22 +3,40 @@ require_relative 'strings_generator'
3
3
  module Rescodegen
4
4
  class SwiftStringsGenerator < StringsGenerator
5
5
 
6
- def generate(keys, values)
7
- super(keys, values)
6
+ def generate(singular_keys, singular_values, plural_keys, plural_values)
7
+ super(singular_keys, singular_values, plural_keys, plural_values)
8
8
  import_header("Foundation")
9
9
  start_struct("Strings")
10
- .start_enum("Singular", "String")
11
- .add_cases(keys, values)
12
- .start_computed_property("localizedString", "String")
13
- .return_localized_string
14
- .close_brackets
15
- .close_brackets
10
+ .add_singular_enum(singular_keys, singular_values)
11
+ .add_plural_enum(plural_keys, plural_values)
16
12
  .close_brackets
17
13
  @output
18
14
  end
19
15
 
20
16
  protected
21
17
 
18
+ def add_singular_enum(keys, values)
19
+ return self if keys.size == 0
20
+ newline
21
+ .start_enum("Singular", "String")
22
+ .add_cases(keys, values)
23
+ .start_computed_property("localizedString", "String")
24
+ .return_localized_string
25
+ .close_brackets
26
+ .close_brackets
27
+ end
28
+
29
+ def add_plural_enum(keys, values)
30
+ return self if keys.size == 0
31
+ newline
32
+ .start_enum("Plural", "String")
33
+ .add_cases(keys, values)
34
+ .start_function("localizedString", "args: CVarArgType...", "String")
35
+ .return_localized_plural_string
36
+ .close_brackets
37
+ close_brackets
38
+ end
39
+
22
40
  def newline
23
41
  @output += "\n"
24
42
  self
@@ -26,22 +44,19 @@ module Rescodegen
26
44
 
27
45
  def import_header(name)
28
46
  @output += "import #{name}"
29
- newline.newline
30
- self
47
+ newline
31
48
  end
32
49
 
33
50
  def start_struct(name)
51
+ newline
34
52
  @output += "struct #{name}"
35
53
  open_brackets
36
- newline
37
- self
38
54
  end
39
55
 
40
56
  def start_enum(name, type)
41
57
  indent
42
58
  @output += "enum #{name}: #{type}"
43
59
  open_brackets
44
- self
45
60
  end
46
61
 
47
62
  def add_cases(keys, values)
@@ -52,21 +67,35 @@ module Rescodegen
52
67
  newline
53
68
  end
54
69
  newline
55
- self
56
70
  end
57
71
 
58
72
  def start_computed_property(name, type)
59
73
  indent
60
74
  @output += "var #{name}: #{type}"
61
75
  open_brackets
62
- self
63
76
  end
64
77
 
65
78
  def return_localized_string
66
79
  indent
67
80
  @output += "return NSLocalizedString(rawValue, comment: \"\")"
68
81
  newline
69
- self
82
+ end
83
+
84
+ def start_function(name, parameter_list, return_type)
85
+ indent
86
+ @output += "func #{name}(#{parameter_list}) -> #{return_type}"
87
+ open_brackets
88
+ end
89
+
90
+ def return_localized_plural_string
91
+ add_line "let localized = NSLocalizedString(rawValue, comment: \"\")"
92
+ add_line "return String(format: localized, locale: NSLocale.currentLocale(), arguments: args)"
93
+ end
94
+
95
+ def add_line(line)
96
+ indent
97
+ @output += "#{line}"
98
+ newline
70
99
  end
71
100
  end
72
101
  end
@@ -0,0 +1,44 @@
1
+
2
+ module Rescodegen
3
+
4
+ class CodeSafeKeyGenerator
5
+
6
+ def code_safe_keys_from_keys(keys)
7
+ keys.map { |key|
8
+ key.replace_string_format_specifiers
9
+ .replace_unsupported_characters
10
+ .replace_whitespace
11
+ .remove_duplicate_underscores
12
+ .trim_underscores
13
+ .protect_from_numbers
14
+ }
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+ class String
21
+ def replace_string_format_specifiers
22
+ gsub(/%\.[0-9]f|%[a-zA-Z@]+/, "_")
23
+ end
24
+
25
+ def replace_unsupported_characters
26
+ gsub(/[^a-zA-Z0-9]/, "_")
27
+ end
28
+
29
+ def replace_whitespace
30
+ gsub(/\s+/, "_")
31
+ end
32
+
33
+ def remove_duplicate_underscores
34
+ gsub(/_+/, "_")
35
+ end
36
+
37
+ def trim_underscores
38
+ gsub(/^_|_$/, "")
39
+ end
40
+
41
+ def protect_from_numbers
42
+ sub(/(^[0-9])/, "_\\1")
43
+ end
44
+ end
@@ -0,0 +1,24 @@
1
+
2
+ module Rescodegen
3
+
4
+ class KeyGenerator
5
+
6
+ attr_reader :key_reader
7
+ attr_reader :code_safe_key_generator
8
+ attr_reader :file_path
9
+
10
+ def initialize(key_reader, code_safe_key_generator, file_path)
11
+ @key_reader = key_reader
12
+ @code_safe_key_generator = code_safe_key_generator
13
+ @file_path = file_path
14
+ end
15
+
16
+ def keys
17
+ key_reader.read_keys_from_lines File.open(file_path).readlines
18
+ end
19
+
20
+ def code_safe_keys
21
+ code_safe_key_generator.code_safe_keys_from_keys keys
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+
2
+ module Rescodegen
3
+
4
+ class StringsKeyReader
5
+
6
+ def read_keys_from_lines(lines)
7
+ lines.map(&method(:encode_line_with_utf8))
8
+ .select(&method(:line_contains_key))
9
+ .map(&method(:extract_key))
10
+ end
11
+
12
+ private
13
+ def encode_line_with_utf8(line)
14
+ line.encode("UTF-8", :invalid=>:replace, :replace=>"?").encode('UTF-8')
15
+ end
16
+
17
+ def line_contains_key(line)
18
+ return false if line.size == 0
19
+ line[0] == "\""
20
+ end
21
+
22
+ def extract_key(line)
23
+ line.sub(/\n$/, "")
24
+ .sub(/(")(.*)(" = ".*)/, "\\2")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'plist'
2
+
3
+ module Rescodegen
4
+
5
+ class StringsdictKeyReader
6
+
7
+ def read_keys_from_lines(lines)
8
+ plist = Plist::parse_xml lines.join("\n")
9
+ raise "Invalid plist file" if plist.nil?
10
+ plist.map { |k, _| k }
11
+ end
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Rescodegen
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -18,8 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_runtime_dependency "plist", "= 3.2.0"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.11"
22
24
  spec.add_development_dependency "rake", "~> 10.0"
23
25
  spec.add_development_dependency "minitest", "~> 5.0"
24
26
  spec.add_development_dependency "simplecov", "~> 0.11"
27
+ spec.add_development_dependency "codeclimate-test-reporter", "~> 0.5"
28
+ spec.add_development_dependency "rspec", "~> 3.4"
25
29
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rescodegen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Henry
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-03 00:00:00.000000000 Z
11
+ date: 2016-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: plist
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,34 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0.11'
83
+ - !ruby/object:Gem::Dependency
84
+ name: codeclimate-test-reporter
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.5'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.5'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.4'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.4'
69
111
  description:
70
112
  email:
71
113
  - hello@seanhenry.codes
@@ -87,7 +129,10 @@ files:
87
129
  - lib/rescodegen/code_generator/objc_main_strings_generator.rb
88
130
  - lib/rescodegen/code_generator/strings_generator.rb
89
131
  - lib/rescodegen/code_generator/swift_strings_generator.rb
90
- - lib/rescodegen/key_generator/strings_key_generator.rb
132
+ - lib/rescodegen/key_generator/code_safe_key_generator.rb
133
+ - lib/rescodegen/key_generator/key_generator.rb
134
+ - lib/rescodegen/key_reader/strings_key_reader.rb
135
+ - lib/rescodegen/key_reader/stringsdict_key_reader.rb
91
136
  - lib/rescodegen/version.rb
92
137
  - rescodegen.gemspec
93
138
  homepage: https://github.com/seanhenry/rescodegen
@@ -1,34 +0,0 @@
1
-
2
- module Rescodegen
3
- class StringsKeyGenerator
4
-
5
- def self.create_from_file(input_file)
6
- lines = File.open(input_file, "rb:UTF-16BE")
7
- StringsKeyGenerator.new lines
8
- end
9
-
10
- def initialize(lines)
11
- @keys = lines.map { |l| l.encode("UTF-8", :invalid=>:replace, :replace=>"?").encode('UTF-8') }
12
- .reject { |l| l.strip == "" }
13
- .select { |l| l[0] == "\"" }
14
- .map do |line|
15
- line.gsub(/\n$/, "")
16
- .gsub(/(")(.*)(" = ".*)/, "\\2") # extracts key from "key" = "description"; format
17
- end
18
- end
19
-
20
- def keys
21
- @keys
22
- end
23
-
24
- def code_safe_keys
25
- @keys.map do |key|
26
- key.gsub(/%\.[0-9]f|%[a-zA-Z@]+/, "_") # replace %d, %ld, %.2f etc
27
- .gsub(/[^a-zA-Z0-9]/, "_") # replace unsupported characters
28
- .gsub(/(\s|_)+/, "_") # replaces 1 or more occurance of whitespace and/or '_' with single '_'
29
- .gsub(/^_|_$/, "") # remove _ from beginning and end
30
- .gsub(/(^[0-9])/, "_\\1") # add underscore at beginning if starts with number
31
- end
32
- end
33
- end
34
- end