rescodegen 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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