Compare commits

...

40 Commits

Author SHA1 Message Date
Charlie Somerville
7403667b89 Merge pull request #67 from github/2-3-github+cve-2014-0130
CVE-2014-0130 protection
2014-05-10 00:52:08 +10:00
Charlie Somerville
1a45ec57bf CVE-2014-0130 protection 2014-05-09 23:55:20 +10:00
Patrick Toomey
1e6e438f6e Update RAILS_VERSION 2014-04-24 10:45:25 -05:00
Patrick Toomey
2b01f832a3 Merge pull request #63 from github/memoize-nil-check
make sure nil isn't memoized and frozen
2014-04-24 10:44:47 -05:00
Patrick Toomey
1e5fda763e backport memoiziation from 3.0 2014-04-24 10:24:04 -05:00
Dirkjan Bussink
7c3d4ec43c Bump version 2014-03-31 13:43:08 +02:00
Dirkjan Bussink
7343ed7b05 Merge pull request #53 from github/dbussink/no-toplevel-exception-rescue
We shouldn't try to rescue every type of exception here
2014-03-31 11:42:02 +00:00
Dirkjan Bussink
2a70c9691d We shouldn't try to rescue every type of exception here 2014-03-31 13:35:28 +02:00
Dirkjan Bussink
a141d9de0d bump 2.3.14.github41 2014-03-27 13:55:26 +01:00
Dirkjan Bussink
74492f43a8 Merge pull request #51 from github/dbussink/fix-logging-frozen-string-query
Dup string before changing encoding because it might be frozen
2014-03-27 12:53:25 +00:00
Dirkjan Bussink
c2894170bf Dup string before changing encoding because it might be frozen
Calling String#force_encoding! on a frozen string throws an exception.
By dupping the string we prevent this from happening.
2014-03-27 13:47:08 +01:00
Charlie Somerville
057aed6e18 Merge pull request #48 from github/2-3-kill-backtick-monkey-patch
[2.3] Kill Object#` monkey patch
2014-02-23 23:20:47 +11:00
Charlie Somerville
02fc012b42 kill Object#` monkey patch 2014-02-23 23:17:44 +11:00
Mastahyeti
4fdaf21b28 bump 2.3.14.github40 2014-02-18 15:28:32 -06:00
Ben Toews
35b871fbcd Merge pull request #47 from github/CVE-2014-0081
CVE-2014-0081
2014-02-18 15:28:00 -06:00
Mastahyeti
a5697840d6 escape format for CVE-2014-0081 2014-02-18 15:25:05 -06:00
Andy Lindeman
d0e554d231 Bumps to github39 2014-02-14 00:12:09 -05:00
Andy Lindeman
d38b7664cc github38 2014-02-13 22:36:14 -05:00
Andy Lindeman
e4cd9caf02 Merge pull request #46 from github/runtime_header
Removes the X-Runtime header from ActionController::Benchmarking
2014-02-13 22:34:18 -05:00
Andy Lindeman
89e4514704 Removes the X-Runtime header from ActionController::Benchmarking
The `Rack::Runtime` middleware now provides this header
2014-02-13 22:25:27 -05:00
Joshua Peek
0a0d975f51 github37 2014-02-11 23:36:30 -06:00
Joshua Peek
62daf4cb6f Merge pull request #45 from github/rack-session-skip
Backport Rack session skip
2014-02-11 23:34:52 -06:00
Joshua Peek
24711e1e29 Backport env['rack.session.options'][:skip] 2014-02-11 23:22:39 -06:00
Charlie Somerville
cf8f36930c Merge pull request #44 from github/builder-3.2.2
Builder 3.2.2
2014-01-20 19:55:16 -08:00
Charlie Somerville
d622643e47 fix tests 2014-01-21 14:30:00 +11:00
Charlie Somerville
3f0241a613 use assert_includes so we get a useful failure message 2014-01-21 14:28:01 +11:00
Charlie Somerville
38a7432590 add builder 3.2.2 to Gemfile.sh and use that instead of vendored copy 2014-01-21 14:22:20 +11:00
Charlie Somerville
1220d3c3ed delete vendored builder 2.1.2 2014-01-21 14:16:36 +11:00
Charlie Somerville
3d72818356 Merge pull request #43 from github/i18n-0.6.9
i18n 0.4.1
2014-01-20 18:05:20 -08:00
Charlie Somerville
221477dc21 fix this bit 2014-01-21 12:51:40 +11:00
Charlie Somerville
975155c110 use i18n 0.6.9 as a gem 2014-01-21 12:33:39 +11:00
Charlie Somerville
2931987892 delete vendored i18n-0.4.1 2014-01-21 12:25:44 +11:00
Charlie Somerville
e3290b98dd Merge pull request #42 from github/dont-test-on-1.9
Don't run tests on 1.9.3
2014-01-20 17:25:13 -08:00
Charlie Somerville
20088080a5 Don't run tests on 1.9.3 2014-01-21 12:23:05 +11:00
Dirkjan Bussink
24e348489d github36 2014-01-17 18:10:31 +01:00
Dirkjan Bussink
ba4f4f8a01 Treat mysql in the name as a mysql driver 2014-01-17 18:05:20 +01:00
Aman Gupta
ccf254b6cb build using 2.1 first 2014-01-15 01:10:18 -08:00
Mislav Marohnić
3766b1b377 github35 2014-01-13 13:58:57 -08:00
Mislav Marohnić
d3f87776a3 Merge pull request #41 from github/disable-generated-id
Disable auto-generated form field IDs by passing nil for "id" attribute
2014-01-13 13:57:38 -08:00
Mislav Marohnić
18c7c1f753 Disable auto-generated form field IDs by passing nil for "id" attribute
Previously it was not possible to opt out of auto-generated ID values
for various form fields.
2014-01-13 13:22:06 -08:00
62 changed files with 105 additions and 3946 deletions

View File

@@ -5,3 +5,5 @@ gem install sqlite3 -v=1.3.7
gem install rack -v=1.4.5
gem install erubis -v=2.7.0
gem install json -v=1.8.0
gem install i18n -v=0.6.9
gem install builder -v=3.2.2

View File

@@ -1 +1 @@
2.3.14.github34
2.3.14.github43

View File

@@ -1320,7 +1320,14 @@ module ActionController #:nodoc:
render
end
CVE_2014_0310 = Class.new(StandardError)
def perform_action
# CVE-2014-0130 protection
if action_name.include? "/"
raise CVE_2014_0310
end
if action_methods.include?(action_name)
send(action_name)
default_render unless performed?

View File

@@ -87,7 +87,6 @@ module ActionController #:nodoc:
log_message << " [#{complete_request_uri rescue "unknown"}]"
logger.info(log_message)
response.headers["X-Runtime"] = "%.0f" % ms
else
perform_action_without_benchmark
end

View File

@@ -37,7 +37,7 @@ module ActionController
# Note that changing digest or secret invalidates all existing sessions!
class CookieStore
include AbstractStore::SessionUtils
# Cookies can typically store 4096 bytes.
MAX = 4096
SECRET_MIN_LENGTH = 30 # characters
@@ -95,14 +95,21 @@ module ActionController
def call(env)
prepare!(env)
status, headers, body = @app.call(env)
session_data = env[ENV_SESSION_KEY]
options = env[ENV_SESSION_OPTIONS_KEY]
request = ActionController::Request.new(env)
if !(options[:secure] && !request.ssl?) && (!session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after])
# Backport standard Rack::Session::Cookie behavior
# Skip writing session if env['rack.session.options'][:skip] is set
if options[:skip]
return [status, headers, body]
end
session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
persistent_session_id!(session_data)
@@ -122,7 +129,7 @@ module ActionController
end
private
def prepare!(env)
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = AbstractStore::OptionsHash.new(self, env, @default_options)
@@ -133,7 +140,7 @@ module ActionController
data = persistent_session_id!(data)
[data[:session_id], data]
end
def extract_session_id(env)
if data = unpacked_cookie_data(env)
persistent_session_id!(data) unless data.empty?

View File

@@ -768,7 +768,11 @@ module ActionView
options = options.stringify_keys
tag_value = options.delete("value")
name_and_id = options.dup
name_and_id["id"] = name_and_id["for"]
if name_and_id.has_key?("for")
name_and_id["id"] = name_and_id["for"]
else
name_and_id.delete("id")
end
add_default_name_and_id_for_value(tag_value, name_and_id)
options.delete("index")
options["for"] ||= name_and_id["id"]
@@ -928,15 +932,15 @@ module ActionView
def add_default_name_and_id(options)
if options.has_key?("index")
options["name"] ||= tag_name_with_index(options["index"])
options["id"] ||= tag_id_with_index(options["index"])
options["name"] = tag_name_with_index(options["index"]) unless options.has_key?("name")
options["id"] = tag_id_with_index(options["index"]) unless options.has_key?("id")
options.delete("index")
elsif defined?(@auto_index)
options["name"] ||= tag_name_with_index(@auto_index)
options["id"] ||= tag_id_with_index(@auto_index)
options["name"] = tag_name_with_index(@auto_index) unless options.has_key?("name")
options["id"] = tag_id_with_index(@auto_index) unless options.has_key?("id")
else
options["name"] ||= tag_name + (options.has_key?('multiple') ? '[]' : '')
options["id"] ||= tag_id
options["name"] = tag_name + (options.has_key?('multiple') ? '[]' : '') unless options.has_key?("name")
options["id"] = tag_id unless options.has_key?("id")
end
end

View File

@@ -73,6 +73,8 @@ module ActionView
def number_to_currency(number, options = {})
options.symbolize_keys!
options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
currency = I18n.translate(:'number.currency.format', :locale => options[:locale], :raise => true) rescue {}
defaults = defaults.merge(currency)

View File

@@ -175,6 +175,10 @@ class FormHelperTest < ActionView::TestCase
I18n.locale = old_locale
end
def test_label_with_for_attribute_as_nil
assert_dom_equal('<label>Title</label>', label(:post, :title, nil, :for => nil))
end
def test_label_with_for_attribute_as_symbol
assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for"))
end
@@ -274,6 +278,11 @@ class FormHelperTest < ActionView::TestCase
hidden_field("post", "title", :value => "Something Else")
end
def test_text_field_with_id_as_nil
assert_dom_equal '<input name="post[title]" type="hidden" value="Hello World" />',
hidden_field("post", "title", :id => nil)
end
def test_check_box
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',

View File

@@ -3,6 +3,12 @@ require 'abstract_unit'
class NumberHelperTest < ActionView::TestCase
tests ActionView::Helpers::NumberHelper
def test_number_helpers_escape_delimiter_and_separator
assert_equal "$1&lt;script&gt;&lt;/script&gt;01", number_to_currency(1.01, :separator => "<script></script>")
assert_equal "$1&lt;script&gt;&lt;/script&gt;000.00", number_to_currency(1000, :delimiter => "<script></script>")
assert_equal "&lt;script&gt;1,000.00$&lt;/script&gt;", number_to_currency(1000, :format => "<script>%n%u</script>")
end
def test_number_to_phone
assert_equal("555-1234", number_to_phone(5551234))
assert_equal("800-555-1212", number_to_phone(8005551212))

View File

@@ -9,8 +9,8 @@ module RenderTestCases
# Reload and register danish language for testing
I18n.reload!
I18n.backend.store_translations 'da', {}
I18n.backend.store_translations 'pt-BR', {}
I18n.backend.store_translations 'da', 'da' => {}
I18n.backend.store_translations 'pt-BR', 'pt-BR' => {}
# Ensure original are still the same since we are reindexing view paths
assert_equal ORIGINAL_LOCALES, I18n.available_locales.map(&:to_s).sort

View File

@@ -195,7 +195,9 @@ module ActiveRecord
def log_info(sql, name, ms)
if @logger && @logger.debug?
name = '%s (%.1fms)' % [name || 'SQL', ms]
sql.force_encoding 'binary' if sql.respond_to?(:force_encoding)
if sql.respond_to?(:force_encoding)
sql = sql.dup.force_encoding 'binary'
end
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
end
end
@@ -212,13 +214,7 @@ module ActiveRecord
log_info(sql, name, 0)
nil
end
rescue SystemExit, SignalException, NoMemoryError => e
# Don't re-wrap these exceptions. They are probably not being caused by invalid
# sql, but rather some external stimulus beyond the responsibilty of this code.
# Additionaly, wrapping these exceptions with StatementInvalid would lead to
# meaningful loss of data, such as losing SystemExit#status.
raise e
rescue Exception => e
rescue => e
# Log message and raise exception.
# Set last_verification to 0, so that connection gets verified
# upon reentering the request loop

View File

@@ -1,5 +1,4 @@
require 'active_support/core_ext/kernel/daemonizing'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/agnostics'
require 'active_support/core_ext/kernel/requires'
require 'active_support/core_ext/kernel/debugger'

View File

