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 +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +59 -0
- data/Rakefile +54 -0
- data/VERSION.yml +4 -0
- data/fluently.gemspec +62 -0
- data/lib/fluently/cocoa_marshal.rb +46 -0
- data/lib/fluently/connection.rb +149 -0
- data/lib/fluently/i18n/backend.rb +123 -0
- data/lib/fluently/i18n.rb +7 -0
- data/lib/fluently/rails_marshal.rb +17 -0
- data/lib/fluently/rake/base/task.rb +40 -0
- data/lib/fluently/rake/cocoa/task.rb +31 -0
- data/lib/fluently/rake/rails/task.rb +31 -0
- data/lib/fluently/reader.rb +85 -0
- data/lib/fluently.rb +2 -0
- data/spec/fluently_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -0
- metadata +92 -0
    
        data/.document
    ADDED
    
    
    
        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
    
    
    
        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,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
    
    
    
        data/spec/spec_helper.rb
    ADDED
    
    
    
        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
         |