16watts-fluently 0.3.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.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 16watts
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.
data/README.rdoc ADDED
@@ -0,0 +1,59 @@
1
+ = fluently
2
+
3
+ Fluently (http://fluent.ly/) helps you translate your application
4
+ into other languages. The fluently gem provides a rake task that
5
+ will sync your project's default locale up to the Fluently servers,
6
+ and conversely sync any translations on the Fluently servers in to
7
+ your local codebase.
8
+
9
+ == Install
10
+
11
+ $ gem sources -a http://gems.github.com (you only have to do this once)
12
+ $ sudo gem install 16watts-fluently
13
+
14
+
15
+ == Setup
16
+
17
+ Put the following in a Rakefile in the root of your project.
18
+
19
+ === Cocoa project
20
+
21
+ require 'rubygems'
22
+ require 'fluently'
23
+ require 'fluently/rake/cocoa/task'
24
+ Fluently::Rake::Task.new do |t|
25
+ t.api_key = "abc123"
26
+ t.api_secret = "xyz456"
27
+ end
28
+
29
+ === Ruby on Rails project
30
+
31
+ require 'rubygems'
32
+ require 'fluently'
33
+ require 'fluently/rake/rails/task'
34
+ Fluently::Rake::Task.new do |t|
35
+ t.api_key = "abc123"
36
+ t.api_secret = "xyz456"
37
+ end
38
+
39
+
40
+ == Usage
41
+
42
+ $ rake fluently:sync
43
+
44
+ or simply
45
+
46
+ $ rake fluently
47
+
48
+
49
+ == Other project types
50
+
51
+ Usually this is as easy as examining the Marshal objects included with
52
+ this gem. We'll incorporate Marshals written by the community in
53
+ future versions -- just fork this project on github and issue a pull
54
+ request.
55
+
56
+
57
+ == Copyright
58
+
59
+ Copyright (c) 2009 16watts. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "fluently"
8
+ gem.summary = %Q{Fluently helps you translate your appliation. http://fluent.ly/}
9
+ gem.email = "bob@zoller.us"
10
+ gem.homepage = "http://github.com/bzoller/fluently"
11
+ gem.authors = ["Bob Zoller"]
12
+
13
+ gem.add_dependency('json', '>= 1.1.4')
14
+ gem.add_dependency('ezcrypto', '>= 0.7.2')
15
+
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
+ end
21
+
22
+ begin
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :default => :spec
36
+ rescue LoadError
37
+ # rspec not available
38
+ end
39
+
40
+ require 'rake/rdoctask'
41
+ Rake::RDocTask.new do |rdoc|
42
+ if File.exist?('VERSION.yml')
43
+ config = YAML.load(File.read('VERSION.yml'))
44
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
45
+ else
46
+ version = ""
47
+ end
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "fluently #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
54
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 3
4
+ :patch: 0
data/fluently.gemspec ADDED
@@ -0,0 +1,62 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{fluently}
5
+ s.version = "0.3.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Bob Zoller"]
9
+ s.date = %q{2009-06-07}
10
+ s.email = %q{bob@zoller.us}
11
+ s.extra_rdoc_files = [
12
+ "LICENSE",
13
+ "README.rdoc"
14
+ ]
15
+ s.files = [
16
+ ".document",
17
+ ".gitignore",
18
+ "LICENSE",
19
+ "README.rdoc",
20
+ "Rakefile",
21
+ "VERSION.yml",
22
+ "fluently.gemspec",
23
+ "lib/fluently.rb",
24
+ "lib/fluently/cocoa_marshal.rb",
25
+ "lib/fluently/connection.rb",
26
+ "lib/fluently/i18n.rb",
27
+ "lib/fluently/i18n/backend.rb",
28
+ "lib/fluently/rails_marshal.rb",
29
+ "lib/fluently/rake/base/task.rb",
30
+ "lib/fluently/rake/cocoa/task.rb",
31
+ "lib/fluently/rake/rails/task.rb",
32
+ "lib/fluently/reader.rb",
33
+ "spec/fluently_spec.rb",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.has_rdoc = true
37
+ s.homepage = %q{http://github.com/bzoller/fluently}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.2}
41
+ s.summary = %q{Fluently helps you translate your appliation. http://fluent.ly/}
42
+ s.test_files = [
43
+ "spec/fluently_spec.rb",
44
+ "spec/spec_helper.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ s.add_runtime_dependency(%q<json>, [">= 1.1.4"])
53
+ s.add_runtime_dependency(%q<ezcrypto>, [">= 0.7.2"])
54
+ else
55
+ s.add_dependency(%q<json>, [">= 1.1.4"])
56
+ s.add_dependency(%q<ezcrypto>, [">= 0.7.2"])
57
+ end
58
+ else
59
+ s.add_dependency(%q<json>, [">= 1.1.4"])
60
+ s.add_dependency(%q<ezcrypto>, [">= 0.7.2"])
61
+ end
62
+ end
@@ -0,0 +1,46 @@
1
+ require 'iconv'
2
+ # cocoa string files are assumed to be UTF-16. the cmess gem
3
+ # can guess at encoding, which might be helpful.
4
+ #require 'cmess/guess_encoding'
5
+ #charset = CMess::GuessEncoding::Automatic.guess(input)
6
+
7
+ module Fluently
8
+ class CocoaMarshal
9
+ def self.read(filename)
10
+ hash = {}
11
+ File.open(filename, 'r') do |f|
12
+ content = Iconv.conv('UTF-8', 'UTF-16', f.read)
13
+ content.gsub!(%r{//.*$}, '') # ignore comments
14
+ content.scan(/"(.*?)"\s*=\s*"(.*?)"\s*;/) do |key, value|
15
+ key = self.read_string(key)
16
+ value = self.read_string(value)
17
+ hash[key] = value
18
+ end
19
+ end
20
+ hash
21
+ end
22
+
23
+ def self.write(filename, hash)
24
+ File.open(filename, 'w') do |f|
25
+ f.puts("/* DO NOT EDIT THIS FILE - YOUR CHANGES WILL BE LOST */\n")
26
+ lines = hash.map do |key, value|
27
+ %{"#{self.write_string(key)}" = "#{self.write_string(value)}";}
28
+ end
29
+ f.write Iconv.conv('UTF-16', 'UTF-8', lines.join("\n"))
30
+ end
31
+ File.chmod(0444, filename)
32
+ end
33
+
34
+ protected
35
+
36
+ # unquote quoted quotes
37
+ def self.read_string(str)
38
+ return str.to_s.gsub(/([^\\])[\\]"/, '\\1"')
39
+ end
40
+
41
+ # quote quotes
42
+ def self.write_string(str)
43
+ return str.to_s.gsub(/"/, '\\"')
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,149 @@
1
+ require 'cgi'
2
+ require 'uri'
3
+ require 'net/https'
4
+ require 'net/http'
5
+ require 'benchmark'
6
+
7
+ # other gems
8
+ require 'json'
9
+ require 'ezcrypto'
10
+
11
+ module Fluently
12
+ class Connection
13
+
14
+ # Base URL of the API
15
+ attr_accessor :site
16
+
17
+ # Fluently API key
18
+ attr_accessor :api_key
19
+
20
+ # Fluently API secret
21
+ attr_accessor :api_secret
22
+
23
+ # HTTP Timeout
24
+ attr_accessor :timeout
25
+
26
+ class << self
27
+ attr_accessor :site
28
+
29
+ def site=(new_site)
30
+ @site = URI.parse(new_site)
31
+ end
32
+ end
33
+
34
+ self.site = "http://fluent.ly/api/v1"
35
+
36
+ def initialize(api_key, api_secret)
37
+ self.api_key = api_key
38
+ self.api_secret = api_secret
39
+ self.site = self.class.site
40
+ end
41
+
42
+ def locale
43
+ @locale ||= self.get("#{self.prefix}locale.json")['response']['message']
44
+ end
45
+
46
+ def sync!(translations)
47
+ self.post("#{self.prefix}sync.json?#{self.options_query}", JSON.generate({:translations => translations[self.locale]}))['response']['translations']
48
+ end
49
+
50
+ protected
51
+
52
+ def http
53
+ http = Net::HTTP.new(@site.host, @site.port)
54
+ http.use_ssl = @site.is_a?(URI::HTTPS)
55
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl
56
+ http.read_timeout = @timeout if @timeout # If timeout is not set, the default Net::HTTP timeout (60s) is used.
57
+ http
58
+ end
59
+
60
+ def get(path, headers = {})
61
+ JSON.parse(request(:get, path, self.build_request_headers(headers)).body)
62
+ end
63
+
64
+ def post(path, body = '', headers = {})
65
+ result = request(:post, path, body.to_s, self.build_request_headers(headers))
66
+ data = JSON.parse(result.body)
67
+ if result.code.to_i == 200
68
+ data
69
+ else
70
+ raise "ERROR: #{data['response']['message']}"
71
+ end
72
+ end
73
+
74
+ def request(method, path, *arguments)
75
+ path = self.sign_url(path)
76
+
77
+ # do we need to encrypt the body?
78
+ if [:put, :post].include? method
79
+ body = arguments.shift
80
+ key = EzCrypto::Key.with_password(self.api_key, self.api_secret)
81
+ body = key.encrypt64(body)
82
+ arguments.unshift(body)
83
+ end
84
+
85
+ logger.info "#{method.to_s.upcase} #{@site.scheme}://#{@site.host}:#{@site.port}#{path}" if logger
86
+ result = nil
87
+ time = Benchmark.realtime { result = http.send(method, path, *arguments) }
88
+ logger.info "--> #{result.code} #{result.message} (#{result.body.length}b %.2fs)" % time if logger
89
+ handle_response(result)
90
+ end
91
+
92
+ def handle_response(result)
93
+ case result.code.to_i
94
+ when 401
95
+ raise 'ERROR: invalid authentication. check api_key and api_secret'
96
+ else
97
+ begin
98
+ key = EzCrypto::Key.with_password(self.api_key, self.api_secret)
99
+ result.send(:instance_variable_set, '@body', key.decrypt64(result.body))
100
+ rescue OpenSSL::CipherError
101
+ raise "ERROR: response was not encrypted:\n\n#{result.body}"
102
+ end
103
+ end
104
+ result
105
+ end
106
+
107
+ def build_request_headers(headers={})
108
+ headers.update('Content-Type' => 'application/json', 'Accept' => 'application/json', 'X-Signed-Resource' => '1.0')
109
+ end
110
+
111
+ def prefix
112
+ "#{@site.path}/"
113
+ end
114
+
115
+ def options_query
116
+ hash = {}
117
+ hash[:force] = 1 if ENV['FORCE']
118
+ hash_to_query(:options => hash)
119
+ end
120
+
121
+ def hash_to_query(hash, namespace = nil)
122
+ hash.collect do |key, value|
123
+ ns_key = namespace ? "#{namespace}[#{key}]" : key
124
+ if value.kind_of?(Hash)
125
+ hash_to_query(value, ns_key)
126
+ else
127
+ "#{CGI.escape(ns_key.to_s)}=#{CGI.escape(value.to_s)}"
128
+ end
129
+ end.sort * '&'
130
+ end
131
+
132
+ def sign_url(path)
133
+ uri = URI.parse(path)
134
+ uri.query = (uri.query.nil? ? '' : "#{uri.query}&") << "ts=#{Time.now.to_i}&rnd=#{self.random_string(16)}&key=#{self.api_key}"
135
+ hash = Digest::SHA1.hexdigest("#{uri.query}#{api_secret}")
136
+ uri.query << "&sig=#{hash}"
137
+ uri.to_s
138
+ end
139
+
140
+ def random_string(length = 8)
141
+ chars = ("a".."z").to_a + ("0".."9").to_a + ("A".."Z").to_a
142
+ Array.new(length, '').collect{chars[rand(chars.size)]}.join
143
+ end
144
+
145
+ def logger
146
+ nil
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,123 @@
1
+ require 'fluently/rails_marshal'
2
+
3
+ module Fluently
4
+ module I18n
5
+
6
+ class InheritableTranslations
7
+ MAX_LOCALES_SAFEGUARD = 1000
8
+
9
+ def initialize(hash)
10
+ @base = hash
11
+ @expanded = {}
12
+ end
13
+
14
+ def [](locale)
15
+ @expanded[locale] ||= build_expanded_locale(locale)
16
+ end
17
+
18
+ protected
19
+ def build_expanded_locale(locale)
20
+ raise 'hit MAX_LOCALES_SAFEGUARD' if @expanded.size == MAX_LOCALES_SAFEGUARD
21
+ expanded = {}
22
+ self.locale_lookup_chain(locale).reverse.each do |locale|
23
+ expanded = deep_hash_merge(expanded, (@base[locale] || {}))
24
+ end
25
+ expanded
26
+ end
27
+
28
+ # From: http://www.ruby-forum.com/topic/142809
29
+ # Author: Stefan Rusterholz
30
+ def deep_hash_merge(first, second)
31
+ merger = proc { |key,v1,v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
32
+ first.merge(second, &merger)
33
+ end
34
+
35
+ def root_locale(locale)
36
+ locale.to_s.split(/[_-]/)[0]
37
+ end
38
+
39
+ def locale_lookup_chain(locale)
40
+ @i18n_fallback_locales ||= {}
41
+ unless @i18n_fallback_locales[locale.to_sym]
42
+ base_locale = (root_locale(locale) || locale).to_sym
43
+ locales = [locale.to_sym, base_locale]
44
+ @base.keys.each do |l|
45
+ current_base_locale = root_locale(l)
46
+ locales << l if current_base_locale && current_base_locale.to_sym == base_locale
47
+ end
48
+ @i18n_fallback_locales[locale.to_sym] = locales.uniq
49
+ end
50
+ @i18n_fallback_locales[locale.to_sym]
51
+ end
52
+ end
53
+
54
+ class Backend < ::I18n::Backend::Simple
55
+
56
+ # the path to locale directories
57
+ attr_accessor :path
58
+
59
+ # the format of locale directory names
60
+ attr_accessor :dirname_format
61
+
62
+ # the format of string file names
63
+ attr_accessor :filename_format
64
+
65
+ # the marshal that will read and write string files
66
+ attr_accessor :marshal
67
+
68
+ def initialize(*args)
69
+ super
70
+ yield self if block_given?
71
+ self.path ||= File.join(RAILS_ROOT, 'config', 'locales')
72
+ self.dirname_format ||= '*'
73
+ self.filename_format ||= '*.yml'
74
+ self.marshal = Fluently::RailsMarshal
75
+ end
76
+
77
+ def load_translations(*args)
78
+ super
79
+ self.reader.translations.each do |locale, translations|
80
+ self.store_translations(locale, translations)
81
+ end
82
+ end
83
+
84
+ def reload!
85
+ super
86
+ @interhitable_translations = nil
87
+ end
88
+
89
+
90
+ protected
91
+ def inheritable_translations
92
+ @inheritable_translations ||= InheritableTranslations.new(self.translations)
93
+ end
94
+
95
+ # Looks up a translation from the translations hash. Returns nil if
96
+ # eiher key is nil, or locale, scope or key do not exist as a key in the
97
+ # nested translations hash. Splits keys or scopes containing dots
98
+ # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
99
+ # <tt>%w(currency format)</tt>.
100
+ def lookup(locale, key, scope = [])
101
+ return unless key
102
+ init_translations unless initialized?
103
+ keys = ::I18n.send(:normalize_translation_keys, locale, key, scope)
104
+ keys.inject(inheritable_translations) do |result, k|
105
+ if (x = result[k.to_sym]).nil?
106
+ return nil
107
+ else
108
+ x
109
+ end
110
+ end
111
+ end
112
+
113
+ def reader
114
+ Fluently::Reader.new do |reader|
115
+ reader.path = self.path
116
+ reader.dirname_format = self.dirname_format
117
+ reader.filename_format = self.filename_format
118
+ reader.marshal = self.marshal
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,7 @@
1
+ module Fluently
2
+ module I18n
3
+ end
4
+ end
5
+ require 'rubygems'
6
+ require 'i18n'
7
+ require 'fluently/i18n/backend'
@@ -0,0 +1,17 @@
1
+ require 'yaml'
2
+
3
+ module Fluently
4
+ class RailsMarshal
5
+ def self.read(filename)
6
+ YAML.load_file(filename)
7
+ end
8
+
9
+ def self.write(filename, hash)
10
+ File.open(filename, 'w') do |f|
11
+ f.puts("# DO NOT EDIT THIS FILE - YOUR CHANGES WILL BE LOST #\n")
12
+ YAML.dump(hash, f)
13
+ end
14
+ File.chmod(0444, filename)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,40 @@
1
+ module Fluently
2
+ module Rake
3
+ class BaseTask
4
+
5
+ # Fluently API key
6
+ attr_accessor :api_key
7
+ # Fluently API secret
8
+ attr_accessor :api_secret
9
+
10
+ # Define a Rake
11
+ def initialize(namespace = "fluently")
12
+ @namespace = namespace
13
+ yield self if block_given?
14
+ define_task
15
+ end
16
+
17
+ def define_task # :nodoc:
18
+ namespace @namespace do
19
+ desc "Perform a sync with Fluently"
20
+ task :sync do
21
+ reader = self.build_reader
22
+ connection = Fluently::Connection.new(self.api_key, self.api_secret)
23
+ new_translations = connection.sync!(reader.translations)
24
+ if !new_translations || new_translations.empty?
25
+ puts "no translations yet."
26
+ else
27
+ reader.translations = new_translations
28
+ end
29
+ end
30
+ end
31
+
32
+ task @namespace => ["#{@namespace}:sync"]
33
+ end
34
+
35
+ def build_reader
36
+ Reader.new
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ require 'fluently/rake/base/task'
2
+ require 'fluently/cocoa_marshal'
3
+
4
+ module Fluently
5
+ module Rake
6
+ # Defines a Rake task for syncing string files.
7
+ #
8
+ # The simplest use of it goes something like:
9
+ #
10
+ # Fluently::Rake::Task.new do |t|
11
+ # t.api_key = "abc123"
12
+ # t.api_secret = "xyz456"
13
+ # end
14
+ #
15
+ # This will create a task named 'fluently' described as 'Perform a sync with
16
+ # Fluently'. It assumes string files all end in ".strings" and live underneath
17
+ # directories called "<lang>.lproj" , ex: "en.lproj"
18
+ #
19
+ # See the attributes for additional configuration possibilities.
20
+ class Task < BaseTask
21
+ def build_reader
22
+ Reader.new do |r|
23
+ r.path = '.'
24
+ r.dirname_format = '*.lproj'
25
+ r.filename_format = '*.strings'
26
+ r.marshal = CocoaMarshal
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'fluently/rake/base/task'
2
+ require 'fluently/rails_marshal'
3
+
4
+ module Fluently
5
+ module Rake
6
+ # Defines a Rake task for syncing string files.
7
+ #
8
+ # The simplest use of it goes something like:
9
+ #
10
+ # Fluently::Rake::Task.new do |t|
11
+ # t.api_key = "abc123"
12
+ # t.api_secret = "xyz456"
13
+ # end
14
+ #
15
+ # This will create a task named 'fluently' described as 'Perform a sync with
16
+ # Fluently'. It assumes string files all end in ".yml" and live underneath
17
+ # directories called "<locale>" , ex: "en-US"
18
+ #
19
+ # See the attributes for additional configuration possibilities.
20
+ class Task < BaseTask
21
+ def build_reader
22
+ Reader.new do |r|
23
+ r.path = File.join(RAILS_ROOT, 'config', 'locales')
24
+ r.dirname_format = '*'
25
+ r.filename_format = '*.yml'
26
+ r.marshal = RailsMarshal
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,85 @@
1
+ require 'fileutils'
2
+
3
+ module Fluently
4
+ class Reader
5
+
6
+ # the path to locale directories
7
+ attr_accessor :path
8
+
9
+ # the format of locale directory names
10
+ attr_accessor :dirname_format
11
+
12
+ # the format of string file names
13
+ attr_accessor :filename_format
14
+
15
+ # the marshal that will read and write string files
16
+ attr_accessor :marshal
17
+
18
+ def initialize
19
+ yield self if block_given?
20
+ end
21
+
22
+ def translations
23
+ @translations ||= read_translations
24
+ end
25
+
26
+ def translations=(new_translations)
27
+ @translations = new_translations
28
+ write_translations
29
+ end
30
+
31
+ %w{path dirname_format filename_format marshal}.each do |method|
32
+ class_eval <<-EOF
33
+ def #{method}=(new_value)
34
+ @translations = nil # reset memo
35
+ @#{method} = new_value
36
+ end
37
+ EOF
38
+ end
39
+
40
+
41
+ protected
42
+
43
+ def read_translations
44
+ data = {}
45
+ self.read_locale_files do |locale, section, translations|
46
+ data[locale] ||= {}
47
+ raise "duplicate section #{section} for locale #{locale}" if data[locale][section]
48
+ data[locale][section] = translations
49
+ end
50
+ data
51
+ end
52
+
53
+ def write_translations
54
+ self.translations.each do |locale, sections|
55
+ locale_dir = File.join(self.path, self.dirname_format.sub('*', locale))
56
+ FileUtils.mkdir_p(locale_dir) unless File.exist?(locale_dir)
57
+ puts "#{File.basename(locale_dir)}#{File::SEPARATOR}"
58
+ sections.each do |section, translations|
59
+ section_file = File.join(locale_dir, self.filename_format.sub('*', section))
60
+ self.marshal.write(section_file, translations)
61
+ puts " #{File.basename(section_file)}"
62
+ end
63
+ end
64
+ end
65
+
66
+ def good_config?
67
+ !!(self.path && self.dirname_format && self.filename_format && self.marshal)
68
+ end
69
+
70
+ def read_locale_files
71
+ raise 'bad config' unless self.good_config?
72
+ Dir.glob(File.join(self.path, self.dirname_format)).each do |locale_path|
73
+ next unless File.directory?(locale_path)
74
+ locale = File.basename(locale_path).match(/^#{self.dirname_format.sub('*', '(.*)')}$/)[1]
75
+ Dir.glob(File.join(locale_path, self.filename_format)).each do |locale_file|
76
+ next unless File.file?(locale_file)
77
+ section = File.basename(locale_file).match(/^#{self.filename_format.sub('*', '(.*)')}$/)[1]
78
+ data = self.marshal.read(locale_file)
79
+ yield [locale, section, data]
80
+ end
81
+ end
82
+ end
83
+
84
+ end
85
+ end
data/lib/fluently.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'fluently/connection'
2
+ require 'fluently/reader'
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fluently" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec'
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ require 'fluently'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 16watts-fluently
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Bob Zoller
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-07 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.4
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: ezcrypto
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.7.2
34
+ version:
35
+ description:
36
+ email: bob@zoller.us
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION.yml
51
+ - fluently.gemspec
52
+ - lib/fluently.rb
53
+ - lib/fluently/cocoa_marshal.rb
54
+ - lib/fluently/connection.rb
55
+ - lib/fluently/i18n.rb
56
+ - lib/fluently/i18n/backend.rb
57
+ - lib/fluently/rails_marshal.rb
58
+ - lib/fluently/rake/base/task.rb
59
+ - lib/fluently/rake/cocoa/task.rb
60
+ - lib/fluently/rake/rails/task.rb
61
+ - lib/fluently/reader.rb
62
+ - spec/fluently_spec.rb
63
+ - spec/spec_helper.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/bzoller/fluently
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --charset=UTF-8
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.2.0
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Fluently helps you translate your appliation. http://fluent.ly/
90
+ test_files:
91
+ - spec/fluently_spec.rb
92
+ - spec/spec_helper.rb