@@ -1,11 +0,0 @@
class Object
# Makes backticks behave (somewhat more) similarly on all platforms.
# On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
# spawned shell prints a message to stderr and sets $?. We emulate
# Unix on the former but not the latter.
def `(command) #:nodoc:
super
rescue Errno::ENOENT => e
STDERR.puts "#$0: #{e}"
end
end

View File

@@ -1,3 +1,6 @@
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/module/aliasing'
module ActiveSupport
module Memoizable
def self.memoized_ivar_for(symbol)
@@ -41,10 +44,10 @@ module ActiveSupport
end
end
def flush_cache(*syms, &block)
def flush_cache(*syms)
syms.each do |sym|
(methods + private_methods + protected_methods).each do |m|
if m.to_s =~ /^_unmemoized_(#{sym})/
if m.to_s =~ /^_unmemoized_(#{sym.to_s.gsub(/\?\Z/, '\?')})/
ivar = ActiveSupport::Memoizable.memoized_ivar_for($1)
instance_variable_get(ivar).clear if instance_variable_defined?(ivar)
end
@@ -69,7 +72,7 @@ module ActiveSupport
if instance_method(:#{symbol}).arity == 0 # if instance_method(:mime_type).arity == 0
def #{symbol}(reload = false) # def mime_type(reload = false)
if reload || !defined?(#{memoized_ivar}) || #{memoized_ivar}.empty? # if reload || !defined?(@_memoized_mime_type) || @_memoized_mime_type.empty?
#{memoized_ivar} = [#{original_method}.freeze] # @_memoized_mime_type = [_unmemoized_mime_type.freeze]
#{memoized_ivar} = [#{original_method}] # @_memoized_mime_type = [_unmemoized_mime_type]
end # end
#{memoized_ivar}[0] # @_memoized_mime_type[0]
end # end
@@ -82,7 +85,7 @@ module ActiveSupport
if !reload && #{memoized_ivar}.has_key?(args) # if !reload && @_memoized_mime_type.has_key?(args)
#{memoized_ivar}[args] # @_memoized_mime_type[args]
elsif #{memoized_ivar} # elsif @_memoized_mime_type
#{memoized_ivar}[args] = #{original_method}(*args).freeze # @_memoized_mime_type[args] = _unmemoized_mime_type(*args).freeze
#{memoized_ivar}[args] = #{original_method}(*args) # @_memoized_mime_type[args] = _unmemoized_mime_type(*args)
end # end
else # else
#{original_method}(*args) # _unmemoized_mime_type(*args)

View File

@@ -1,11 +1,6 @@
# Prefer gems to the bundled libs.
require 'rubygems'
begin
gem 'builder', '~> 2.1.2'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/builder-2.1.2"
end
require 'builder'
begin
@@ -16,11 +11,6 @@ end
$:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.12"
begin
gem 'i18n', '>= 0.4.1'
rescue Gem::LoadError
$:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.4.1"
end
require 'i18n'
module I18n

View File

@@ -1,13 +0,0 @@
#!/usr/bin/env ruby
#--
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
require 'builder/xmlmarkup'
require 'builder/xmlevents'

View File

@@ -1,248 +0,0 @@
#!/usr/bin/env ruby
#--
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
# Copyright 2005 by Scott Barron (scott@elitists.net).
# All rights reserved.
#
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#
# Much of this is taken from Jim's work in xmlbase.rb and xmlmarkup.rb.
# Documentation has also been copied and pasted and modified to reflect
# that we're building CSS here instead of XML. Jim is conducting the
# orchestra here and I'm just off in the corner playing a flute.
#++
# Provide a flexible and easy to use Builder for creating Cascading
# Style Sheets (CSS).
module Builder
# Create a Cascading Style Sheet (CSS) using Ruby.
#
# Example usage:
#
# css = Builder::CSS.new
#
# text_color = '#7F7F7F'
# preferred_fonts = 'Helvetica, Arial, sans_serif'
#
# css.comment! 'This is our stylesheet'
# css.body {
# background_color '#FAFAFA'
# font_size 'small'
# font_family preferred_fonts
# color text_color
# }
#
# css.id!('navbar') {
# width '500px'
# }
#
# css.class!('navitem') {
# color 'red'
# }
#
# css.a :hover {
# text_decoration 'underline'
# }
#
# css.div(:id => 'menu') {
# background 'green'
# }
#
# css.div(:class => 'foo') {
# background 'red'
# }
#
# This will yield the following stylesheet:
#
# /* This is our stylesheet */
# body {
# background_color: #FAFAFA;
# font_size: small;
# font_family: Helvetica, Arial, sans_serif;
# color: #7F7F7F;
# }
#
# #navbar {
# width: 500px;
# }
#
# .navitem {
# color: red;
# }
#
# a:hover {
# text_decoration: underline;
# }
#
# div#menu {
# background: green;
# }
#
# div.foo {
# background: red;
# }
#
class CSS < BasicObject
# Create a CSS builder.
#
# out:: Object receiving the markup.1 +out+ must respond to
# <tt><<</tt>.
# indent:: Number of spaces used for indentation (0 implies no
# indentation and no line breaks).
#
def initialize(indent=2)
@indent = indent
@target = []
@parts = []
@library = {}
end
def +(part)
_join_with_op! '+'
self
end
def >>(part)
_join_with_op! ''
self
end
def >(part)
_join_with_op! '>'
self
end
def |(part)
_join_with_op! ','
self
end
# Return the target of the builder
def target!
@target * ''
end
# Create a comment string in the output.
def comment!(comment_text)
@target << "/* #{comment_text} */\n"
end
def id!(arg, &block)
_start_container('#'+arg.to_s, nil, block_given?)
_css_block(block) if block
_unify_block
self
end
def class!(arg, &block)
_start_container('.'+arg.to_s, nil, block_given?)
_css_block(block) if block
_unify_block
self
end
def store!(sym, &block)
@library[sym] = block.to_proc
end
def group!(*args, &block)
args.each do |arg|
if arg.is_a?(::Symbol)
instance_eval(&@library[arg])
else
instance_eval(&arg)
end
_text ', ' unless arg == args.last
end
if block
_css_block(block)
_unify_block
end
end
def method_missing(sym, *args, &block)
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
if block
_start_container(sym, args.first)
_css_block(block)
_unify_block
elsif @in_block
_indent
_css_line(sym, *args)
_newline
return self
else
_start_container(sym, args.first, false)
_unify_block
end
self
end
# "Cargo culted" from Jim who also "cargo culted" it. See xmlbase.rb.
def nil?
false
end
private
def _unify_block
@target << @parts * ''
@parts = []
end
def _join_with_op!(op)
rhs, lhs = @target.pop, @target.pop
@target << "#{lhs} #{op} #{rhs}"
end
def _text(text)
@parts << text
end
def _css_block(block)
_newline
_nested_structures(block)
_end_container
_end_block
end
def _end_block
_newline
_newline
end
def _newline
_text "\n"
end
def _indent
_text ' ' * @indent
end
def _nested_structures(block)
@in_block = true
self.instance_eval(&block)
@in_block = false
end
def _start_container(sym, atts = {}, with_bracket = true)
selector = sym.to_s
selector << ".#{atts[:class]}" if atts && atts[:class]
selector << '#' + "#{atts[:id]}" if atts && atts[:id]
@parts << "#{selector}#{with_bracket ? ' {' : ''}"
end
def _end_container
@parts << "}"
end
def _css_line(sym, *args)
_text("#{sym.to_s.gsub('_','-')}: #{args * ' '};")
end
end
end

View File

@@ -1,115 +0,0 @@
#!/usr/bin/env ruby
# The XChar library is provided courtesy of Sam Ruby (See
# http://intertwingly.net/stories/2005/09/28/xchar.rb)
# --------------------------------------------------------------------
# If the Builder::XChar module is not currently defined, fail on any
# name clashes in standard library classes.
module Builder
def self.check_for_name_collision(klass, method_name, defined_constant=nil)
if klass.instance_methods.include?(method_name.to_s)
fail RuntimeError,
"Name Collision: Method '#{method_name}' is already defined in #{klass}"
end
end
end
if ! defined?(Builder::XChar)
Builder.check_for_name_collision(String, "to_xs")
Builder.check_for_name_collision(Fixnum, "xchr")
end
######################################################################
module Builder
####################################################################
# XML Character converter, from Sam Ruby:
# (see http://intertwingly.net/stories/2005/09/28/xchar.rb).
#
module XChar # :nodoc:
# See
# http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
# for details.
CP1252 = { # :nodoc:
128 => 8364, # euro sign
130 => 8218, # single low-9 quotation mark
131 => 402, # latin small letter f with hook
132 => 8222, # double low-9 quotation mark
133 => 8230, # horizontal ellipsis
134 => 8224, # dagger
135 => 8225, # double dagger
136 => 710, # modifier letter circumflex accent
137 => 8240, # per mille sign
138 => 352, # latin capital letter s with caron
139 => 8249, # single left-pointing angle quotation mark
140 => 338, # latin capital ligature oe
142 => 381, # latin capital letter z with caron
145 => 8216, # left single quotation mark
146 => 8217, # right single quotation mark
147 => 8220, # left double quotation mark
148 => 8221, # right double quotation mark
149 => 8226, # bullet
150 => 8211, # en dash
151 => 8212, # em dash
152 => 732, # small tilde
153 => 8482, # trade mark sign
154 => 353, # latin small letter s with caron
155 => 8250, # single right-pointing angle quotation mark
156 => 339, # latin small ligature oe
158 => 382, # latin small letter z with caron
159 => 376, # latin capital letter y with diaeresis
}
# See http://www.w3.org/TR/REC-xml/#dt-chardata for details.
PREDEFINED = {
38 => '&amp;', # ampersand
60 => '&lt;', # left angle bracket
62 => '&gt;', # right angle bracket
}
# See http://www.w3.org/TR/REC-xml/#charsets for details.
VALID = [
0x9, 0xA, 0xD,
(0x20..0xD7FF),
(0xE000..0xFFFD),
(0x10000..0x10FFFF)
]
end
end
######################################################################
# Enhance the Fixnum class with a XML escaped character conversion.
#
class Fixnum
XChar = Builder::XChar if ! defined?(XChar)
# XML escaped version of chr
def xchr
n = XChar::CP1252[self] || self
case n when *XChar::VALID
XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
else
'*'
end
end
end
######################################################################
# Enhance the String class with a XML escaped character version of
# to_s.
#
class String
# XML escaped version of to_s
def to_xs
unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
rescue
unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
end
end

View File

@@ -1,137 +0,0 @@
#!/usr/bin/env ruby
module Builder
# Generic error for builder
class IllegalBlockError < RuntimeError; end
# XmlBase is a base class for building XML builders. See
# Builder::XmlMarkup and Builder::XmlEvents for examples.
class XmlBase < BasicObject
# Create an XML markup builder.
#
# out:: Object receiving the markup. +out+ must respond to
# <tt><<</tt>.
# indent:: Number of spaces used for indentation (0 implies no
# indentation and no line breaks).
# initial:: Level of initial indentation.
#
def initialize(indent=0, initial=0)
@indent = indent
@level = initial
end
# Create a tag named +sym+. Other than the first argument which
# is the tag name, the arguments are the same as the tags
# implemented via <tt>method_missing</tt>.
def tag!(sym, *args, &block)
method_missing(sym.to_sym, *args, &block)
end
# Create XML markup based on the name of the method. This method
# is never invoked directly, but is called for each markup method
# in the markup block.
def method_missing(sym, *args, &block)
text = nil
attrs = nil
sym = "#{sym}:#{args.shift}" if args.first.kind_of?(::Symbol)
args.each do |arg|
case arg
when ::Hash
attrs ||= {}
attrs.merge!(arg)
else
text ||= ''
text << arg.to_s
end
end
if block
unless text.nil?
raise ::ArgumentError, "XmlMarkup cannot mix a text argument with a block"
end
_indent
_start_tag(sym, attrs)
_newline
_nested_structures(block)
_indent
_end_tag(sym)
_newline
elsif text.nil?
_indent
_start_tag(sym, attrs, true)
_newline
else
_indent
_start_tag(sym, attrs)
text! text
_end_tag(sym)
_newline
end
@target
end
# Append text to the output target. Escape any markup. May be
# used within the markup brackets as:
#
# builder.p { |b| b.br; b.text! "HI" } #=> <p><br/>HI</p>
def text!(text)
_text(_escape(text))
end
# Append text to the output target without escaping any markup.
# May be used within the markup brackets as:
#
# builder.p { |x| x << "<br/>HI" } #=> <p><br/>HI</p>
#
# This is useful when using non-builder enabled software that
# generates strings. Just insert the string directly into the
# builder without changing the inserted markup.
#
# It is also useful for stacking builder objects. Builders only
# use <tt><<</tt> to append to the target, so by supporting this
# method/operation builders can use other builders as their
# targets.
def <<(text)
_text(text)
end
# For some reason, nil? is sent to the XmlMarkup object. If nil?
# is not defined and method_missing is invoked, some strange kind
# of recursion happens. Since nil? won't ever be an XML tag, it
# is pretty safe to define it here. (Note: this is an example of
# cargo cult programming,
# cf. http://fishbowl.pastiche.org/2004/10/13/cargo_cult_programming).
def nil?
false
end
private
require 'builder/xchar'
def _escape(text)
text.to_xs
end
def _escape_quote(text)
_escape(text).gsub(%r{"}, '&quot;') # " WART
end
def _newline
return if @indent == 0
text! "\n"
end
def _indent
return if @indent == 0 || @level == 0
text!(" " * (@level * @indent))
end
def _nested_structures(block)
@level += 1
block.call(self)
ensure
@level -= 1
end
end
end

View File

@@ -1,63 +0,0 @@
#!/usr/bin/env ruby
#--
# Copyright 2004 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
require 'builder/xmlmarkup'
module Builder
# Create a series of SAX-like XML events (e.g. start_tag, end_tag)
# from the markup code. XmlEvent objects are used in a way similar
# to XmlMarkup objects, except that a series of events are generated
# and passed to a handler rather than generating character-based
# markup.
#
# Usage:
# xe = Builder::XmlEvents.new(handler)
# xe.title("HI") # Sends start_tag/end_tag/text messages to the handler.
#
# Indentation may also be selected by providing value for the
# indentation size and initial indentation level.
#
# xe = Builder::XmlEvents.new(handler, indent_size, initial_indent_level)
#
# == XML Event Handler
#
# The handler object must expect the following events.
#
# [<tt>start_tag(tag, attrs)</tt>]
# Announces that a new tag has been found. +tag+ is the name of
# the tag and +attrs+ is a hash of attributes for the tag.
#
# [<tt>end_tag(tag)</tt>]
# Announces that an end tag for +tag+ has been found.
#
# [<tt>text(text)</tt>]
# Announces that a string of characters (+text+) has been found.
# A series of characters may be broken up into more than one
# +text+ call, so the client cannot assume that a single
# callback contains all the text data.
#
class XmlEvents < XmlMarkup
def text!(text)
@target.text(text)
end
def _start_tag(sym, attrs, end_too=false)
@target.start_tag(sym, attrs)
_end_tag(sym) if end_too
end
def _end_tag(sym)
@target.end_tag(sym)
end
end
end

View File

@@ -1,328 +0,0 @@
#!/usr/bin/env ruby
#--
# Copyright 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
# All rights reserved.
# Permission is granted for use, copying, modification, distribution,
# and distribution of modified versions of this work as long as the
# above copyright notice is included.
#++
# Provide a flexible and easy to use Builder for creating XML markup.
# See XmlBuilder for usage details.
require 'builder/xmlbase'
module Builder
# Create XML markup easily. All (well, almost all) methods sent to
# an XmlMarkup object will be translated to the equivalent XML
# markup. Any method with a block will be treated as an XML markup
# tag with nested markup in the block.
#
# Examples will demonstrate this easier than words. In the
# following, +xm+ is an +XmlMarkup+ object.
#
# xm.em("emphasized") # => <em>emphasized</em>
# xm.em { xmm.b("emp & bold") } # => <em><b>emph &amp; bold</b></em>
# xm.a("A Link", "href"=>"http://onestepback.org")
# # => <a href="http://onestepback.org">A Link</a>
# xm.div { br } # => <div><br/></div>
# xm.target("name"=>"compile", "option"=>"fast")
# # => <target option="fast" name="compile"\>
# # NOTE: order of attributes is not specified.
#
# xm.instruct! # <?xml version="1.0" encoding="UTF-8"?>
# xm.html { # <html>
# xm.head { # <head>
# xm.title("History") # <title>History</title>
# } # </head>
# xm.body { # <body>
# xm.comment! "HI" # <! -- HI -->
# xm.h1("Header") # <h1>Header</h1>
# xm.p("paragraph") # <p>paragraph</p>
# } # </body>
# } # </html>
#
# == Notes:
#
# * The order that attributes are inserted in markup tags is
# undefined.
#
# * Sometimes you wish to insert text without enclosing tags. Use
# the <tt>text!</tt> method to accomplish this.
#
# Example:
#
# xm.div { # <div>
# xm.text! "line"; xm.br # line<br/>
# xm.text! "another line"; xmbr # another line<br/>
# } # </div>
#
# * The special XML characters <, >, and & are converted to &lt;,
# &gt; and &amp; automatically. Use the <tt><<</tt> operation to
# insert text without modification.
#
# * Sometimes tags use special characters not allowed in ruby
# identifiers. Use the <tt>tag!</tt> method to handle these
# cases.
#
# Example:
#
# xml.tag!("SOAP:Envelope") { ... }
#
# will produce ...
#
# <SOAP:Envelope> ... </SOAP:Envelope>"
#
# <tt>tag!</tt> will also take text and attribute arguments (after
# the tag name) like normal markup methods. (But see the next
# bullet item for a better way to handle XML namespaces).
#
# * Direct support for XML namespaces is now available. If the
# first argument to a tag call is a symbol, it will be joined to
# the tag to produce a namespace:tag combination. It is easier to
# show this than describe it.
#
# xml.SOAP :Envelope do ... end
#
# Just put a space before the colon in a namespace to produce the
# right form for builder (e.g. "<tt>SOAP:Envelope</tt>" =>
# "<tt>xml.SOAP :Envelope</tt>")
#
# * XmlMarkup builds the markup in any object (called a _target_)
# that accepts the <tt><<</tt> method. If no target is given,
# then XmlMarkup defaults to a string target.
#
# Examples:
#
# xm = Builder::XmlMarkup.new
# result = xm.title("yada")
# # result is a string containing the markup.
#
# buffer = ""
# xm = Builder::XmlMarkup.new(buffer)
# # The markup is appended to buffer (using <<)
#
# xm = Builder::XmlMarkup.new(STDOUT)
# # The markup is written to STDOUT (using <<)
#
# xm = Builder::XmlMarkup.new
# x2 = Builder::XmlMarkup.new(:target=>xm)
# # Markup written to +x2+ will be send to +xm+.
#
# * Indentation is enabled by providing the number of spaces to
# indent for each level as a second argument to XmlBuilder.new.
# Initial indentation may be specified using a third parameter.
#
# Example:
#
# xm = Builder.new(:indent=>2)
# # xm will produce nicely formatted and indented XML.
#
# xm = Builder.new(:indent=>2, :margin=>4)
# # xm will produce nicely formatted and indented XML with 2
# # spaces per indent and an over all indentation level of 4.
#
# builder = Builder::XmlMarkup.new(:target=>$stdout, :indent=>2)
# builder.name { |b| b.first("Jim"); b.last("Weirich) }
# # prints:
# # <name>
# # <first>Jim</first>
# # <last>Weirich</last>
# # </name>
#
# * The instance_eval implementation which forces self to refer to
# the message receiver as self is now obsolete. We now use normal
# block calls to execute the markup block. This means that all
# markup methods must now be explicitly send to the xml builder.
# For instance, instead of
#
# xml.div { strong("text") }
#
# you need to write:
#
# xml.div { xml.strong("text") }
#
# Although more verbose, the subtle change in semantics within the
# block was found to be prone to error. To make this change a
# little less cumbersome, the markup block now gets the markup
# object sent as an argument, allowing you to use a shorter alias
# within the block.
#
# For example:
#
# xml_builder = Builder::XmlMarkup.new
# xml_builder.div { |xml|
# xml.stong("text")
# }
#
class XmlMarkup < XmlBase
# Create an XML markup builder. Parameters are specified by an
# option hash.
#
# :target=><em>target_object</em>::
# Object receiving the markup. +out+ must respond to the
# <tt><<</tt> operator. The default is a plain string target.
#
# :indent=><em>indentation</em>::
# Number of spaces used for indentation. The default is no
# indentation and no line breaks.
#
# :margin=><em>initial_indentation_level</em>::
# Amount of initial indentation (specified in levels, not
# spaces).
#
# :escape_attrs=><b>OBSOLETE</em>::
# The :escape_attrs option is no longer supported by builder
# (and will be quietly ignored). String attribute values are
# now automatically escaped. If you need unescaped attribute
# values (perhaps you are using entities in the attribute
# values), then give the value as a Symbol. This allows much
# finer control over escaping attribute values.
#
def initialize(options={})
indent = options[:indent] || 0
margin = options[:margin] || 0
super(indent, margin)
@target = options[:target] || ""
end
# Return the target of the builder.
def target!
@target
end
def comment!(comment_text)
_ensure_no_block ::Kernel.block_given?
_special("<!-- ", " -->", comment_text, nil)
end
# Insert an XML declaration into the XML markup.
#
# For example:
#
# xml.declare! :ELEMENT, :blah, "yada"
# # => <!ELEMENT blah "yada">
def declare!(inst, *args, &block)
_indent
@target << "<!#{inst}"
args.each do |arg|
case arg
when ::String
@target << %{ "#{arg}"} # " WART
when ::Symbol
@target << " #{arg}"
end
end
if ::Kernel.block_given?
@target << " ["
_newline
_nested_structures(block)
@target << "]"
end
@target << ">"
_newline
end
# Insert a processing instruction into the XML markup. E.g.
#
# For example:
#
# xml.instruct!
# #=> <?xml version="1.0" encoding="UTF-8"?>
# xml.instruct! :aaa, :bbb=>"ccc"
# #=> <?aaa bbb="ccc"?>
#
def instruct!(directive_tag=:xml, attrs={})
_ensure_no_block ::Kernel.block_given?
if directive_tag == :xml
a = { :version=>"1.0", :encoding=>"UTF-8" }
attrs = a.merge attrs
end
_special(
"<?#{directive_tag}",
"?>",
nil,
attrs,
[:version, :encoding, :standalone])
end
# Insert a CDATA section into the XML markup.
#
# For example:
#
# xml.cdata!("text to be included in cdata")
# #=> <![CDATA[text to be included in cdata]]>
#
def cdata!(text)
_ensure_no_block ::Kernel.block_given?
_special("<![CDATA[", "]]>", text, nil)
end
private
# NOTE: All private methods of a builder object are prefixed when
# a "_" character to avoid possible conflict with XML tag names.
# Insert text directly in to the builder's target.
def _text(text)
@target << text
end
# Insert special instruction.
def _special(open, close, data=nil, attrs=nil, order=[])
_indent
@target << open
@target << data if data
_insert_attributes(attrs, order) if attrs
@target << close
_newline
end
# Start an XML tag. If <tt>end_too</tt> is true, then the start
# tag is also the end tag (e.g. <br/>
def _start_tag(sym, attrs, end_too=false)
@target << "<#{sym}"
_insert_attributes(attrs)
@target << "/" if end_too
@target << ">"
end
# Insert an ending tag.
def _end_tag(sym)
@target << "</#{sym}>"
end
# Insert the attributes (given in the hash).
def _insert_attributes(attrs, order=[])
return if attrs.nil?
order.each do |k|
v = attrs[k]
@target << %{ #{k}="#{_attr_value(v)}"} if v # " WART
end
attrs.each do |k, v|
@target << %{ #{k}="#{_attr_value(v)}"} unless order.member?(k) # " WART
end
end
def _attr_value(value)
case value
when ::Symbol
value.to_s
else
_escape_quote(value.to_s)
end
end
def _ensure_no_block(got_block)
if got_block
fail IllegalBlockError,
"Blocks are not allowed on XML instructions"
end
end
end
end

View File

@@ -1,322 +0,0 @@
# encoding: utf-8
# Authors:: Sven Fuchs (http://www.artweb-design.de),
# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
# Stephan Soller (http://www.arkanis-development.de/),
# Saimon Moore (http://saimonmoore.net),
# Matt Aimonetti (http://railsontherun.com/)
# Copyright:: Copyright (c) 2008 The Ruby i18n Team
# License:: MIT
require 'i18n/exceptions'
require 'i18n/core_ext/string/interpolate'
module I18n
autoload :Backend, 'i18n/backend'
autoload :Config, 'i18n/config'
autoload :Gettext, 'i18n/gettext'
autoload :Locale, 'i18n/locale'
class << self
# Gets I18n configuration object.
def config
Thread.current[:i18n_config] ||= I18n::Config.new
end
# Sets I18n configuration object.
def config=(value)
Thread.current[:i18n_config] = value
end
# Write methods which delegates to the configuration object
%w(locale backend default_locale available_locales default_separator
exception_handler load_path).each do |method|
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
def #{method}
config.#{method}
end
def #{method}=(value)
config.#{method} = (value)
end
DELEGATORS
end
# Tells the backend to reload translations. Used in situations like the
# Rails development environment. Backends can implement whatever strategy
# is useful.
def reload!
config.backend.reload!
end
# Translates, pluralizes and interpolates a given key using a given locale,
# scope, and default, as well as interpolation values.
#
# *LOOKUP*
#
# Translation data is organized as a nested hash using the upper-level keys
# as namespaces. <em>E.g.</em>, ActionView ships with the translation:
# <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
#
# Translations can be looked up at any level of this hash using the key argument
# and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
# returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
#
# Key can be either a single key or a dot-separated key (both Strings and Symbols
# work). <em>E.g.</em>, the short format can be looked up using both:
# I18n.t 'date.formats.short'
# I18n.t :'date.formats.short'
#
# Scope can be either a single key, a dot-separated key or an array of keys
# or dot-separated keys. Keys and scopes can be combined freely. So these
# examples will all look up the same short date format:
# I18n.t 'date.formats.short'
# I18n.t 'formats.short', :scope => 'date'
# I18n.t 'short', :scope => 'date.formats'
# I18n.t 'short', :scope => %w(date formats)
#
# *INTERPOLATION*
#
# Translations can contain interpolation variables which will be replaced by
# values passed to #translate as part of the options hash, with the keys matching
# the interpolation variable names.
#
# <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option
# value for the key +bar+ will be interpolated into the translation:
# I18n.t :foo, :bar => 'baz' # => 'foo baz'
#
# *PLURALIZATION*
#
# Translation data can contain pluralized translations. Pluralized translations
# are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
#
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
# pluralization rules. Other algorithms can be supported by custom backends.
#
# This returns the singular version of a pluralized translation:
# I18n.t :foo, :count => 1 # => 'Foo'
#
# These both return the plural version of a pluralized translation:
# I18n.t :foo, :count => 0 # => 'Foos'
# I18n.t :foo, :count => 2 # => 'Foos'
#
# The <tt>:count</tt> option can be used both for pluralization and interpolation.
# <em>E.g.</em>, with the translation
# <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will
# be interpolated to the pluralized translation:
# I18n.t :foo, :count => 1 # => '1 foo'
#
# *DEFAULTS*
#
# This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
# I18n.t :foo, :default => 'default'
#
# This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
# translation for <tt>:foo</tt> was found:
# I18n.t :foo, :default => :bar
#
# Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
# or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
# I18n.t :foo, :default => [:bar, 'default']
#
# *BULK LOOKUP*
#
# This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
# I18n.t [:foo, :bar]
#
# Can be used with dot-separated nested keys:
# I18n.t [:'baz.foo', :'baz.bar']
#
# Which is the same as using a scope option:
# I18n.t [:foo, :bar], :scope => :baz
#
# *LAMBDAS*
#
# Both translations and defaults can be given as Ruby lambdas. Lambdas will be
# called and passed the key and options.
#
# E.g. assuming the key <tt>:salutation</tt> resolves to:
# lambda { |key, options| options[:gender] == 'm' ? "Mr. %{options[:name]}" : "Mrs. %{options[:name]}" }
#
# Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
#
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
# a cache layer is put in front of I18n.translate it will generate a cache key
# from the argument values passed to #translate. Therefor your lambdas should
# always return the same translations/values per unique combination of argument
# values.
def translate(*args)
options = args.pop if args.last.is_a?(Hash)
key = args.shift
locale = options && options.delete(:locale) || config.locale
raises = options && options.delete(:raise)
config.backend.translate(locale, key, options || {})
rescue I18n::ArgumentError => exception
raise exception if raises
handle_exception(exception, locale, key, options)
end
alias :t :translate
def translate!(key, options = {})
translate(key, options.merge( :raise => true ))
end
alias :t! :translate!
# Transliterates UTF-8 characters to ASCII. By default this method will
# transliterate only Latin strings to an ASCII approximation:
#
# I18n.transliterate("Ærøskøbing")
# # => "AEroskobing"
#
# I18n.transliterate("日本語")
# # => "???"
#
# It's also possible to add support for per-locale transliterations. I18n
# expects transliteration rules to be stored at
# <tt>i18n.transliterate.rule</tt>.
#
# Transliteration rules can either be a Hash or a Proc. Procs must accept a
# single string argument. Hash rules inherit the default transliteration
# rules, while Procs do not.
#
# *Examples*
#
# Setting a Hash in <locale>.yml:
#
# i18n:
# transliterate:
# rule:
# ü: "ue"
# ö: "oe"
#
# Setting a Hash using Ruby:
#
# store_translations(:de, :i18n => {
# :transliterate => {
# :rule => {
# "ü" => "ue",
# "ö" => "oe"
# }
# }
# )
#
# Setting a Proc:
#
# translit = lambda {|string| MyTransliterator.transliterate(string) }
# store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
#
# Transliterating strings:
#
# I18n.locale = :en
# I18n.transliterate("Jürgen") # => "Jurgen"
# I18n.locale = :de
# I18n.transliterate("Jürgen") # => "Juergen"
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
def transliterate(*args)
options = args.pop if args.last.is_a?(Hash)
key = args.shift
locale = options && options.delete(:locale) || config.locale
raises = options && options.delete(:raise)
replacement = options && options.delete(:replacement)
config.backend.transliterate(locale, key, replacement)
rescue I18n::ArgumentError => exception
raise exception if raises
handle_exception(exception, locale, key, options)
end
# Localizes certain objects, such as dates and numbers to local formatting.
def localize(object, options = {})
locale = options.delete(:locale) || config.locale
format = options.delete(:format) || :default
config.backend.localize(locale, object, format, options)
end
alias :l :localize
# Executes block with given I18n.locale set.
def with_locale(tmp_locale = nil)
if tmp_locale
current_locale = self.locale
self.locale = tmp_locale
end
yield
ensure
self.locale = current_locale if tmp_locale
end
# Merges the given locale, key and scope into a single array of keys.
# Splits keys that contain dots into multiple keys. Makes sure all
# keys are Symbols.
def normalize_keys(locale, key, scope, separator = nil)
separator ||= I18n.default_separator
keys = []
keys.concat normalize_key(locale, separator)
keys.concat normalize_key(scope, separator)
keys.concat normalize_key(key, separator)
keys
end
# making these private until Ruby 1.9.2 can send to protected methods again
# see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
private
# Handles exceptions raised in the backend. All exceptions except for
# MissingTranslationData exceptions are re-raised. When a MissingTranslationData
# was caught and the option :raise is not set the handler returns an error
# message string containing the key/scope.
def default_exception_handler(exception, locale, key, options)
return exception.message if MissingTranslationData === exception
raise exception
end
# Any exceptions thrown in translate will be sent to the @@exception_handler
# which can be a Symbol, a Proc or any other Object.
#
# If exception_handler is a Symbol then it will simply be sent to I18n as
# a method call. A Proc will simply be called. In any other case the
# method #call will be called on the exception_handler object.
#
# Examples:
#
# I18n.exception_handler = :default_exception_handler # this is the default
# I18n.default_exception_handler(exception, locale, key, options) # will be called like this
#
# I18n.exception_handler = lambda { |*args| ... } # a lambda
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
#
# I18n.exception_handler = I18nExceptionHandler.new # an object
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
def handle_exception(exception, locale, key, options)
case config.exception_handler
when Symbol
send(config.exception_handler, exception, locale, key, options)
else
config.exception_handler.call(exception, locale, key, options)
end
end
# Deprecated. Will raise a warning in future versions and then finally be
# removed. Use I18n.normalize_keys instead.
def normalize_translation_keys(locale, key, scope, separator = nil)
normalize_keys(locale, key, scope, separator)
end
def normalize_key(key, separator)
normalized_key_cache[separator][key] ||=
case key
when Array
key.map { |k| normalize_key(k, separator) }.flatten
else
keys = key.to_s.split(separator)
keys.delete('')
keys.map!{ |k| k.to_sym }
keys
end
end
def normalized_key_cache
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
end
end
end

View File

@@ -1,20 +0,0 @@
module I18n
module Backend
autoload :ActiveRecord, 'i18n/backend/active_record'
autoload :Base, 'i18n/backend/base'
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
autoload :Cache, 'i18n/backend/cache'
autoload :Cascade, 'i18n/backend/cascade'
autoload :Chain, 'i18n/backend/chain'
autoload :Cldr, 'i18n/backend/cldr'
autoload :Fallbacks, 'i18n/backend/fallbacks'
autoload :Flatten, 'i18n/backend/flatten'
autoload :Gettext, 'i18n/backend/gettext'
autoload :KeyValue, 'i18n/backend/key_value'
autoload :Memoize, 'i18n/backend/memoize'
autoload :Metadata, 'i18n/backend/metadata'
autoload :Pluralization, 'i18n/backend/pluralization'
autoload :Simple, 'i18n/backend/simple'
autoload :Transliterator, 'i18n/backend/transliterator'
end
end

View File

@@ -1,61 +0,0 @@
require 'i18n/backend/base'
require 'i18n/backend/active_record/translation'
module I18n
module Backend
class ActiveRecord
autoload :Missing, 'i18n/backend/active_record/missing'
autoload :StoreProcs, 'i18n/backend/active_record/store_procs'
autoload :Translation, 'i18n/backend/active_record/translation'
module Implementation
include Base, Flatten
def available_locales
begin
Translation.available_locales
rescue ::ActiveRecord::StatementInvalid
[]
end
end
def store_translations(locale, data, options = {})
escape = options.fetch(:escape, true)
flatten_translations(locale, data, escape, false).each do |key, value|
Translation.locale(locale).lookup(expand_keys(key)).delete_all
Translation.create(:locale => locale.to_s, :key => key.to_s, :value => value)
end
end
protected
def lookup(locale, key, scope = [], options = {})
key = normalize_flat_keys(locale, key, scope, options[:separator])
result = Translation.locale(locale).lookup(key).all
if result.empty?
nil
elsif result.first.key == key
result.first.value
else
chop_range = (key.size + FLATTEN_SEPARATOR.size)..-1
result = result.inject({}) do |hash, r|
hash[r.key.slice(chop_range)] = r.value
hash
end
result.deep_symbolize_keys
end
end
# For a key :'foo.bar.baz' return ['foo', 'foo.bar', 'foo.bar.baz']
def expand_keys(key)
key.to_s.split(FLATTEN_SEPARATOR).inject([]) do |keys, key|
keys << [keys.last, key].compact.join(FLATTEN_SEPARATOR)
end
end
end
include Implementation
end
end
end

View File

@@ -1,65 +0,0 @@
# This extension stores translation stub records for missing translations to
# the database.
#
# This is useful if you have a web based translation tool. It will populate
# the database with untranslated keys as the application is being used. A
# translator can then go through these and add missing translations.
#
# Example usage:
#
# I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing)
# I18n.backend = I18nChainBackend.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
#
# Stub records for pluralizations will also be created for each key defined
# in i18n.plural.keys.
#
# For example:
#
# # en.yml
# en:
# i18n:
# plural:
# keys: [:zero, :one, :other]
#
# # pl.yml
# pl:
# i18n:
# plural:
# keys: [:zero, :one, :few, :other]
#
# It will also persist interpolation keys in Translation#interpolations so
# translators will be able to review and use them.
module I18n
module Backend
class ActiveRecord
module Missing
def store_default_translations(locale, key, options = {})
count, scope, default, separator = options.values_at(:count, *Base::RESERVED_KEYS)
separator ||= I18n.default_separator
keys = I18n.normalize_keys(locale, key, scope, separator)[1..-1]
key = keys.join(separator || I18n.default_separator)
unless ActiveRecord::Translation.locale(locale).lookup(key).exists?
interpolations = options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }.keys
keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(separator) } : [key]
keys.each { |key| store_default_translation(locale, key, interpolations) }
end
end
def store_default_translation(locale, key, interpolations)
translation = ActiveRecord::Translation.new :locale => locale.to_s, :key => key
translation.interpolations = interpolations
translation.save
end
def translate(locale, key, options = {})
super
rescue I18n::MissingTranslationData => e
self.store_default_translations(locale, key, options)
raise e
end
end
end
end
end

View File

@@ -1,38 +0,0 @@
# This module is intended to be mixed into the ActiveRecord backend to allow
# storing Ruby Procs as translation values in the database.
#
# I18n.backend = I18n::Backend::ActiveRecord.new
# I18n::Backend::ActiveRecord::Translation.send(:include, I18n::Backend::ActiveRecord::StoreProcs)
#
# The StoreProcs module requires the ParseTree and ruby2ruby gems and therefor
# was extracted from the original backend.
#
# ParseTree is not compatible with Ruby 1.9.
begin
require 'ruby2ruby'
require 'parse_tree'
require 'parse_tree_extensions'
rescue LoadError => e
puts "can't use StoreProcs because: #{e.message}"
end
module I18n
module Backend
class ActiveRecord
module StoreProcs
def value=(v)
case v
when Proc
write_attribute(:value, v.to_ruby)
write_attribute(:is_proc, true)
else
write_attribute(:value, v)
end
end
Translation.send(:include, self) if method(:to_s).respond_to?(:to_ruby)
end
end
end
end

View File

@@ -1,93 +0,0 @@
require 'active_record'
module I18n
module Backend
# ActiveRecord model used to store actual translations to the database.
#
# This model expects a table like the following to be already set up in
# your the database:
#
# create_table :translations do |t|
# t.string :locale
# t.string :key
# t.text :value
# t.text :interpolations
# t.boolean :is_proc, :default => false
# end
#
# This model supports to named scopes :locale and :lookup. The :locale
# scope simply adds a condition for a given locale:
#
# I18n::Backend::ActiveRecord::Translation.locale(:en).all
# # => all translation records that belong to the :en locale
#
# The :lookup scope adds a condition for looking up all translations
# that either start with the given keys (joined by an optionally given
# separator or I18n.default_separator) or that exactly have this key.
#
# # with translations present for :"foo.bar" and :"foo.baz"
# I18n::Backend::ActiveRecord::Translation.lookup(:foo)
# # => an array with both translation records :"foo.bar" and :"foo.baz"
#
# I18n::Backend::ActiveRecord::Translation.lookup([:foo, :bar])
# I18n::Backend::ActiveRecord::Translation.lookup(:"foo.bar")
# # => an array with the translation record :"foo.bar"
#
# When the StoreProcs module was mixed into this model then Procs will
# be stored to the database as Ruby code and evaluated when :value is
# called.
#
# Translation = I18n::Backend::ActiveRecord::Translation
# Translation.create \
# :locale => 'en'
# :key => 'foo'
# :value => lambda { |key, options| 'FOO' }
# Translation.find_by_locale_and_key('en', 'foo').value
# # => 'FOO'
class ActiveRecord
class Translation < ::ActiveRecord::Base
set_table_name 'translations'
attr_protected :is_proc, :interpolations
serialize :value
serialize :interpolations, Array
scope_method = ::ActiveRecord::VERSION::MAJOR == 2 ? :named_scope : :scope
send scope_method, :locale, lambda { |locale|
{ :conditions => { :locale => locale.to_s } }
}
send scope_method, :lookup, lambda { |keys, *separator|
column_name = connection.quote_column_name('key')
keys = Array(keys).map! { |key| key.to_s }
unless separator.empty?
warn "[DEPRECATION] Giving a separator to Translation.lookup is deprecated. " <<
"You can change the internal separator by overwriting FLATTEN_SEPARATOR."
end
namespace = "#{keys.last}#{I18n::Backend::Flatten::FLATTEN_SEPARATOR}%"
{ :conditions => ["#{column_name} IN (?) OR #{column_name} LIKE ?", keys, namespace] }
}
def self.available_locales
Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale.to_sym }
end
def interpolates?(key)
self.interpolations.include?(key) if self.interpolations
end
def value
if is_proc
Kernel.eval(read_attribute(:value))
else
value = read_attribute(:value)
value == 'f' ? false : value
end
end
end
end
end
end

View File

@@ -1,237 +0,0 @@
# encoding: utf-8
require 'yaml'
require 'i18n/core_ext/hash'
module I18n
module Backend
module Base
include I18n::Backend::Transliterator
RESERVED_KEYS = [:scope, :default, :separator, :resolve]
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
DEPRECATED_INTERPOLATION_SYNTAX_PATTERN = /(\\)?\{\{([^\}]+)\}\}/
INTERPOLATION_SYNTAX_PATTERN = /%\{([^\}]+)\}/
# Accepts a list of paths to translation files. Loads translations from
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
# for details.
def load_translations(*filenames)
filenames = I18n.load_path.flatten if filenames.empty?
filenames.each { |filename| load_file(filename) }
end
# This method receives a locale, a data hash and options for storing translations.
# Should be implemented
def store_translations(locale, data, options = {})
raise NotImplementedError
end
def translate(locale, key, options = {})
raise InvalidLocale.new(locale) unless locale
return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
entry = key && lookup(locale, key, options[:scope], options)
if options.empty?
entry = resolve(locale, key, entry, options)
else
count, default = options.values_at(:count, :default)
values = options.except(*RESERVED_KEYS)
entry = entry.nil? && default ?
default(locale, key, default, options) : resolve(locale, key, entry, options)
end
raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil?
entry = entry.dup if entry.is_a?(String)
entry = pluralize(locale, entry, count) if count
entry = interpolate(locale, entry, values) if values
entry
end
# Acts the same as +strftime+, but uses a localized version of the
# format string. Takes a key from the date/time formats translations as
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
def localize(locale, object, format = :default, options = {})
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
if Symbol === format
key = format
type = object.respond_to?(:sec) ? 'time' : 'date'
format = I18n.t(:"#{type}.formats.#{key}", options.merge(:raise => true, :object => object, :locale => locale))
end
# format = resolve(locale, object, format, options)
format = format.to_s.gsub(/%[aAbBp]/) do |match|
case match
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format) if object.respond_to? :hour
end
end
object.strftime(format)
end
# Returns an array of locales for which translations are available
# ignoring the reserved translation meta data key :i18n.
def available_locales
raise NotImplementedError
end
def reload!
@skip_syntax_deprecation = false
end
protected
# The method which actually looks up for the translation in the store.
def lookup(locale, key, scope = [], options = {})
raise NotImplementedError
end
# Evaluates defaults.
# If given subject is an Array, it walks the array and returns the
# first translation that can be resolved. Otherwise it tries to resolve
# the translation directly.
def default(locale, object, subject, options = {})
options = options.dup.reject { |key, value| key == :default }
case subject
when Array
subject.each do |item|
result = resolve(locale, object, item, options) and return result
end and nil
else
resolve(locale, object, subject, options)
end
end
# Resolves a translation.
# If the given subject is a Symbol, it will be translated with the
# given options. If it is a Proc then it will be evaluated. All other
# subjects will be returned directly.
def resolve(locale, object, subject, options = nil)
return subject if options[:resolve] == false
case subject
when Symbol
I18n.translate(subject, (options || {}).merge(:locale => locale, :raise => true))
when Proc
date_or_time = options.delete(:object) || object
resolve(locale, object, subject.call(date_or_time, options), options = {})
else
subject
end
rescue MissingTranslationData
nil
end
# Picks a translation from an array according to English pluralization
# rules. It will pick the first translation if count is not equal to 1
# and the second translation if it is equal to 1. Other backends can
# implement more flexible or complex pluralization rules.
def pluralize(locale, entry, count)
return entry unless entry.is_a?(Hash) && count
key = :zero if count == 0 && entry.has_key?(:zero)
key ||= count == 1 ? :one : :other
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
entry[key]
end
# Interpolates values into a given string.
#
# interpolate "file %{file} opened by %%{user}", :file => 'test.txt', :user => 'Mr. X'
# # => "file test.txt opened by %{user}"
#
# Note that you have to double escape the <tt>\\</tt> when you want to escape
# the <tt>{{...}}</tt> key in a string (once for the string and once for the
# interpolation).
def interpolate(locale, string, values = {})
return string unless string.is_a?(::String) && !values.empty?
original_values = values.dup
preserve_encoding(string) do
string = string.gsub(DEPRECATED_INTERPOLATION_SYNTAX_PATTERN) do
escaped, key = $1, $2.to_sym
if escaped
"{{#{key}}}"
else
warn_syntax_deprecation!
"%{#{key}}"
end
end
keys = string.scan(INTERPOLATION_SYNTAX_PATTERN).flatten
return string if keys.empty?
values.each do |key, value|
if keys.include?(key.to_s)
value = value.call(values) if interpolate_lambda?(value, string, key)
value = value.to_s unless value.is_a?(::String)
values[key] = value
else
values.delete(key)
end
end
string % values
end
rescue KeyError => e
if string =~ RESERVED_KEYS_PATTERN
raise ReservedInterpolationKey.new($1.to_sym, string)
else
raise MissingInterpolationArgument.new(original_values, string)
end
end
def preserve_encoding(string)
if string.respond_to?(:encoding)
encoding = string.encoding
result = yield
result.force_encoding(encoding) if result.respond_to?(:force_encoding)
result
else
yield
end
end
# returns true when the given value responds to :call and the key is
# an interpolation placeholder in the given string
def interpolate_lambda?(object, string, key)
object.respond_to?(:call) && string =~ /%\{#{key}\}|%\<#{key}>.*?\d*\.?\d*[bBdiouxXeEfgGcps]\}/
end
# Loads a single translations file by delegating to #load_rb or
# #load_yml depending on the file extension and directly merges the
# data to the existing translations. Raises I18n::UnknownFileType
# for all other file extensions.
def load_file(filename)
type = File.extname(filename).tr('.', '').downcase
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
data = send(:"load_#{type}", filename) # TODO raise a meaningful exception if this does not yield a Hash
data.each { |locale, d| store_translations(locale, d) }
end
# Loads a plain Ruby translations file. eval'ing the file must yield
# a Hash containing translation data with locales as toplevel keys.
def load_rb(filename)
eval(IO.read(filename), binding, filename)
end
# Loads a YAML translations file. The data must have locales as
# toplevel keys.
def load_yml(filename)
YAML::load(IO.read(filename))
end
def warn_syntax_deprecation! #:nodoc:
return if @skip_syntax_deprecation
warn "The {{key}} interpolation syntax in I18n messages is deprecated. Please use %{key} instead.\n#{caller.join("\n")}"
@skip_syntax_deprecation = true
end
end
end
end

View File

@@ -1,77 +0,0 @@
# encoding: utf-8
# This module allows you to easily cache all responses from the backend - thus
# speeding up the I18n aspects of your application quite a bit.
#
# To enable caching you can simply include the Cache module to the Simple
# backend - or whatever other backend you are using:
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Cache)
#
# You will also need to set a cache store implementation that you want to use:
#
# I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
#
# You can use any cache implementation you want that provides the same API as
# ActiveSupport::Cache (only the methods #fetch and #write are being used).
#
# The cache_key implementation assumes that you only pass values to
# I18n.translate that return a valid key from #hash (see
# http://www.ruby-doc.org/core/classes/Object.html#M000337).
module I18n
class << self
@@cache_store = nil
@@cache_namespace = nil
def cache_store
@@cache_store
end
def cache_store=(store)
@@cache_store = store
end
def cache_namespace
@@cache_namespace
end
def cache_namespace=(namespace)
@@cache_namespace = namespace
end
def perform_caching?
!cache_store.nil?
end
end
module Backend
# TODO Should the cache be cleared if new translations are stored?
module Cache
def translate(*args)
I18n.perform_caching? ? fetch(*args) { super } : super
end
protected
def fetch(*args, &block)
result = I18n.cache_store.fetch(cache_key(*args), &block)
raise result if result.is_a?(Exception)
result = result.dup if result.frozen? rescue result
result
rescue MissingTranslationData => exception
I18n.cache_store.write(cache_key(*args), exception)
raise exception
end
def cache_key(*args)
# This assumes that only simple, native Ruby values are passed to I18n.translate.
# Also, in Ruby < 1.8.7 {}.hash != {}.hash
# (see http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7)
# If args.inspect does not work for you for some reason, patches are very welcome :)
hash = RUBY_VERSION >= "1.8.7" ? args.hash : args.inspect
keys = ['i18n', I18n.cache_namespace, hash]
keys.compact.join('-')
end
end
end
end

View File

@@ -1,57 +0,0 @@
# encoding: utf-8
# EXPERIMENTAL
#
# The Cascade module adds the ability to do cascading lookups to backends that
# are compatible to the Simple backend.
#
# By cascading lookups we mean that for any key that can not be found the
# Cascade module strips one segment off the scope part of the key and then
# tries to look up the key in that scope.
#
# E.g. when a lookup for the key :"foo.bar.baz" does not yield a result then
# the segment :bar will be stripped off the scope part :"foo.bar" and the new
# scope :foo will be used to look up the key :baz. If that does not succeed
# then the remaining scope segment :foo will be omitted, too, and again the
# key :baz will be looked up (now with no scope).
#
# To enable a cascading lookup one passes the :cascade option:
#
# I18n.t(:'foo.bar.baz', :cascade => true)
#
# This will return the first translation found for :"foo.bar.baz", :"foo.baz"
# or :baz in this order.
#
# The cascading lookup takes precedence over resolving any given defaults.
# I.e. defaults will kick in after the cascading lookups haven't succeeded.
#
# This behavior is useful for libraries like ActiveRecord validations where
# the library wants to give users a bunch of more or less fine-grained options
# of scopes for a particular key.
#
# Thanks to Clemens Kofler for the initial idea and implementation! See
# http://github.com/clemens/i18n-cascading-backend
module I18n
module Backend
module Cascade
def lookup(locale, key, scope = [], options = {})
return super unless cascade = options[:cascade]
separator = options[:separator] || I18n.default_separator
skip_root = cascade.has_key?(:skip_root) ? cascade[:skip_root] : true
step = cascade[:step]
keys = I18n.normalize_keys(nil, key, nil, separator)
offset = options[:cascade][:offset] || keys.length
scope = I18n.normalize_keys(nil, nil, scope, separator) + keys
key = scope.slice!(-offset, offset).join(separator)
begin
result = super
return result unless result.nil?
end while !scope.empty? && scope.slice!(-step, step) && (!scope.empty? || !skip_root)
end
end
end
end

View File

@@ -1,77 +0,0 @@
# encoding: utf-8
module I18n
module Backend
# Backend that chains multiple other backends and checks each of them when
# a translation needs to be looked up. This is useful when you want to use
# standard translations with a Simple backend but store custom application
# translations in a database or other backends.
#
# To use the Chain backend instantiate it and set it to the I18n module.
# You can add chained backends through the initializer or backends
# accessor:
#
# # preserves the existing Simple backend set to I18n.backend
# I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n.backend)
#
# The implementation assumes that all backends added to the Chain implement
# a lookup method with the same API as Simple backend does.
class Chain
include Base
attr_accessor :backends
def initialize(*backends)
self.backends = backends
end
def reload!
backends.each { |backend| backend.reload! }
end
def store_translations(locale, data, options = {})
backends.first.store_translations(locale, data, options = {})
end
def available_locales
backends.map { |backend| backend.available_locales }.flatten.uniq
end
def translate(locale, key, options = {})
return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
default = options.delete(:default)
namespace = {}
backends.each do |backend|
begin
options.update(:default => default) if default and backend == backends.last
translation = backend.translate(locale, key, options)
if namespace_lookup?(translation, options)
namespace.update(translation)
elsif !translation.nil?
return translation
end
rescue MissingTranslationData
end
end
return namespace unless namespace.empty?
raise(I18n::MissingTranslationData.new(locale, key, options))
end
def localize(locale, object, format = :default, options = {})
backends.each do |backend|
begin
result = backend.localize(locale, object, format, options) and return result
rescue MissingTranslationData
end
end
raise(I18n::MissingTranslationData.new(locale, format, options))
end
protected
def namespace_lookup?(result, options)
result.is_a?(Hash) and not options.has_key?(:count)
end
end
end
end

View File

@@ -1,100 +0,0 @@
# encoding: utf-8
require 'cldr'
module I18n
module Backend
module Cldr
include ::Cldr::Format
def localize(locale, object, format = :default, options = {})
options[:as] ||= detect_type(object, options)
send(:"format_#{options[:as]}", locale, object, format, options)
end
def format_decimal(locale, object, format = :default, options = {})
formatter(locale, :decimal, format).apply(object, options)
end
def format_integer(locale, object, format = :default, options = {})
format_object(number, options.merge(:precision => 0))
end
def format_currency(locale, object, format = :default, options = {})
options.merge!(:currency => lookup_currency(locale, options[:currency], object)) if options[:currency].is_a?(Symbol)
formatter(locale, :currency, format).apply(object, options)
end
def format_percent(locale, object, format = :default, options = {})
formatter(locale, :percent, format).apply(object, options)
end
def format_date(locale, object, format = :default, options = {})
formatter(locale, :date, format).apply(object, options)
end
def format_time(locale, object, format = :default, options = {})
formatter(locale, :time, format).apply(object, options)
end
def format_datetime(locale, object, format = :default, options = {})
key = :"calendars.gregorian.formats.datetime.#{format}.pattern"
date = I18n.l(object, :format => options[:date_format] || format, :locale => locale, :as => :date)
time = I18n.l(object, :format => options[:time_format] || format, :locale => locale, :as => :time)
I18n.t(key, :date => date, :time => time, :locale => locale, :raise => true)
end
protected
def detect_type(object, options)
options.has_key?(:currency) ? :currency : case object
when ::Numeric
:decimal
when ::Date, ::DateTime, ::Time
object.class.name.downcase.to_sym
else
raise_unspecified_format_type!
end
end
def formatter(locale, type, format)
(@formatters ||= {})[:"#{locale}.#{type}.#{format}"] ||= begin
format = lookup_format(locale, type, format)
data = lookup_format_data(locale, type)
::Cldr::Format.const_get(type.to_s.camelize).new(format, data)
end
end
def lookup_format(locale, type, format)
key = case type
when :date, :time, :datetime
:"calendars.gregorian.formats.#{type}.#{format}.pattern"
else
:"numbers.formats.#{type}.patterns.#{format || :default}"
end
I18n.t(key, :locale => locale, :raise => true)
end
def lookup_format_data(locale, type)
key = case type
when :date, :time, :datetime
:'calendars.gregorian'
else
:'numbers.symbols'
end
I18n.t(key, :locale => locale, :raise => true)
end
def lookup_currency(locale, currency, count)
I18n.t(:"currencies.#{currency}", :locale => locale, :count => count)
end
def raise_unspecified_format_type!
raise ArgumentError.new("You have to specify a format type, e.g. :as => :number.")
end
def raise_unspecified_currency!
raise ArgumentError.new("You have to specify a currency, e.g. :currency => 'EUR'.")
end
end
end
end

View File

@@ -1,69 +0,0 @@
# encoding: utf-8
# I18n locale fallbacks are useful when you want your application to use
# translations from other locales when translations for the current locale are
# missing. E.g. you might want to use :en translations when translations in
# your applications main locale :de are missing.
#
# To enable locale fallbacks you can simply include the Fallbacks module to
# the Simple backend - or whatever other backend you are using:
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
module I18n
@@fallbacks = nil
class << self
# Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
def fallbacks
@@fallbacks ||= I18n::Locale::Fallbacks.new
end
# Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
def fallbacks=(fallbacks)
@@fallbacks = fallbacks
end
end
module Backend
module Fallbacks
# Overwrites the Base backend translate method so that it will try each
# locale given by I18n.fallbacks for the given locale. E.g. for the
# locale :"de-DE" it might try the locales :"de-DE", :de and :en
# (depends on the fallbacks implementation) until it finds a result with
# the given options. If it does not find any result for any of the
# locales it will then raise a MissingTranslationData exception as
# usual.
#
# The default option takes precedence over fallback locales
# only when it's not a String. When default contains String it
# is evaluated after fallback locales.
def translate(locale, key, options = {})
default = extract_string_default!(options) if options[:default]
I18n.fallbacks[locale].each do |fallback|
begin
result = super(fallback, key, options)
return result unless result.nil?
rescue I18n::MissingTranslationData
end
end
return super(locale, nil, options.merge(:default => default)) if default
raise(I18n::MissingTranslationData.new(locale, key, options))
end
def extract_string_default!(options)
defaults = Array(options[:default])
if index = find_first_string_default(defaults)
options[:default] = defaults[0, index]
defaults[index]
end
end
def find_first_string_default(defaults)
defaults.each_index { |ix| return ix if String === defaults[ix] }
nil
end
end
end
end

View File

@@ -1,113 +0,0 @@
module I18n
module Backend
# This module contains several helpers to assist flattening translations.
# You may want to flatten translations for:
#
# 1) speed up lookups, as in the Memoize backend;
# 2) In case you want to store translations in a data store, as in ActiveRecord backend;
#
# You can check both backends above for some examples.
# This module also keeps all links in a hash so they can be properly resolved when flattened.
module Flatten
SEPARATOR_ESCAPE_CHAR = "\001"
FLATTEN_SEPARATOR = "."
# normalize_keys the flatten way. This method is significantly faster
# and creates way less objects than the one at I18n.normalize_keys.
# It also handles escaping the translation keys.
def self.normalize_flat_keys(locale, key, scope, separator)
keys = [scope, key].flatten.compact
separator ||= I18n.default_separator
if separator != FLATTEN_SEPARATOR
keys.map! do |k|
k.to_s.tr("#{FLATTEN_SEPARATOR}#{separator}",
"#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}")
end
end
keys.join(".")
end
# Receives a string and escape the default separator.
def self.escape_default_separator(key) #:nodoc:
key.to_s.tr(FLATTEN_SEPARATOR, SEPARATOR_ESCAPE_CHAR)
end
# Shortcut to I18n::Backend::Flatten.normalize_flat_keys
# and then resolve_links.
def normalize_flat_keys(locale, key, scope, separator)
key = I18n::Backend::Flatten.normalize_flat_keys(locale, key, scope, separator)
resolve_link(locale, key)
end
# Store flattened links.
def links
@links ||= Hash.new { |h,k| h[k] = {} }
end
# Flatten keys for nested Hashes by chaining up keys:
#
# >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind
# => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }
#
def flatten_keys(hash, escape, prev_key=nil, &block)
hash.each_pair do |key, value|
key = escape_default_separator(key) if escape
curr_key = [prev_key, key].compact.join(FLATTEN_SEPARATOR).to_sym
yield curr_key, value
flatten_keys(value, escape, curr_key, &block) if value.is_a?(Hash)
end
end
# Receives a hash of translations (where the key is a locale and
# the value is another hash) and return a hash with all
# translations flattened.
#
# Nested hashes are included in the flattened hash just if subtree
# is true and Symbols are automatically stored as links.
def flatten_translations(locale, data, escape, subtree)
hash = {}
flatten_keys(data, escape) do |key, value|
if value.is_a?(Hash)
hash[key] = value if subtree
else
store_link(locale, key, value) if value.is_a?(Symbol)
hash[key] = value
end
end
hash
end
protected
def store_link(locale, key, link)
links[locale.to_sym][key.to_s] = link.to_s
end
def resolve_link(locale, key)
key, locale = key.to_s, locale.to_sym
links = self.links[locale]
if links.key?(key)
links[key]
elsif link = find_link(locale, key)
store_link(locale, key, key.gsub(*link))
else
key
end
end
def find_link(locale, key) #:nodoc:
links[locale].each do |from, to|
return [from, to] if key[0, from.length] == from
end && nil
end
def escape_default_separator(key) #:nodoc:
I18n::Backend::Flatten.escape_default_separator(key)
end
end
end
end

View File

@@ -1,75 +0,0 @@
# encoding: utf-8
require 'i18n/gettext'
require 'i18n/gettext/po_parser'
# Experimental support for using Gettext po files to store translations.
#
# To use this you can simply include the module to the Simple backend - or
# whatever other backend you are using.
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Gettext)
#
# Now you should be able to include your Gettext translation (*.po) files to
# the I18n.load_path so they're loaded to the backend and you can use them as
# usual:
#
# I18n.load_path += Dir["path/to/locales/*.po"]
#
# Following the Gettext convention this implementation expects that your
# translation files are named by their locales. E.g. the file en.po would
# contain the translations for the English locale.
module I18n
module Backend
module Gettext
class PoData < Hash
def set_comment(msgid_or_sym, comment)
# ignore
end
end
protected
def load_po(filename)
locale = ::File.basename(filename, '.po').to_sym
data = normalize(locale, parse(filename))
{ locale => data }
end
def parse(filename)
GetText::PoParser.new.parse(::File.read(filename), PoData.new)
end
def normalize(locale, data)
data.inject({}) do |result, (key, value)|
unless key.nil? || key.empty?
key, value = normalize_pluralization(locale, key, value) if key.index("\000")
parts = key.split('|').reverse
normalized = parts.inject({}) do |normalized, part|
normalized = { part => normalized.empty? ? value : normalized }
end
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
result.merge!(normalized, &merger)
end
result
end
end
def normalize_pluralization(locale, key, value)
# FIXME po_parser includes \000 chars that can not be turned into Symbols
key = key.gsub("\000", I18n::Gettext::PLURAL_SEPARATOR).split(I18n::Gettext::PLURAL_SEPARATOR).first
keys = I18n::Gettext.plural_keys(locale)
values = value.split("\000")
raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect}" if values.size != keys.size
result = {}
values.each_with_index { |value, ix| result[keys[ix]] = value }
[key, result]
end
end
end
end

View File

@@ -1,123 +0,0 @@
# encoding: utf-8
# The InterpolationCompiler module contains optimizations that can tremendously
# speed up the interpolation process on the Simple backend.
#
# It works by defining a pre-compiled method on stored translation Strings that
# already bring all the knowledge about contained interpolation variables etc.
# so that the actual recurring interpolation will be very fast.
#
# To enable pre-compiled interpolations you can simply include the
# InterpolationCompiler module to the Simple backend:
#
# I18n::Backend::Simple.send(:include, I18n::Backend::InterpolationCompiler)
#
# Note that InterpolationCompiler does not yield meaningful results and consequently
# should not be used with Ruby 1.9 (YARV) but improves performance everywhere else
# (jRuby, Rubinius and 1.8.7).
module I18n
module Backend
module InterpolationCompiler
module Compiler
extend self
TOKENIZER = /(%%\{[^\}]+\}|%\{[^\}]+\})/
INTERPOLATION_SYNTAX_PATTERN = /(%)?(%\{([^\}]+)\})/
def compile_if_an_interpolation(string)
if interpolated_str?(string)
string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__
def i18n_interpolate(v = {})
"#{compiled_interpolation_body(string)}"
end
RUBY_EVAL
end
string
end
def interpolated_str?(str)
str.kind_of?(::String) && str =~ INTERPOLATION_SYNTAX_PATTERN
end
protected
# tokenize("foo %{bar} baz %%{buz}") # => ["foo ", "%{bar}", " baz ", "%%{buz}"]
def tokenize(str)
str.split(TOKENIZER)
end
def compiled_interpolation_body(str)
tokenize(str).map do |token|
(matchdata = token.match(INTERPOLATION_SYNTAX_PATTERN)) ? handle_interpolation_token(token, matchdata) : escape_plain_str(token)
end.join
end
def handle_interpolation_token(interpolation, matchdata)
escaped, pattern, key = matchdata.values_at(1, 2, 3)
escaped ? pattern : compile_interpolation_token(key.to_sym)
end
def compile_interpolation_token(key)
"\#{#{interpolate_or_raise_missing(key)}}"
end
def interpolate_or_raise_missing(key)
escaped_key = escape_key_sym(key)
Base::RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key)
end
def interpolate_key(key)
[direct_key(key), nil_key(key), missing_key(key)].join('||')
end
def direct_key(key)
"((t = v[#{key}]) && t.respond_to?(:call) ? t.call : t)"
end
def nil_key(key)
"(v.has_key?(#{key}) && '')"
end
def missing_key(key)
"raise(MissingInterpolationArgument.new(#{key}, self))"
end
def reserved_key(key)
"raise(ReservedInterpolationKey.new(#{key}, self))"
end
def escape_plain_str(str)
str.gsub(/"|\\|#/) {|x| "\\#{x}"}
end
def escape_key_sym(key)
# rely on Ruby to do all the hard work :)
key.to_sym.inspect
end
end
def interpolate(locale, string, values)
if string.respond_to?(:i18n_interpolate)
string.i18n_interpolate(values)
elsif values
super
else
string
end
end
def store_translations(locale, data, options = {})
compile_all_strings_in(data)
super
end
protected
def compile_all_strings_in(data)
data.each_value do |value|
Compiler.compile_if_an_interpolation(value)
compile_all_strings_in(value) if value.kind_of?(Hash)
end
end
end
end
end

View File

@@ -1,102 +0,0 @@
# encoding: utf-8
require 'i18n/backend/base'
require 'active_support/json'
module I18n
module Backend
# This is a basic backend for key value stores. It receives on
# initialization the store, which should respond to three methods:
#
# * store#[](key) - Used to get a value
# * store#[]=(key, value) - Used to set a value
# * store#keys - Used to get all keys
#
# Since these stores only supports string, all values are converted
# to JSON before being stored, allowing it to also store booleans,
# hashes and arrays. However, this store does not support Procs.
#
# As the ActiveRecord backend, Symbols are just supported when loading
# translations from the filesystem or through explicit store translations.
#
# Also, avoid calling I18n.available_locales since it's a somehow
# expensive operation in most stores.
#
# == Example
#
# To setup I18n to use TokyoCabinet in memory is quite straightforward:
#
# require 'rufus/tokyo/cabinet' # gem install rufus-tokyo
# I18n.backend = I18n::Backend::KeyValue.new(Rufus::Tokyo::Cabinet.new('*'))
#
# == Performance
#
# You may make this backend even faster by including the Memoize module.
# However, notice that you should properly clear the cache if you change
# values directly in the key-store.
#
# == Subtrees
#
# In most backends, you are allowed to retrieve part of a translation tree:
#
# I18n.backend.store_translations :en, :foo => { :bar => :baz }
# I18n.t "foo" #=> { :bar => :baz }
#
# This backend supports this feature by default, but it slows down the storage
# of new data considerably and makes hard to delete entries. That said, you are
# allowed to disable the storage of subtrees on initialization:
#
# I18n::Backend::KeyValue.new(@store, false)
#
# This is useful if you are using a KeyValue backend chained to a Simple backend.
class KeyValue
module Implementation
attr_accessor :store
include Base, Flatten
def initialize(store, subtrees=true)
@store, @subtrees = store, subtrees
end
def store_translations(locale, data, options = {})
escape = options.fetch(:escape, true)
flatten_translations(locale, data, escape, @subtrees).each do |key, value|
key = "#{locale}.#{key}"
case value
when Hash
if @subtrees && (old_value = @store[key])
old_value = ActiveSupport::JSON.decode(old_value)
value = old_value.deep_symbolize_keys.deep_merge!(value) if old_value.is_a?(Hash)
end
when Proc
raise "Key-value stores cannot handle procs"
end
@store[key] = ActiveSupport::JSON.encode(value) unless value.is_a?(Symbol)
end
end
def available_locales
locales = @store.keys.map { |k| k =~ /\./; $` }
locales.uniq!
locales.compact!
locales.map! { |k| k.to_sym }
locales
end
protected
def lookup(locale, key, scope = [], options = {})
key = normalize_flat_keys(locale, key, scope, options[:separator])
value = @store["#{locale}.#{key}"]
value = ActiveSupport::JSON.decode(value) if value
value.is_a?(Hash) ? value.deep_symbolize_keys : value
end
end
include Implementation
end
end
end

