16watts-fluently 0.3.0

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