i18n_yaml_sorter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Bernardo de Pádua
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,133 @@
1
+ = i18n_yaml_sorter
2
+
3
+ A simple and very limited yaml deep sorter that will not mess up your strings and text values. Made exclusively to sort the yaml commonly used in rails apps, specially i18n. It will not sort arrays, yaml objects. It doesn't use the YAML parser, since it would mess up text, but sorts the lines in the yaml file
4
+
5
+ As your rails i18n app grows, it becomes very boring to keep all locale files in sync. You have to care about adding all keys in the same order, in different files, or you will get yourself into a mess and will lose lots of precious time comparing the files in different locales and looking for the appropriate translation keys whenever you want to change something.
6
+
7
+ If you try deserializing/serializing the yml files using the YAML parser, to get them in the same order, you will figure that your strings in the ymls will be "standarized" to whatever the YAML generator prefers (strings in double quotes). It will also mess up your indentation (and use its defaults).
8
+
9
+ i18n_yaml_sorter to the rescue! Add it to Textmate and you will be able to sort your yaml file in nanoseconds. Your file will look pristine, like it was human edited.
10
+
11
+ == Example
12
+
13
+ This:
14
+
15
+ b_two:
16
+ a_1: Simple most common text
17
+ b_two: |
18
+ This is the best way of
19
+ inputing large chunks of text
20
+ in the YAML files.
21
+
22
+ Note that this format keeps blank
23
+ lines in the same indentation.
24
+ d_four: "We can also
25
+ use the boring \"
26
+ accross multiple lines
27
+ but have to escape then."
28
+ e_five: Or you can do it
29
+ like that as well, it also works.
30
+ c_three:
31
+ a: "Marcelo"
32
+ d_4: Rafael
33
+ # Your comments will be untouched
34
+ # but will be bound to the element
35
+ # on top of them (d_4 here).
36
+ "b": Bernardo
37
+ c_3:
38
+ unify: Luiz
39
+ klass: Lucas
40
+ a_one: >
41
+ This is another way
42
+ of inputing text. It
43
+ will squish whitespace
44
+ when deserialized
45
+ (like HTML does).
46
+
47
+
48
+ Becomes:
49
+
50
+ a_one: >
51
+ This is another way
52
+ of inputing text. It
53
+ will squish whitespace
54
+ when deserialized
55
+ (like HTML does).
56
+ b_two:
57
+ a_1: Simple most common text
58
+ b_two: |
59
+ This is the best way of
60
+ inputing large chunks of text
61
+ in the YAML files.
62
+
63
+ Note that this format keeps blank
64
+ lines in the same indentation.
65
+ d_four: "We can also
66
+ use the boring \"
67
+ accross multiple lines
68
+ but have to escape then."
69
+ c_three:
70
+ a: "Marcelo"
71
+ "b": Bernardo
72
+ c_3:
73
+ klass: Lucas
74
+ unify: Luiz
75
+ d_4: Rafael
76
+ # Your comments will be untouched
77
+ # but will be bound to the element
78
+ # on top of them (d_4 here).
79
+ e_five: Or you can do it
80
+ like that as well, it also works.
81
+
82
+ == Instaling
83
+
84
+ Easy, just install the gem:
85
+
86
+ $ gem install i18n_yaml_sorter
87
+
88
+ Then the +sort_yaml+ command line tool will be available. If you prefer using Ruby (in your rakes, etc), use the simple +I18nYamlSorter+ class.
89
+
90
+ == Textmate Bundle
91
+
92
+ Run the Bundle Editor (Bundles > Bundle Editor > Show), and create a new command ( + > New Command ).
93
+
94
+ Erase the template code and write this command (no more, no less):
95
+
96
+ sort_yaml
97
+
98
+ Add a key binding and a scope selector (source.yaml) if you which. Now, when you run this command, the opened yaml file (or just the part of it that is selected) will be sorted. To edit selected part of the file, make sure it is valid YAML by itself, or your yaml file might be corrupt (you can always Undo if you mess up).
99
+
100
+ == Command line
101
+
102
+ +sort_yaml+ will operate on STDIN and STDOUT, so sorting an existing yaml file should be as easy as:
103
+
104
+ $ sort_yaml < in.yml > out.yml
105
+
106
+ TODO: Add command line arguments parsing and options (so you can, for instance, sort a whole dir of yaml) and inline help.
107
+
108
+ == Rake task
109
+
110
+ If you don't use textmate, it should be trivial to add a rake task to sort all the i18n yaml files of a rails app (eg.: in the dir config/locale/ ), using the +sort_yaml+ command.
111
+
112
+ TODO: Write the code here once I have time!
113
+
114
+ == Future improvements (Forks Welcome!)
115
+
116
+ * Make +sort_yaml+ smart, take directories, etc
117
+ * Make it a "gem plugin" so it will add rake tasks that will sort all yml files in your +I18n.load_path+ (make it configurable, since we usually don't want to sort all locale yamls, like the rails default ones).
118
+ * Refactoring: code is still ugly, but works
119
+ * Gather other existing rails i18n related yaml tools, textmate bundles, etc, make a super gem out of it, and rule the world!
120
+
121
+ == Note on Patches/Pull Requests
122
+
123
+ * Fork the project.
124
+ * Make your feature addition or bug fix.
125
+ * Add tests for it. This is important so I don't break it in a
126
+ future version unintentionally.
127
+ * Commit, do not mess with rakefile, version, or history.
128
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
129
+ * Send me a pull request. Bonus points for topic branches.
130
+
131
+ == Copyright
132
+
133
+ Copyright (c) 2010 Bernardo de Pádua. See LICENSE for details.
@@ -0,0 +1,55 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "i18n_yaml_sorter"
8
+ gem.summary = %Q{ A I18n YAML deep sorter that will keep your locales organized and not screw up your text formating }
9
+ gem.description = %Q{ Allows you to deep sort YAML files that are mainly composed of
10
+ nested hashes and string values. Great to sort your rails I18n YAML files. You can easily
11
+ add it to a textmate bundle, rake task, or just use the included regular comand line tool.
12
+ }
13
+ gem.email = "berpasan@gmail.com"
14
+ gem.homepage = "http://github.com/redealumni/i18n_yaml_sorter"
15
+ gem.authors = ["Bernardo de Pádua"]
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+ rescue LoadError
38
+ task :rcov do
39
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
40
+ end
41
+ end
42
+
43
+ task :test => :check_dependencies
44
+
45
+ task :default => :test
46
+
47
+ require 'rake/rdoctask'
48
+ Rake::RDocTask.new do |rdoc|
49
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
50
+
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = "i18n_yaml_sorter #{version}"
53
+ rdoc.rdoc_files.include('README*')
54
+ rdoc.rdoc_files.include('lib/**/*.rb')
55
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'i18n_yaml_sorter'
4
+
5
+ sorter = I18nYamlSorter.new(STDIN)
6
+ puts sorter.sort
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{i18n_yaml_sorter}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Bernardo de P\303\241dua"]
12
+ s.date = %q{2010-03-11}
13
+ s.default_executable = %q{sort_yaml}
14
+ s.description = %q{ Allows you to deep sort YAML files that are mainly composed of
15
+ nested hashes and string values. Great to sort your rails I18n YAML files. You can easily
16
+ add it to a textmate bundle, rake task, or just use the included regular comand line tool.
17
+ }
18
+ s.email = %q{berpasan@gmail.com}
19
+ s.executables = ["sort_yaml"]
20
+ s.extra_rdoc_files = [
21
+ "LICENSE",
22
+ "README.rdoc"
23
+ ]
24
+ s.files = [
25
+ ".document",
26
+ ".gitignore",
27
+ "LICENSE",
28
+ "README.rdoc",
29
+ "Rakefile",
30
+ "VERSION",
31
+ "bin/sort_yaml",
32
+ "i18n_yaml_sorter.gemspec",
33
+ "lib/i18n_yaml_sorter.rb",
34
+ "test/helper.rb",
35
+ "test/in.yml",
36
+ "test/out.yml",
37
+ "test/test_i18n_yaml_sorter.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/redealumni/i18n_yaml_sorter}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.6}
43
+ s.summary = %q{A I18n YAML deep sorter that will keep your locales organized and not screw up your text formating}
44
+ s.test_files = [
45
+ "test/helper.rb",
46
+ "test/test_i18n_yaml_sorter.rb"
47
+ ]
48
+
49
+ if s.respond_to? :specification_version then
50
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
+ s.specification_version = 3
52
+
53
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
54
+ else
55
+ end
56
+ else
57
+ end
58
+ end
59
+
@@ -0,0 +1,124 @@
1
+ class I18nYamlSorter
2
+
3
+ def initialize(io_input)
4
+ @io_input = io_input
5
+ end
6
+
7
+ def sort
8
+ @array = break_blocks_into_array
9
+ @current_array_index = 0
10
+ sorted_yaml_from_blocks_array
11
+ end
12
+
13
+ private
14
+
15
+ def break_blocks_into_array
16
+ array = []
17
+
18
+ loop do
19
+
20
+ maybe_next_line = @io_input.gets || break
21
+ maybe_next_line.chomp!
22
+
23
+ #Is it blank? Discard!
24
+ next if maybe_next_line.match(/^\s*$/)
25
+
26
+ #Does it look like a key: value line?
27
+ key_value_parse = maybe_next_line.match(/^(\s*)(["']?[\w\-]+["']?)(: )(\s*)(\S.*\S)(\s*)$/)
28
+ if key_value_parse
29
+ array << maybe_next_line.concat("\n") #yes, it is the beginning of a key:value block
30
+
31
+ #Special cases when it should add extra lines to the array element (multi line quoted strings)
32
+
33
+ #Is the value surrounded by quotes?
34
+ starts_with_quote = key_value_parse[5].match(/^["']/)[0] rescue nil
35
+ ends_with_quote = key_value_parse[5].match(/[^\\](["'])$/)[1] rescue nil
36
+ if starts_with_quote and !(starts_with_quote == ends_with_quote)
37
+
38
+ loop do #Append next lines until we find the closing quote
39
+ content_line = @io_input.gets || break
40
+ content_line.chomp!
41
+ array.last << content_line.concat("\n")
42
+ break if content_line.match(/[^\\][#{starts_with_quote}]\s*$/)
43
+ end
44
+
45
+ end # if starts_with_quote
46
+
47
+ next
48
+ end # if key_value_parse
49
+
50
+ # Is it a | or > string alue?
51
+ is_special_string = maybe_next_line.match(/^(\s*)(["']?[\w\-]+["']?)(: )(\s*)([|>])(\s*)$/)
52
+ if is_special_string
53
+ array << maybe_next_line.concat("\n") #yes, it is the beginning of a key block
54
+ indentation = is_special_string[1]
55
+ #Append the next lines until we find one that is not indented
56
+ loop do
57
+ content_line = @io_input.gets || break
58
+ content_line.chomp!
59
+ this_indentation = content_line.match(/^\s*/)[0] rescue ""
60
+ if indentation.size < this_indentation.size
61
+ array.last << content_line.concat("\n")
62
+ else
63
+ @io_input.seek(-content_line.size, IO::SEEK_CUR) #returns so we can read this line again
64
+ break
65
+ end
66
+ end
67
+
68
+ next
69
+ end #if is_special_string
70
+
71
+ # Is it the begining of a multi level hash?
72
+ is_start_of_hash = maybe_next_line.match(/^(\s*)(["']?[\w\-]+["']?)(:)(\s*)$/)
73
+ if is_start_of_hash
74
+ array << maybe_next_line.concat("\n")
75
+ next
76
+ end
77
+
78
+ #If we got here and nothing was done, this line
79
+ # should probably be merged with the previous one.
80
+ if array.last
81
+ array.last << maybe_next_line.concat("\n")
82
+ else
83
+ array << maybe_next_line.concat("\n")
84
+ end
85
+ end #loop
86
+
87
+ array
88
+ end
89
+
90
+ def sorted_yaml_from_blocks_array(current_block = nil)
91
+
92
+ unless current_block
93
+ current_block = @array[@current_array_index]
94
+ @current_array_index += 1
95
+ end
96
+
97
+ out_array = []
98
+ current_match = current_block.match(/^(\s*)(["']?[\w\-]+["']?)(:)/)
99
+ current_level = current_match[1] rescue ''
100
+ current_key = current_match[2].downcase.tr(%q{"'}, "") rescue ''
101
+ out_array << [current_key, current_block]
102
+
103
+ loop do
104
+ next_block = @array[@current_array_index] || break
105
+ @current_array_index += 1
106
+
107
+ current_match = next_block.match(/^(\s*)(["']?[\w\-]+["']?)(:)/) || next
108
+ current_key = current_match[2].downcase.tr(%q{"'}, "")
109
+ next_level = current_match[1]
110
+
111
+ if current_level.size < next_level.size
112
+ out_array.last.last << sorted_yaml_from_blocks_array(next_block)
113
+ elsif current_level.size == next_level.size
114
+ out_array << [current_key, next_block]
115
+ elsif current_level.size > next_level.size
116
+ @current_array_index -= 1
117
+ break
118
+ end
119
+ end
120
+
121
+ return out_array.sort.map(&:last).join
122
+ end
123
+
124
+ end
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'i18n_yaml_sorter'
7
+
8
+ class Test::Unit::TestCase
9
+ end
@@ -0,0 +1,44 @@
1
+ b_two:
2
+ a_1: Joãozinho
3
+ b_two: |
4
+ Nos somos legais, como
5
+ por exemplo: João,
6
+ que: gosta de nos dar milhões
7
+ em dinheiro todos os dias.
8
+
9
+ Linhas em Branco com tabulação
10
+ nos strings devem ser preservadas.
11
+
12
+ d_four: "Somos chatos
13
+ gostamos de várias linhas\"
14
+ e ainda usamos escaping
15
+ para fuder"
16
+ e_five: Prefirimos strings sem
17
+ nenhum tipo de especificação.
18
+ c_three:
19
+ a: "Marcelo"
20
+ d_4: Rafael
21
+ # Comentário de muitas
22
+ # linhas (goes with d_4)
23
+ "b": Bernardo
24
+ c_3:
25
+ unify: Luiz
26
+ klass: Lucas
27
+ f_six: Dalton
28
+ a_one: José # Comentado
29
+ c_three: Gustavo
30
+ # Comentário (goes with c_three)
31
+ f_six: Thiago
32
+ e_five:
33
+ b_two: Catarina
34
+ a_one: Rodrigo
35
+ # Comentário (goes with a_one)
36
+ "g-seven": Marcelo
37
+ d_four: Oliveira
38
+ k_eleven: Ronaldo
39
+ "l_other123123": Emilio
40
+ i_nine: Prestes
41
+ j_ten: >
42
+ Pior quando resolvemos escrever
43
+ assim, impossível aturar!
44
+ h_eight: "Jonivildo"
@@ -0,0 +1,44 @@
1
+ a_one: José # Comentado
2
+ b_two:
3
+ a_1: Joãozinho
4
+ b_two: |
5
+ Nos somos legais, como
6
+ por exemplo: João,
7
+ que: gosta de nos dar milhões
8
+ em dinheiro todos os dias.
9
+
10
+ Linhas em Branco com tabulação
11
+ nos strings devem ser preservadas.
12
+
13
+ d_four: "Somos chatos
14
+ gostamos de várias linhas\"
15
+ e ainda usamos escaping
16
+ para fuder"
17
+ c_three:
18
+ a: "Marcelo"
19
+ "b": Bernardo
20
+ c_3:
21
+ klass: Lucas
22
+ unify: Luiz
23
+ d_4: Rafael
24
+ # Comentário de muitas
25
+ # linhas (goes with d_4)
26
+ e_five: Prefirimos strings sem
27
+ nenhum tipo de especificação.
28
+ f_six: Dalton
29
+ c_three: Gustavo
30
+ # Comentário (goes with c_three)
31
+ d_four: Oliveira
32
+ e_five:
33
+ a_one: Rodrigo
34
+ # Comentário (goes with a_one)
35
+ b_two: Catarina
36
+ f_six: Thiago
37
+ "g-seven": Marcelo
38
+ h_eight: "Jonivildo"
39
+ i_nine: Prestes
40
+ j_ten: >
41
+ Pior quando resolvemos escrever
42
+ assim, impossível aturar!
43
+ k_eleven: Ronaldo
44
+ "l_other123123": Emilio
@@ -0,0 +1,19 @@
1
+ require 'helper'
2
+
3
+ class TestI18nYamlSorter < Test::Unit::TestCase
4
+ def test_should_sort_complex_sample_file
5
+ open('in.yml') do |file|
6
+ sorter = I18nYamlSorter.new(file)
7
+ open('out.yml') do |expected_out|
8
+ assert_equal sorter.sort, expected_out.read
9
+ end
10
+ end
11
+ end
12
+
13
+ def test_command_line_should_work_in_stdin
14
+ output = `../bin/sort_yaml < in.yml`
15
+ open('out.yml') do |expected_out|
16
+ assert_equal output, expected_out.read
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: i18n_yaml_sorter
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - "Bernardo de P\xC3\xA1dua"
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-11 00:00:00 -03:00
18
+ default_executable: sort_yaml
19
+ dependencies: []
20
+
21
+ description: " Allows you to deep sort YAML files that are mainly composed of \n nested hashes and string values. Great to sort your rails I18n YAML files. You can easily\n add it to a textmate bundle, rake task, or just use the included regular comand line tool. \n "
22
+ email: berpasan@gmail.com
23
+ executables:
24
+ - sort_yaml
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - LICENSE
29
+ - README.rdoc
30
+ files:
31
+ - .document
32
+ - .gitignore
33
+ - LICENSE
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - bin/sort_yaml
38
+ - i18n_yaml_sorter.gemspec
39
+ - lib/i18n_yaml_sorter.rb
40
+ - test/helper.rb
41
+ - test/in.yml
42
+ - test/out.yml
43
+ - test/test_i18n_yaml_sorter.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/redealumni/i18n_yaml_sorter
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --charset=UTF-8
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.6
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: A I18n YAML deep sorter that will keep your locales organized and not screw up your text formating
74
+ test_files:
75
+ - test/helper.rb
76
+ - test/test_i18n_yaml_sorter.rb