View File

@@ -1,48 +0,0 @@
# encoding: utf-8
#
# Memoize module simply memoizes the values returned by lookup using
# a flat hash and can tremendously speed up the lookup process in a backend.
#
# To enable it you can simply include the Memoize module to your backend:
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Memoize)
#
# Notice that it's the responsibility of the backend to define whenever the
# cache should be cleaned.
module I18n
module Backend
module Memoize
def available_locales
@memoized_locales ||= super
end
def store_translations(locale, data, options = {})
reset_memoizations!(locale)
super
end
def reload!
reset_memoizations!
super
end
protected
def lookup(locale, key, scope = nil, options = {})
flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale,
key, scope, options[:separator]).to_sym
flat_hash = memoized_lookup[locale.to_sym]
flat_hash.key?(flat_key) ? flat_hash[flat_key] : (flat_hash[flat_key] = super)
end
def memoized_lookup
@memoized_lookup ||= Hash.new { |h, k| h[k] = {} }
end
def reset_memoizations!(locale=nil)
@memoized_locales = nil
(locale ? memoized_lookup[locale.to_sym] : memoized_lookup).clear
end
end
end
end

View File

@@ -1,65 +0,0 @@
# I18n translation metadata is useful when you want to access information
# about how a translation was looked up, pluralized or interpolated in
# your application.
#
# msg = I18n.t(:message, :default => 'Hi!', :scope => :foo)
# msg.translation_metadata
# # => { :key => :message, :scope => :foo, :default => 'Hi!' }
#
# If a :count option was passed to #translate it will be set to the metadata.
# Likewise, if any interpolation variables were passed they will also be set.
#
# To enable translation metadata you can simply include the Metadata module
# into the Simple backend class - or whatever other backend you are using:
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Metadata)
#
module I18n
module Backend
module Metadata
class << self
def included(base)
Object.class_eval do
def translation_metadata
@translation_metadata ||= {}
end
def translation_metadata=(translation_metadata)
@translation_metadata = translation_metadata
end
end unless Object.method_defined?(:translation_metadata)
end
end
def translate(locale, key, options = {})
metadata = {
:locale => locale,
:key => key,
:scope => options[:scope],
:default => options[:default],
:separator => options[:separator],
:values => options.reject { |name, value| Base::RESERVED_KEYS.include?(name) }
}
with_metadata(metadata) { super }
end
def interpolate(locale, entry, values = {})
metadata = entry.translation_metadata.merge(:original => entry)
with_metadata(metadata) { super }
end
def pluralize(locale, entry, count)
with_metadata(:count => count) { super }
end
protected
def with_metadata(metadata, &block)
result = yield
result.translation_metadata = result.translation_metadata.merge(metadata) if result
result
end
end
end
end

View File

@@ -1,57 +0,0 @@
# encoding: utf-8
# I18n locale fallbacks are useful when you want your application to use
# translations from other locales when translations for the current locale are
# missing. E.g. you might want to use :en translations when translations in
# your applications main locale :de are missing.
#
# To enable locale specific pluralizations you can simply include the
# Pluralization module to the Simple backend - or whatever other backend you
# are using.
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
#
# You also need to make sure to provide pluralization algorithms to the
# backend, i.e. include them to your I18n.load_path accordingly.
module I18n
module Backend
module Pluralization
# Overwrites the Base backend translate method so that it will check the
# translation meta data space (:i18n) for a locale specific pluralization
# rule and use it to pluralize the given entry. I.e. the library expects
# pluralization rules to be stored at I18n.t(:'i18n.plural.rule')
#
# Pluralization rules are expected to respond to #call(entry, count) and
# return a pluralization key. Valid keys depend on the translation data
# hash (entry) but it is generally recommended to follow CLDR's style,
# i.e., return one of the keys :zero, :one, :few, :many, :other.
#
# The :zero key is always picked directly when count equals 0 AND the
# translation data has the key :zero. This way translators are free to
# either pick a special :zero translation even for languages where the
# pluralizer does not return a :zero key.
def pluralize(locale, entry, count)
return entry unless entry.is_a?(Hash) and count
pluralizer = pluralizer(locale)
if pluralizer.respond_to?(:call)
key = count == 0 && entry.has_key?(:zero) ? :zero : pluralizer.call(count)
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
entry[key]
else
super
end
end
protected
def pluralizers
@pluralizers ||= {}
end
def pluralizer(locale)
pluralizers[locale] ||= I18n.t(:'i18n.plural.rule', :locale => locale, :resolve => false)
end
end
end
end

View File

@@ -1,87 +0,0 @@
# encoding: utf-8
module I18n
module Backend
# A simple backend that reads translations from YAML files and stores them in
# an in-memory hash. Relies on the Base backend.
#
# The implementation is provided by a Implementation module allowing to easily
# extend Simple backend's behavior by including modules. E.g.:
#
# module I18n::Backend::Pluralization
# def pluralize(*args)
# # extended pluralization logic
# super
# end
# end
#
# I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)
class Simple
module Implementation
include Base
def initialized?
@initialized ||= false
end
# Stores translations for the given locale in memory.
# This uses a deep merge for the translations hash, so existing
# translations will be overwritten by new ones only at the deepest
# level of the hash.
def store_translations(locale, data, options = {})
locale = locale.to_sym
translations[locale] ||= {}
data = data.deep_symbolize_keys
translations[locale].deep_merge!(data)
end
# Get available locales from the translations hash
def available_locales
init_translations unless initialized?
translations.inject([]) do |locales, (locale, data)|
locales << locale unless (data.keys - [:i18n]).empty?
locales
end
end
# Clean up translations hash and set initialized to false on reload!
def reload!
@initialized = false
@translations = nil
super
end
protected
def init_translations
load_translations
@initialized = true
end
def translations
@translations ||= {}
end
# Looks up a translation from the translations hash. Returns nil if
# eiher key is nil, or locale, scope or key do not exist as a key in the
# nested translations hash. Splits keys or scopes containing dots
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
# <tt>%w(currency format)</tt>.
def lookup(locale, key, scope = [], options = {})
init_translations unless initialized?
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
keys.inject(translations) do |result, key|
key = key.to_sym
return nil unless result.is_a?(Hash) && result.has_key?(key)
result = result[key]
result = resolve(locale, key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
result
end
end
end
include Implementation
end
end
end

View File

@@ -1,98 +0,0 @@
# encoding: utf-8
module I18n
module Backend
module Transliterator
DEFAULT_REPLACEMENT_CHAR = "?"
# Given a locale and a UTF-8 string, return the locale's ASCII
# approximation for the string.
def transliterate(locale, string, replacement = nil)
@transliterators ||= {}
@transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
:locale => locale, :resolve => false, :default => {})
@transliterators[locale].transliterate(string, replacement)
end
# Get a transliterator instance.
def self.get(rule = nil)
if !rule || rule.kind_of?(Hash)
HashTransliterator.new(rule)
elsif rule.kind_of? Proc
ProcTransliterator.new(rule)
else
raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
end
end
# A transliterator which accepts a Proc as its transliteration rule.
class ProcTransliterator
def initialize(rule)
@rule = rule
end
def transliterate(string, replacement = nil)
@rule.call(string)
end
end
# A transliterator which accepts a Hash of characters as its translation
# rule.
class HashTransliterator
DEFAULT_APPROXIMATIONS = {
"À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
"ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
"ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
"ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
"ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
"Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
"ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
"Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
"ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
"Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
"ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
"Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
"ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
"ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
"Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
"Ž"=>"Z", "ž"=>"z"
}
def initialize(rule = nil)
@rule = rule
add DEFAULT_APPROXIMATIONS
add rule if rule
end
def transliterate(string, replacement = nil)
string.gsub(/[^\x00-\x7f]/u) do |char|
approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
end
end
private
def approximations
@approximations ||= {}
end
# Add transliteration rules to the approximations hash.
def add(hash)
hash.keys.each {|key| hash[key.to_s] = hash.delete(key).to_s}
approximations.merge! hash
end
end
end
end
end

View File

@@ -1,84 +0,0 @@
module I18n
class Config
# The only configuration value that is not global and scoped to thread is :locale.
# It defaults to the default_locale.
def locale
@locale ||= default_locale
end
# Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
def locale=(locale)
@locale = locale.to_sym rescue nil
end
# Returns the current backend. Defaults to +Backend::Simple+.
def backend
@@backend ||= Backend::Simple.new
end
# Sets the current backend. Used to set a custom backend.
def backend=(backend)
@@backend = backend
end
# Returns the current default locale. Defaults to :'en'
def default_locale
@@default_locale ||= :en
end
# Sets the current default locale. Used to set a custom default locale.
def default_locale=(locale)
@@default_locale = locale.to_sym rescue nil
end
# Returns an array of locales for which translations are available.
# Unless you explicitely set the these through I18n.available_locales=
# the call will be delegated to the backend and memoized on the I18n module.
def available_locales
@@available_locales ||= backend.available_locales
end
# Sets the available locales.
def available_locales=(locales)
@@available_locales = locales
end
# Returns the current default scope separator. Defaults to '.'
def default_separator
@@default_separator ||= '.'
end
# Sets the current default scope separator.
def default_separator=(separator)
@@default_separator = separator
end
# Return the current exception handler. Defaults to :default_exception_handler.
def exception_handler
@@exception_handler ||= :default_exception_handler
end
# Sets the exception handler.
def exception_handler=(exception_handler)
@@exception_handler = exception_handler
end
# Allow clients to register paths providing translation data sources. The
# backend defines acceptable sources.
#
# E.g. the provided SimpleBackend accepts a list of paths to translation
# files which are either named *.rb and contain plain Ruby Hashes or are
# named *.yml and contain YAML data. So for the SimpleBackend clients may
# register translation files like this:
# I18n.load_path << 'path/to/locale/en.yml'
def load_path
@@load_path ||= []
end
# Sets the load path instance. Custom implementations are expected to
# behave like a Ruby Array.
def load_path=(load_path)
@@load_path = load_path
end
end
end

View File

@@ -1,29 +0,0 @@
class Hash
def slice(*keep_keys)
h = {}
keep_keys.each { |key| h[key] = fetch(key) }
h
end unless Hash.method_defined?(:slice)
def except(*less_keys)
slice(*keys - less_keys)
end unless Hash.method_defined?(:except)
def deep_symbolize_keys
inject({}) { |result, (key, value)|
value = value.deep_symbolize_keys if value.is_a?(Hash)
result[(key.to_sym rescue key) || key] = value
result
}
end unless Hash.method_defined?(:deep_symbolize_keys)
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
MERGER = proc do |key, v1, v2|
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
end
def deep_merge!(data)
merge!(data, &MERGER)
end unless Hash.method_defined?(:deep_merge!)
end

View File

@@ -1,98 +0,0 @@
# encoding: utf-8
=begin
heavily based on Masao Mutoh's gettext String interpolation extension
http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
Copyright (C) 2005-2009 Masao Mutoh
You may redistribute it and/or modify it under the same license terms as Ruby.
=end
begin
raise ArgumentError if ("a %{x}" % {:x=>'b'}) != 'a b'
rescue ArgumentError
# KeyError is raised by String#% when the string contains a named placeholder
# that is not contained in the given arguments hash. Ruby 1.9 includes and
# raises this exception natively. We define it to mimic Ruby 1.9's behaviour
# in Ruby 1.8.x
class KeyError < IndexError
def initialize(message = nil)
super(message || "key not found")
end
end unless defined?(KeyError)
# Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
#
# String#% method which accept "named argument". The translator can know
# the meaning of the msgids using "named argument" instead of %s/%d style.
class String
# For older ruby versions, such as ruby-1.8.5
alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'}
alias :interpolate_without_ruby_19_syntax :% # :nodoc:
INTERPOLATION_PATTERN = Regexp.union(
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
)
INTERPOLATION_PATTERN_WITH_ESCAPE = Regexp.union(
/%%/,
INTERPOLATION_PATTERN
)
# % uses self (i.e. the String) as a format specification and returns the
# result of applying it to the given arguments. In other words it interpolates
# the given arguments to the string according to the formats the string
# defines.
#
# There are three ways to use it:
#
# * Using a single argument or Array of arguments.
#
# This is the default behaviour of the String class. See Kernel#sprintf for
# more details about the format string.
#
# Example:
#
# "%d %s" % [1, "message"]
# # => "1 message"
#
# * Using a Hash as an argument and unformatted, named placeholders.
#
# When you pass a Hash as an argument and specify placeholders with %{foo}
# it will interpret the hash values as named arguments.
#
# Example:
#
# "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
# # => "Masao Mutoh"
#
# * Using a Hash as an argument and formatted, named placeholders.
#
# When you pass a Hash as an argument and specify placeholders with %<foo>d
# it will interpret the hash values as named arguments and format the value
# according to the formatting instruction appended to the closing >.
#
# Example:
#
# "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
# # => "10, 43.3"
def %(args)
if args.kind_of?(Hash)
dup.gsub(INTERPOLATION_PATTERN_WITH_ESCAPE) do |match|
if match == '%%'
'%'
else
key = ($1 || $2).to_sym
raise KeyError unless args.has_key?(key)
$3 ? sprintf("%#{$3}", args[key]) : args[key]
end
end
elsif self =~ INTERPOLATION_PATTERN
raise ArgumentError.new('one hash required')
else
result = gsub(/%([{<])/, '%%\1')
result.send :'interpolate_without_ruby_19_syntax', args
end
end
end
end

View File

@@ -1,61 +0,0 @@
# encoding: utf-8
class KeyError < IndexError
def initialize(message = nil)
super(message || "key not found")
end
end unless defined?(KeyError)
module I18n
class ArgumentError < ::ArgumentError; end
class InvalidLocale < ArgumentError
attr_reader :locale
def initialize(locale)
@locale = locale
super "#{locale.inspect} is not a valid locale"
end
end
class MissingTranslationData < ArgumentError
attr_reader :locale, :key, :options
def initialize(locale, key, opts = nil)
@key, @locale, @options = key, locale, opts || {}
keys = I18n.normalize_keys(locale, key, options[:scope])
keys << 'no key' if keys.size < 2
super "translation missing: #{keys.join(', ')}"
end
end
class InvalidPluralizationData < ArgumentError
attr_reader :entry, :count
def initialize(entry, count)
@entry, @count = entry, count
super "translation data #{entry.inspect} can not be used with :count => #{count}"
end
end
class MissingInterpolationArgument < ArgumentError
attr_reader :values, :string
def initialize(values, string)
@values, @string = values, string
super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)"
end
end
class ReservedInterpolationKey < ArgumentError
attr_reader :key, :string
def initialize(key, string)
@key, @string = key, string
super "reserved key #{key.inspect} used in #{string.inspect}"
end
end
class UnknownFileType < ArgumentError
attr_reader :type, :filename
def initialize(type, filename)
@type, @filename = type, filename
super "can not load translations from #{filename}, the file type #{type} is not known"
end
end
end

View File

@@ -1,27 +0,0 @@
# encoding: utf-8
module I18n
module Gettext
PLURAL_SEPARATOR = "\001"
CONTEXT_SEPARATOR = "\004"
autoload :Helpers, 'i18n/gettext/helpers'
@@plural_keys = { :en => [:one, :other] }
class << self
# returns an array of plural keys for the given locale so that we can
# convert from gettext's integer-index based style
# TODO move this information to the pluralization module
def plural_keys(locale)
@@plural_keys[locale] || @@plural_keys[:en]
end
def extract_scope(msgid, separator)
scope = msgid.to_s.split(separator)
msgid = scope.pop
[scope, msgid]
end
end
end
end

View File

@@ -1,65 +0,0 @@
# encoding: utf-8
require 'i18n/gettext'
module I18n
module Gettext
# Implements classical Gettext style accessors. To use this include the
# module to the global namespace or wherever you want to use it.
#
# include I18n::Helpers::Gettext
module Helpers
def gettext(msgid, options = {})
I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options))
end
alias _ gettext
def sgettext(msgid, separator = '|')
scope, msgid = I18n::Gettext.extract_scope(msgid, separator)
I18n.t(msgid, :scope => scope, :default => msgid, :separator => separator)
end
alias s_ sgettext
def pgettext(msgctxt, msgid)
separator = I18n::Gettext::CONTEXT_SEPARATOR
sgettext([msgctxt, msgid].join(separator), separator)
end
alias p_ pgettext
def ngettext(msgid, msgid_plural, n = 1)
nsgettext(msgid, msgid_plural, n)
end
alias n_ ngettext
# Method signatures:
# nsgettext('Fruits|apple', 'apples', 2)
# nsgettext(['Fruits|apple', 'apples'], 2)
def nsgettext(msgid, msgid_plural, n = 1, separator = '|')
if msgid.is_a?(Array)
msgid, msgid_plural, n, separator = msgid[0], msgid[1], msgid_plural, n
separator = '|' unless separator.is_a?(::String)
end
scope, msgid = I18n::Gettext.extract_scope(msgid, separator)
default = { :one => msgid, :other => msgid_plural }
I18n.t(msgid, :default => default, :count => n, :scope => scope, :separator => separator)
end
alias ns_ nsgettext
# Method signatures:
# npgettext('Fruits', 'apple', 'apples', 2)
# npgettext('Fruits', ['apple', 'apples'], 2)
def npgettext(msgctxt, msgid, msgid_plural, n = 1)
separator = I18n::Gettext::CONTEXT_SEPARATOR
if msgid.is_a?(Array)
msgid_plural, msgid, n = msgid[1], [msgctxt, msgid[0]].join(separator), msgid_plural
else
msgid = [msgctxt, msgid].join(separator)
end
nsgettext(msgid, msgid_plural, n, separator)
end
alias np_ npgettext
end
end
end

View File

@@ -1,329 +0,0 @@
=begin
poparser.rb - Generate a .mo
Copyright (C) 2003-2009 Masao Mutoh <mutoh at highway.ne.jp>
You may redistribute it and/or modify it under the same
license terms as Ruby.
=end
#MODIFIED
# removed include GetText etc
# added stub translation method _(x)
require 'racc/parser'
module GetText
class PoParser < Racc::Parser
def _(x)
x
end
module_eval <<'..end src/poparser.ry modeval..id7a99570e05', 'src/poparser.ry', 108
def unescape(orig)
ret = orig.gsub(/\\n/, "\n")
ret.gsub!(/\\t/, "\t")
ret.gsub!(/\\r/, "\r")
ret.gsub!(/\\"/, "\"")
ret
end
def parse(str, data, ignore_fuzzy = true)
@comments = []
@data = data
@fuzzy = false
@msgctxt = ""
$ignore_fuzzy = ignore_fuzzy
str.strip!
@q = []
until str.empty? do
case str
when /\A\s+/
str = $'
when /\Amsgctxt/
@q.push [:MSGCTXT, $&]
str = $'
when /\Amsgid_plural/
@q.push [:MSGID_PLURAL, $&]
str = $'
when /\Amsgid/
@q.push [:MSGID, $&]
str = $'
when /\Amsgstr/
@q.push [:MSGSTR, $&]
str = $'
when /\A\[(\d+)\]/
@q.push [:PLURAL_NUM, $1]
str = $'
when /\A\#~(.*)/
$stderr.print _("Warning: obsolete msgid exists.\n")
$stderr.print " #{$&}\n"
@q.push [:COMMENT, $&]
str = $'
when /\A\#(.*)/
@q.push [:COMMENT, $&]
str = $'
when /\A\"(.*)\"/
@q.push [:STRING, $1]
str = $'
else
#c = str[0,1]
#@q.push [:STRING, c]
str = str[1..-1]
end
end
@q.push [false, '$end']
if $DEBUG
@q.each do |a,b|
puts "[#{a}, #{b}]"
end
end
@yydebug = true if $DEBUG
do_parse
if @comments.size > 0
@data.set_comment(:last, @comments.join("\n"))
end
@data
end
def next_token
@q.shift
end
def on_message(msgid, msgstr)
if msgstr.size > 0
@data[msgid] = msgstr
@data.set_comment(msgid, @comments.join("\n"))
end
@comments.clear
@msgctxt = ""
end
def on_comment(comment)
@fuzzy = true if (/fuzzy/ =~ comment)
@comments << comment
end
..end src/poparser.ry modeval..id7a99570e05
##### racc 1.4.5 generates ###
racc_reduce_table = [
0, 0, :racc_error,
0, 10, :_reduce_none,
2, 10, :_reduce_none,
2, 10, :_reduce_none,
2, 10, :_reduce_none,
2, 12, :_reduce_5,
1, 13, :_reduce_none,
1, 13, :_reduce_none,
4, 15, :_reduce_8,
5, 16, :_reduce_9,
2, 17, :_reduce_10,
1, 17, :_reduce_none,
3, 18, :_reduce_12,
1, 11, :_reduce_13,
2, 14, :_reduce_14,
1, 14, :_reduce_15 ]
racc_reduce_n = 16
racc_shift_n = 26
racc_action_table = [
3, 13, 5, 7, 9, 15, 16, 17, 20, 17,
13, 17, 13, 13, 11, 17, 23, 20, 13, 17 ]
racc_action_check = [
1, 16, 1, 1, 1, 12, 12, 12, 18, 18,
7, 14, 15, 9, 3, 19, 20, 21, 23, 25 ]
racc_action_pointer = [
nil, 0, nil, 14, nil, nil, nil, 3, nil, 6,
nil, nil, 0, nil, 4, 5, -6, nil, 2, 8,
8, 11, nil, 11, nil, 12 ]
racc_action_default = [
-1, -16, -2, -16, -3, -13, -4, -16, -6, -16,
-7, 26, -16, -15, -5, -16, -16, -14, -16, -8,
-16, -9, -11, -16, -10, -12 ]
racc_goto_table = [
12, 22, 14, 4, 24, 6, 2, 8, 18, 19,
10, 21, 1, nil, nil, nil, 25 ]
racc_goto_check = [
5, 9, 5, 3, 9, 4, 2, 6, 5, 5,
7, 8, 1, nil, nil, nil, 5 ]
racc_goto_pointer = [
nil, 12, 5, 2, 4, -7, 6, 9, -7, -17 ]
racc_goto_default = [
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil ]
racc_token_table = {
false => 0,
Object.new => 1,
:COMMENT => 2,
:MSGID => 3,
:MSGCTXT => 4,
:MSGID_PLURAL => 5,
:MSGSTR => 6,
:STRING => 7,
:PLURAL_NUM => 8 }
racc_use_result_var = true
racc_nt_base = 9
Racc_arg = [
racc_action_table,
racc_action_check,
racc_action_default,
racc_action_pointer,
racc_goto_table,
racc_goto_check,
racc_goto_default,
racc_goto_pointer,
racc_nt_base,
racc_reduce_table,
racc_token_table,
racc_shift_n,
racc_reduce_n,
racc_use_result_var ]
Racc_token_to_s_table = [
'$end',
'error',
'COMMENT',
'MSGID',
'MSGCTXT',
'MSGID_PLURAL',
'MSGSTR',
'STRING',
'PLURAL_NUM',
'$start',
'msgfmt',
'comment',
'msgctxt',
'message',
'string_list',
'single_message',
'plural_message',
'msgstr_plural',
'msgstr_plural_line']
Racc_debug_parser = true
##### racc system variables end #####
# reduce 0 omitted
# reduce 1 omitted
# reduce 2 omitted
# reduce 3 omitted
# reduce 4 omitted
module_eval <<'.,.,', 'src/poparser.ry', 25
def _reduce_5( val, _values, result )
@msgctxt = unescape(val[1]) + "\004"
result
end
.,.,
# reduce 6 omitted
# reduce 7 omitted
module_eval <<'.,.,', 'src/poparser.ry', 48
def _reduce_8( val, _values, result )
if @fuzzy and $ignore_fuzzy
if val[1] != ""
$stderr.print _("Warning: fuzzy message was ignored.\n")
$stderr.print " msgid '#{val[1]}'\n"
else
on_message('', unescape(val[3]))
end
@fuzzy = false
else
on_message(@msgctxt + unescape(val[1]), unescape(val[3]))
end
result = ""
result
end
.,.,
module_eval <<'.,.,', 'src/poparser.ry', 65
def _reduce_9( val, _values, result )
if @fuzzy and $ignore_fuzzy
if val[1] != ""
$stderr.print _("Warning: fuzzy message was ignored.\n")
$stderr.print "msgid = '#{val[1]}\n"
else
on_message('', unescape(val[3]))
end
@fuzzy = false
else
on_message(@msgctxt + unescape(val[1]) + "\000" + unescape(val[3]), unescape(val[4]))
end
result = ""
result
end
.,.,
module_eval <<'.,.,', 'src/poparser.ry', 76
def _reduce_10( val, _values, result )
if val[0].size > 0
result = val[0] + "\000" + val[1]
else
result = ""
end
result
end
.,.,
# reduce 11 omitted
module_eval <<'.,.,', 'src/poparser.ry', 84
def _reduce_12( val, _values, result )
result = val[2]
result
end
.,.,
module_eval <<'.,.,', 'src/poparser.ry', 91
def _reduce_13( val, _values, result )
on_comment(val[0])
result
end
.,.,
module_eval <<'.,.,', 'src/poparser.ry', 99
def _reduce_14( val, _values, result )
result = val.delete_if{|item| item == ""}.join
result
end
.,.,
module_eval <<'.,.,', 'src/poparser.ry', 103
def _reduce_15( val, _values, result )
result = val[0]
result
end
.,.,
def _reduce_none( val, _values, result )
result
end
end # class PoParser
end # module GetText

View File

@@ -1,6 +0,0 @@
module I18n
module Locale
autoload :Fallbacks, 'i18n/locale/fallbacks'
autoload :Tag, 'i18n/locale/tag'
end
end

View File

@@ -1,98 +0,0 @@
# encoding: utf-8
# Locale Fallbacks
#
# Extends the I18n module to hold a fallbacks instance which is set to an
# instance of I18n::Locale::Fallbacks by default but can be swapped with a
# different implementation.
#
# Locale fallbacks will compute a number of fallback locales for a given locale.
# For example:
#
# <pre><code>
# I18n.fallbacks[:"es-MX"] # => [:"es-MX", :es, :en] </code></pre>
#
# Locale fallbacks always fall back to
#
# * all parent locales of a given locale (e.g. :es for :"es-MX") first,
# * the current default locales and all of their parents second
#
# The default locales are set to [I18n.default_locale] by default but can be
# set to something else.
#
# One can additionally add any number of additional fallback locales manually.
# These will be added before the default locales to the fallback chain. For
# example:
#
# # using the default locale as default fallback locale
#
# I18n.default_locale = :"en-US"
# I18n.fallbacks = I18n::Fallbacks.new(:"de-AT" => :"de-DE")
# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :"de-DE", :de, :"en-US", :en]
#
# # using a custom locale as default fallback locale
#
# I18n.fallbacks = I18n::Fallbacks.new(:"en-GB", :"de-AT" => :de, :"de-CH" => :de)
# I18n.fallbacks[:"de-AT"] # => [:"de-AT", :de, :"en-GB", :en]
# I18n.fallbacks[:"de-CH"] # => [:"de-CH", :de, :"en-GB", :en]
#
# # mapping fallbacks to an existing instance
#
# # people speaking Catalan also speak Spanish as spoken in Spain
# fallbacks = I18n.fallbacks
# fallbacks.map(:ca => :"es-ES")
# fallbacks[:ca] # => [:ca, :"es-ES", :es, :"en-US", :en]
#
# # people speaking Arabian as spoken in Palestine also speak Hebrew as spoken in Israel
# fallbacks.map(:"ar-PS" => :"he-IL")
# fallbacks[:"ar-PS"] # => [:"ar-PS", :ar, :"he-IL", :he, :"en-US", :en]
# fallbacks[:"ar-EG"] # => [:"ar-EG", :ar, :"en-US", :en]
#
# # people speaking Sami as spoken in Finnland also speak Swedish and Finnish as spoken in Finnland
# fallbacks.map(:sms => [:"se-FI", :"fi-FI"])
# fallbacks[:sms] # => [:sms, :"se-FI", :se, :"fi-FI", :fi, :"en-US", :en]
module I18n
module Locale
class Fallbacks < Hash
def initialize(*mappings)
@map = {}
map(mappings.pop) if mappings.last.is_a?(Hash)
self.defaults = mappings.empty? ? [I18n.default_locale.to_sym] : mappings
end
def defaults=(defaults)
@defaults = defaults.map { |default| compute(default, false) }.flatten
end
attr_reader :defaults
def [](locale)
raise InvalidLocale.new(locale) if locale.nil?
locale = locale.to_sym
super || store(locale, compute(locale))
end
def map(mappings)
mappings.each do |from, to|
from, to = from.to_sym, Array(to)
to.each do |to|
@map[from] ||= []
@map[from] << to.to_sym
end
end
end
protected
def compute(tags, include_defaults = true)
result = Array(tags).collect do |tag|
tags = I18n::Locale::Tag.tag(tag).self_and_parents.map! { |t| t.to_sym }
tags.each { |tag| tags += compute(@map[tag]) if @map[tag] }
tags
end.flatten
result.push(*defaults) if include_defaults
result.uniq
end
end
end
end

View File

@@ -1,28 +0,0 @@
# encoding: utf-8
module I18n
module Locale
module Tag
autoload :Parents, 'i18n/locale/tag/parents'
autoload :Rfc4646, 'i18n/locale/tag/rfc4646'
autoload :Simple, 'i18n/locale/tag/simple'
class << self
# Returns the current locale tag implementation. Defaults to +I18n::Locale::Tag::Simple+.
def implementation
@@implementation ||= Simple
end
# Sets the current locale tag implementation. Use this to set a different locale tag implementation.
def implementation=(implementation)
@@implementation = implementation
end
# Factory method for locale tags. Delegates to the current locale tag implementation.
def tag(tag)
implementation.tag(tag)
end
end
end
end
end

View File

@@ -1,24 +0,0 @@
# encoding: utf-8
module I18n
module Locale
module Tag
module Parents
def parent
@parent ||= begin
segs = to_a.compact
segs.length > 1 ? self.class.tag(*segs[0..(segs.length-2)].join('-')) : nil
end
end
def self_and_parents
@self_and_parents ||= [self] + parents
end
def parents
@parents ||= ([parent] + (parent ? parent.parents : [])).compact
end
end
end
end
end

View File

@@ -1,76 +0,0 @@
# encoding: utf-8
# RFC 4646/47 compliant Locale tag implementation that parses locale tags to
# subtags such as language, script, region, variant etc.
#
# For more information see by http://en.wikipedia.org/wiki/IETF_language_tag
#
# Rfc4646::Parser does not implement grandfathered tags.
module I18n
module Locale
module Tag
RFC4646_SUBTAGS = [ :language, :script, :region, :variant, :extension, :privateuse, :grandfathered ]
RFC4646_FORMATS = { :language => :downcase, :script => :capitalize, :region => :upcase, :variant => :downcase }
class Rfc4646 < Struct.new(*RFC4646_SUBTAGS)
class << self
# Parses the given tag and returns a Tag instance if it is valid.
# Returns false if the given tag is not valid according to RFC 4646.
def tag(tag)
matches = parser.match(tag)
new(*matches) if matches
end
def parser
@@parser ||= Rfc4646::Parser
end
def parser=(parser)
@@parser = parser
end
end
include Parents
RFC4646_FORMATS.each do |name, format|
define_method(name) { self[name].send(format) unless self[name].nil? }
end
def to_sym
to_s.to_sym
end
def to_s
@tag ||= to_a.compact.join("-")
end
def to_a
members.collect { |attr| self.send(attr) }
end
module Parser
PATTERN = %r{\A(?:
([a-z]{2,3}(?:(?:-[a-z]{3}){0,3})?|[a-z]{4}|[a-z]{5,8}) # language
(?:-([a-z]{4}))? # script
(?:-([a-z]{2}|\d{3}))? # region
(?:-([0-9a-z]{5,8}|\d[0-9a-z]{3}))* # variant
(?:-([0-9a-wyz](?:-[0-9a-z]{2,8})+))* # extension
(?:-(x(?:-[0-9a-z]{1,8})+))?| # privateuse subtag
(x(?:-[0-9a-z]{1,8})+)| # privateuse tag
/* ([a-z]{1,3}(?:-[0-9a-z]{2,8}){1,2}) */ # grandfathered
)\z}xi
class << self
def match(tag)
c = PATTERN.match(tag.to_s).captures
c[0..4] << (c[5].nil? ? c[6] : c[5]) << c[7] # TODO c[7] is grandfathered, throw a NotImplemented exception here?
rescue
false
end
end
end
end
end
end
end

View File

@@ -1,41 +0,0 @@
# encoding: utf-8
# Simple Locale tag implementation that computes subtags by simply splitting
# the locale tag at '-' occurences.
module I18n
module Locale
module Tag
class Simple
class << self
def tag(tag)
new(tag)
end
end
include Parents
attr_reader :tag
def initialize(*tag)
@tag = tag.join('-').to_sym
end
def subtags
@subtags = tag.to_s.split('-').map { |subtag| subtag.to_s }
end
def to_sym
tag
end
def to_s
tag.to_s
end
def to_a
subtags
end
end
end
end
end

View File

@@ -1,3 +0,0 @@
module I18n
VERSION = "0.4.1"
end

View File

@@ -505,7 +505,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal "<person>", xml.first(8)
assert xml.include?(%(<street>Paulina</street>))
assert xml.include?(%(<name>David</name>))
assert xml.include?(%(<age nil="true"></age>))
assert_includes xml, %(<age nil="true"/>)
end
def test_one_level_with_skipping_types
@@ -513,7 +513,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal "<person>", xml.first(8)
assert xml.include?(%(<street>Paulina</street>))
assert xml.include?(%(<name>David</name>))
assert xml.include?(%(<age nil="true"></age>))
assert_includes xml, %(<age nil="true"/>)
end
def test_one_level_with_yielding
@@ -618,12 +618,12 @@ class HashToXmlTest < Test::Unit::TestCase
EOT
expected_topic_hash = {
:title => nil,
:title => nil,
:id => nil,
:approved => nil,
:written_on => nil,
:viewed_at => nil,
:content => nil,
:content => nil,
:parent_id => nil
}.stringify_keys
@@ -701,7 +701,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"]
end
def test_empty_array_from_xml
blog_xml = <<-XML
<blog>
@@ -815,13 +815,13 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal expected_bacon_hash, Hash.from_xml(bacon_xml)["bacon"]
end
def test_type_trickles_through_when_unknown
product_xml = <<-EOT
<product>
<weight type="double">0.5</weight>
<image type="ProductImage"><filename>image.gif</filename></image>
</product>
EOT
@@ -830,7 +830,7 @@ class HashToXmlTest < Test::Unit::TestCase
:image => {'type' => 'ProductImage', 'filename' => 'image.gif' },
}.stringify_keys
assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"]
assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"]
end
def test_should_use_default_value_for_unknown_key
@@ -864,41 +864,41 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal expected, hash.to_xml(@xml_options)
end
end
def test_empty_string_works_for_typecast_xml_value
def test_empty_string_works_for_typecast_xml_value
assert_nothing_raised do
Hash.__send__(:typecast_xml_value, "")
end
end
def test_escaping_to_xml
hash = {
:bare_string => 'First & Last Name',
hash = {
:bare_string => 'First & Last Name',
:pre_escaped_string => 'First &amp; Last Name'
}.stringify_keys
expected_xml = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
assert_equal expected_xml, hash.to_xml(@xml_options)
end
def test_unescaping_from_xml
xml_string = '<person><bare-string>First &amp; Last Name</bare-string><pre-escaped-string>First &amp;amp; Last Name</pre-escaped-string></person>'
expected_hash = {
:bare_string => 'First & Last Name',
expected_hash = {
:bare_string => 'First & Last Name',
:pre_escaped_string => 'First &amp; Last Name'
}.stringify_keys
assert_equal expected_hash, Hash.from_xml(xml_string)['person']
end
def test_roundtrip_to_xml_from_xml
hash = {
:bare_string => 'First & Last Name',
hash = {
:bare_string => 'First & Last Name',
:pre_escaped_string => 'First &amp; Last Name'
}.stringify_keys
assert_equal hash, Hash.from_xml(hash.to_xml(@xml_options))['person']
end
def test_to_xml_dups_options
options = {:skip_instruct => true}
{}.to_xml(options)
@@ -916,7 +916,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert alert_at.utc?
assert_equal Time.utc(2008, 2, 10, 15, 30, 45), alert_at
end
def test_datetime_xml_type_with_non_utc_time
alert_xml = <<-XML
<alert>
@@ -927,7 +927,7 @@ class HashToXmlTest < Test::Unit::TestCase
assert alert_at.utc?
assert_equal Time.utc(2008, 2, 10, 15, 30, 45), alert_at
end
def test_datetime_xml_type_with_far_future_date
alert_xml = <<-XML
<alert>

View File

@@ -1,3 +1,4 @@
require 'abstract_unit'
require 'active_support'
require 'test/unit'

View File

@@ -4,12 +4,13 @@ class MemoizableTest < Test::Unit::TestCase
class Person
extend ActiveSupport::Memoizable
attr_reader :name_calls, :age_calls, :is_developer_calls
attr_reader :name_calls, :age_calls, :is_developer_calls, :name_query_calls
def initialize
@name_calls = 0
@age_calls = 0
@is_developer_calls = 0
@name_query_calls = 0
end
def name
@@ -18,6 +19,7 @@ class MemoizableTest < Test::Unit::TestCase
end
def name?
@name_query_calls += 1
true
end
memoize :name?
@@ -123,6 +125,13 @@ class MemoizableTest < Test::Unit::TestCase
end
end
def test_memoization_flush_with_punctuation
assert_equal true, @person.name?
@person.flush_cache(:name?)
3.times { assert_equal true, @person.name? }
assert_equal 2, @person.name_query_calls
end
def test_memoization_with_nil_value
assert_equal nil, @person.age
assert_equal 1, @person.age_calls
@@ -131,13 +140,7 @@ class MemoizableTest < Test::Unit::TestCase
assert_equal 1, @person.age_calls
end
def test_memorized_results_are_immutable
assert_equal "Josh", @person.name
assert_raise(ActiveSupport::FrozenObjectError) { @person.name.gsub!("Josh", "Gosh") }
end
def test_reloadable
counter = @calculator.counter
assert_equal 1, @calculator.counter
assert_equal 2, @calculator.counter(:reload)
assert_equal 2, @calculator.counter

View File

@@ -45,7 +45,7 @@ def find_cmd(*commands)
end
case config["adapter"]
when /^mysql/
when /mysql/
args = {
'host' => '--host',
'port' => '--port',

View File

@@ -53,7 +53,7 @@ namespace :db do
end
rescue
case config['adapter']
when /^mysql/
when /mysql/
@charset = ENV['CHARSET'] || 'utf8'
@collation = ENV['COLLATION'] || 'utf8_unicode_ci'
begin
@@ -159,7 +159,7 @@ namespace :db do
task :charset => :environment do
config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
case config['adapter']
when /^mysql/
when /mysql/
ActiveRecord::Base.establish_connection(config)
puts ActiveRecord::Base.connection.charset
when 'postgresql'
@@ -174,7 +174,7 @@ namespace :db do
task :collation => :environment do
config = ActiveRecord::Base.configurations[RAILS_ENV || 'development']
case config['adapter']
when /^mysql/
when /mysql/
ActiveRecord::Base.establish_connection(config)
puts ActiveRecord::Base.connection.collation
else
@@ -274,7 +274,7 @@ namespace :db do
task :dump => :environment do
abcs = ActiveRecord::Base.configurations
case abcs[RAILS_ENV]["adapter"]
when /^mysql/, "oci", "oracle"
when /mysql/, "oci", "oracle"
ActiveRecord::Base.establish_connection(abcs[RAILS_ENV])
File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump }
when "postgresql"
@@ -320,7 +320,7 @@ namespace :db do
task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do
abcs = ActiveRecord::Base.configurations
case abcs["test"]["adapter"]
when /^mysql/
when /mysql/
ActiveRecord::Base.establish_connection(:test)
ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0')
IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table|
@@ -354,7 +354,7 @@ namespace :db do
task :purge => :environment do
abcs = ActiveRecord::Base.configurations
case abcs["test"]["adapter"]
when /^mysql/
when /mysql/
ActiveRecord::Base.establish_connection(:test)
ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"], abcs["test"])
when "postgresql"
@@ -408,7 +408,7 @@ end
def drop_database(config)
begin
case config['adapter']
when /^mysql/
when /mysql/
ActiveRecord::Base.establish_connection(config)
ActiveRecord::Base.connection.drop_database config['database']
when /^sqlite/

View File

@@ -3,5 +3,4 @@
set -x
set -e
script/cibuild-on 1.9.3-p231-tcs-github
script/cibuild-on 2.1.0-github