merb_global 0.0.4.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/HISTORY CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.0.5
2
+ * Handles lang_REGION as lang
3
+ * Renamed providers into message providers and translate_to into localize
4
+ * Added support for localization of dates and numbers
5
+ * Updated specs
6
+
1
7
  === 0.0.4
2
8
  * Changed database format (to allow importing/exporting)
3
9
  * Added importing/exporting (EXPERIMENTAL)
data/Rakefile CHANGED
@@ -2,10 +2,11 @@ require 'rubygems'
2
2
  require 'rake/gempackagetask'
3
3
  require 'rake/rdoctask'
4
4
  require 'spec/rake/spectask'
5
+ require 'fileutils'
5
6
 
6
7
  PLUGIN = "merb_global"
7
8
  NAME = "merb_global"
8
- GEM_VERSION = "0.0.4.2"
9
+ GEM_VERSION = "0.0.5"
9
10
  AUTHORS = ["Alex Coles", "Maciej Piechotka"]
10
11
  EMAIL = "alex@alexcolesportfolio.com"
11
12
  HOMEPAGE = "http://trac.ikonoklastik.com/merb_global/"
@@ -20,6 +21,7 @@ spec = Gem::Specification.new do |s|
20
21
  s.authors = AUTHORS
21
22
  s.email = EMAIL
22
23
  s.homepage = HOMEPAGE
24
+ s.rubyforge_project = 'merb-global'
23
25
  s.add_dependency('merb-core', '>= 0.9.1')
24
26
  s.add_dependency('treetop', '>= 1.2.3') # Tested on 1.2.3
25
27
  s.require_path = 'lib'
@@ -50,6 +52,17 @@ Rake::RDocTask.new do |rd|
50
52
  rd.rdoc_files.include "lib/**/*.rb"
51
53
  end
52
54
 
55
+ desc "Creates database for examples"
56
+ task :populate_db do
57
+ pwd = File.dirname __FILE__
58
+ db = "#{pwd}/examples/database.db"
59
+ sh %{sqlite3 #{db} < #{pwd}/examples/database.sql}
60
+ FileUtils.cp db, "#{pwd}/examples/active_record_example/database.db"
61
+ FileUtils.cp db, "#{pwd}/examples/data_mapper_example/database.db"
62
+ FileUtils.cp db, "#{pwd}/examples/sequel_example/database.db"
63
+ end
64
+ task :gem => :populate_db
65
+
53
66
  desc "Run all specs"
54
67
  Spec::Rake::SpecTask.new('specs') do |st|
55
68
  st.libs = ['lib', 'spec']
@@ -1,5 +1,5 @@
1
1
  #<% if false %>
2
- class Merb::Global::Providers::ActiveRecord
2
+ class Merb::Global::MessageProviders::ActiveRecord
3
3
  #<% end %>
4
4
  class AddTranslationsMigration < ActiveRecord::Migration
5
5
  def self.up
@@ -1,34 +1,47 @@
1
1
  require 'merb_global/config'
2
2
  require 'merb_global/plural'
3
- require 'merb_global/provider'
4
- require 'merb_global/providers'
5
- require 'merb_global/transfer'
3
+ require 'merb_global/date_providers'
4
+ require 'merb_global/message_providers'
5
+ require 'merb_global/numeric_providers'
6
6
 
7
7
  module Merb
8
8
  module Global
9
- attr_accessor :lang, :provider
9
+ attr_accessor :lang, :message_provider, :date_provider, :numeric_provider
10
10
 
11
11
  def lang #:nodoc:
12
12
  @lang ||= 'en'
13
13
  end
14
14
 
15
- def provider #:nodoc:
16
- @provider ||= Merb::Global::Providers.provider
15
+ def message_provider #:nodoc:
16
+ @message_provider ||= Merb::Global::MessageProviders.provider
17
+ end
18
+
19
+ def date_provider #:nodoc:
20
+ @date_provider ||= Merb::Global::DateProviders.provider
21
+ end
22
+
23
+ def numeric_provider #:nodoc:
24
+ @numeric_provider ||= Merb::Global::NumericProviders.provider
17
25
  end
18
26
 
19
27
  # call-seq:
20
- # _(singular, opts) => translated
21
- # _(singlular, plural, opts) => translated
28
+ # _(singular, opts) => translated message
29
+ # _(singlular, plural, opts) => translated message
30
+ # _(date, format) => localized date
31
+ # _(number) => localized number
22
32
  #
23
33
  # Translate a string.
24
34
  # ==== Parameters
25
35
  # singular<String>:: A string to translate
26
36
  # plural<String>:: A plural form of string
27
37
  # opts<Hash>:: An options hash (see below)
38
+ # date<~strftime>:: A date to localize
39
+ # format<String>:: A format of string (should be compatibile with strftime)
40
+ # number<Numeric>:: A numeber to localize
28
41
  #
29
42
  # ==== Options (opts)
30
43
  # :lang<String>:: A language to translate on
31
- # :n<Fixnum>:: A number of objects
44
+ # :n<Fixnum>:: A number of objects (for messages)
32
45
  #
33
46
  # ==== Returns
34
47
  # translated<String>:: A translated string
@@ -38,12 +51,30 @@ module Merb
38
51
  def _(*args)
39
52
  opts = {:lang => self.lang, :n => 1}
40
53
  opts.merge! args.pop if args.last.is_a? Hash
41
- if args.size == 1
42
- self.provider.translate_to args[0], nil, opts
43
- elsif args.size == 2
44
- self.provider.translate_to args[0], args[1], opts
54
+ if args.first.respond_to? :strftime
55
+ if args.size == 2
56
+ self.date_provider.localize opts[:lang], args[0], args[1]
57
+ else
58
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
59
+ end
60
+ elsif args.first.is_a? Numeric
61
+ if args.size == 1
62
+ self.numeric_provider.localize opts[:lang], args.first
63
+ else
64
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 1)"
65
+ end
66
+ elsif args.first.is_a? String
67
+ if args.size == 1
68
+ self.message_provider.localize args[0], nil, opts
69
+ elsif args.size == 2
70
+ self.message_provider.localize args[0], args[1], opts
71
+ else
72
+ raise ArgumentError,
73
+ "wrong number of arguments (#{args.size} for 1-2)"
74
+ end
45
75
  else
46
- raise ArgumentError, "wrong number of arguments (#{args.size} for 1-2)"
76
+ raise ArgumentError,
77
+ "wrong type of arguments - see documentation for details"
47
78
  end
48
79
  end
49
80
  end
@@ -1,7 +1,7 @@
1
1
  require 'merb_global/base'
2
2
 
3
3
  module Merb
4
- class Controller
4
+ class Controller
5
5
  include Merb::Global
6
6
  class_inheritable_accessor :_language
7
7
 
@@ -9,7 +9,7 @@ module Merb
9
9
  # Set up the language
10
10
  accept_language = self.request.env['HTTP_ACCEPT_LANGUAGE']
11
11
  self.lang = params[:language] ||
12
- (self._language && self._language.call) ||
12
+ (self._language && self.instance_eval(&self._language)) ||
13
13
  begin
14
14
  unless accept_language.nil?
15
15
  accept_language = accept_language.split(',')
@@ -25,15 +25,30 @@ module Merb
25
25
  end
26
26
  accept_language.sort! {|lang_a, lang_b| lang_a[1] <=> lang_b[1]}
27
27
  accept_language.collect! {|lang| lang[0]}
28
- accept_language.reject! do |lang|
29
- lang != '*' and not self.provider.support? lang
28
+ accept_language_orig = accept_language.dup
29
+ accept_language.collect! do |lang|
30
+ if lang == '*'
31
+ '*'
32
+ elsif self.message_provider.support? lang
33
+ lang
34
+ elsif lang.include? '-'
35
+ lang = lang.split('-')[0]
36
+ if self.message_provider.support? lang
37
+ lang
38
+ else
39
+ nil
40
+ end
41
+ else
42
+ nil
43
+ end
30
44
  end
45
+ accept_language.reject! {|lang| lang.nil?}
31
46
  unless accept_language.empty?
32
47
  unless accept_language.last == '*'
33
48
  accept_language.last
34
49
  else
35
50
  accept_language.pop
36
- self.provider.choose accept_language
51
+ self.message_provider.choose accept_language
37
52
  end
38
53
  end
39
54
  end
@@ -0,0 +1,35 @@
1
+ require 'inline'
2
+
3
+ module Merb
4
+ module Global
5
+ module DateProviders
6
+ class Fork
7
+ include Merb::Global::DateProviders::Base
8
+
9
+ def localize(lang, date, format)
10
+ pipe_rd, pipe_wr = IO.pipe
11
+ pid = fork do
12
+ pipe_rd.close
13
+ setlocale(lang)
14
+ pipe_wr.write(date.strftime(format))
15
+ pipe_wr.flush
16
+ end
17
+ pipe_wr.close
18
+ Process.wait(pid)
19
+ pipe_rd.read
20
+ end
21
+
22
+ inline do |builder|
23
+ builder.include '<locale.h>'
24
+ builder.c <<C
25
+ void set_locale(const char *locale)
26
+ {
27
+ setlocale(LC_ALL, locale);
28
+ }
29
+ C
30
+ end
31
+ private :set_locale
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ require 'merb_global/providers'
2
+
3
+ module Merb
4
+ module Global
5
+ module DateProviders
6
+ include Providers
7
+ # call-seq:
8
+ # provider => provider
9
+ #
10
+ # Returns the provider of required type
11
+ #
12
+ # ==== Returns
13
+ # provider<Provider>:: Returns provider
14
+ def self.provider
15
+ @@provider ||= self[Merb::Global.config(:date_provider, 'fork')]
16
+ end
17
+ # Merb-global is able to handle localization in different ways.
18
+ # Providers are the interface.
19
+ #
20
+ # Please note that it is not required to include this module - despite it
21
+ # is recomended both as a documentation part and the more customized
22
+ # error messages.
23
+ module Base
24
+ ##
25
+ #
26
+ # Localize date using format as in strftime
27
+ def localize(lang, date, format)
28
+ raise NoMethodError.new('method localize has not been implemented')
29
+ end
30
+ end
31
+ end
32
+ # Perform the registration
33
+ #
34
+ # ==== Parameters
35
+ # name<~to_sym>:: Name under which it is registred
36
+ def self.DateProvider(provider_name)
37
+ Module.new do
38
+ @@rb_date_provider_name = provider_name
39
+ include Merb::Global::DateProviders::Base
40
+
41
+ def self.included(klass)
42
+ Merb::Global::DateProviders.register @@rb_date_provider_name, klass
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,25 +1,25 @@
1
1
  require 'fileutils'
2
2
 
3
3
  namespace :merb_global do
4
-
4
+
5
5
  task :merb_start do
6
6
  Merb.start_environment :adapter => 'runner',
7
7
  :environment => ENV['MERB_ENV'] || 'development'
8
8
  end
9
-
9
+
10
10
  desc 'Create migration'
11
11
  task :migration => :merb_start do
12
- Merb::Global::Providers.provider.create!
12
+ Merb::Global::MessageProviders.provider.create!
13
13
  end
14
-
14
+
15
15
  desc 'Transfer the translations from one provider to another'
16
16
  task :transfer => :merb_start do
17
17
  from = Merb::Global.config 'source', 'gettext'
18
18
  into = Merb::Global.config 'provider', 'gettext'
19
19
  if from == 'gettext' and into == 'gettext'
20
- Dir[Merb::Global::Providers.localedir + '/*.po'].each do |file|
20
+ Dir[Merb::Global::MessageProviders.localedir + '/*.po'].each do |file|
21
21
  lang = File.basename file, '.po'
22
- lang_dir = File.join(Merb::Global::Providers.localedir,
22
+ lang_dir = File.join(Merb::Global::MessageProviders.localedir,
23
23
  lang, 'LC_MESSAGES')
24
24
  FileUtils.mkdir_p lang_dir
25
25
  domain = Merb::Global.config([:gettext, :domain], 'merbapp')
@@ -28,10 +28,10 @@ namespace :merb_global do
28
28
  elsif from == into
29
29
  Merb.logger.error 'Tried transfer from and into the same provider'
30
30
  else
31
- from = Merb::Global::Providers[from]
32
- into = Merb::Global::Providers[into]
31
+ from = Merb::Global::MessageProviders[from]
32
+ into = Merb::Global::MessageProviders[into]
33
33
  Merb::Global::Provider.transfer from, into
34
34
  end
35
35
  end
36
-
37
- end
36
+
37
+ end
@@ -7,13 +7,13 @@ require 'merb_global/plural'
7
7
 
8
8
  module Merb
9
9
  module Global
10
- module Providers
10
+ module MessageProviders
11
11
  class ActiveRecord #:nodoc: all
12
- include Merb::Global::Provider
13
- include Merb::Global::Provider::Importer
14
- include Merb::Global::Provider::Exporter
15
-
16
- def translate_to(singular, plural, opts)
12
+ include Merb::Global::MessageProviders::Base
13
+ include Merb::Global::MessageProviders::Base::Importer
14
+ include Merb::Global::MessageProviders::Base::Exporter
15
+
16
+ def localize(singular, plural, opts)
17
17
  language = Language.find :first,
18
18
  :conditions => {:name => opts[:lang]}
19
19
  unless language.nil?
@@ -22,7 +22,7 @@ module Merb
22
22
  translation = Translation.find [language.id, singular, n]
23
23
  else
24
24
  translation = Translation.find [language.id, singular, nil]
25
- end
25
+ end
26
26
  return translation.msgstr
27
27
  end rescue nil
28
28
  return opts[:n] > 1 ? plural : singular # Fallback if not in database
@@ -57,54 +57,61 @@ module Merb
57
57
  # "(#{except.join(',')})"])
58
58
  end
59
59
 
60
- def import(exporter, export_data)
60
+ def import
61
+ data = {}
61
62
  Language.transaction do
62
63
  Translation.transaction do
63
- Language.find(:all).each do |language|
64
- exporter.export_language export_data, language.name,
65
- language.nplural,
66
- language.plural do |lang|
67
- language.translations.each do |translation|
68
- exporter.export_string lang, translation.msgid,
69
- translation.msgid_plural,
70
- translation.msgstr_index,
71
- translation.msgstr
72
- end
64
+ Language.find(:all).each do |lang|
65
+ data[lang.name] = lang_hash = {
66
+ :plural => lang.plural,
67
+ :nplural => lang.nplural
68
+ }
69
+ lang.translations.each do |translation|
70
+ lang_hash[translation.msgid] ||= {
71
+ :plural => translation.msgid_plural
72
+ }
73
+ lang_hash[translation.msgid][translation.msgstr_index] =
74
+ translation.msgstr
73
75
  end
74
76
  end
75
77
  end
76
78
  end
79
+ data
77
80
  end
78
81
 
79
- def export
82
+ def export(data)
80
83
  Language.transaction do
81
84
  Translation.transaction do
82
- Language.delete_all
83
85
  Translation.delete_all
84
- yield nil
86
+ Language.delete_all
87
+ data.each do |lang_name, lang|
88
+ lang_id = Language.create!(:name => lang_name,
89
+ :plural => lang[:plural],
90
+ :nplural => lang[:nplural]).id
91
+ lang.each do |msgid, msgstrs|
92
+ if msgid.is_a? String
93
+ plural = msgstrs[:plural]
94
+ msgstrs.each do |index, msgstr|
95
+ if index.nil? or index.is_a? Fixnum
96
+ Translation.create! :language_id => lang_id,
97
+ :msgid => msgid,
98
+ :msgid_plural => plural,
99
+ :msgstr => msgstr,
100
+ :msgstr_index => index
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
85
106
  end
86
107
  end
87
108
  end
88
109
 
89
- def export_language(export_data, language, nplural, plural)
90
- yield Language.create!(:language => language, :nplural => nplural,
91
- :plural => plural).id
92
- end
93
-
94
- def export_string(language_id, msgid, msgid_plural,
95
- msgstr, msgstr_index)
96
- Translation.create! :language_id => language_id,
97
- :msgid => msgid,
98
- :msgid_plural => msgid_plural,
99
- :msgstr => msgstr,
100
- :msgstr_index => msgstr_index
101
- end
102
-
103
110
  class Language < ::ActiveRecord::Base
104
111
  set_table_name :merb_global_languages
105
112
  has_many :translations,
106
113
  :class_name =>
107
- "::Merb::Global::Providers::ActiveRecord::Translation"
114
+ "::Merb::Global::MessageProviders::ActiveRecord::Translation"
108
115
  end
109
116
 
110
117
  class Translation < ::ActiveRecord::Base
@@ -1,16 +1,16 @@
1
- require 'data_mapper'
1
+ require 'dm-core'
2
2
  require 'dm-aggregates'
3
3
  require 'merb_global/plural'
4
4
 
5
5
  module Merb
6
6
  module Global
7
- module Providers
7
+ module MessageProviders
8
8
  class DataMapper #:nodoc: all
9
- include Merb::Global::Provider
10
- include Merb::Global::Provider::Importer
11
- include Merb::Global::Provider::Exporter
12
-
13
- def translate_to(singular, plural, opts)
9
+ include Merb::Global::MessageProviders
10
+ include Merb::Global::MessageProviders::Base::Importer
11
+ include Merb::Global::MessageProviders::Base::Exporter
12
+
13
+ def localize(singular, plural, opts)
14
14
  # I hope it's from MemCache
15
15
  language = Language.first :name => opts[:lang]
16
16
  unless language.nil?
@@ -43,50 +43,54 @@ module Merb
43
43
  Language.first(:name.not => except).name
44
44
  end
45
45
 
46
- def import(exporter, export_data)
46
+ def import
47
+ data = {}
47
48
  ::DataMapper::Transaction.new(Language, Translation) do
48
49
  Language.all.each do |language|
49
- exporter.export_language export_data, language.name,
50
- language.nplural,
51
- language.plural do |lang|
52
- language.translations.each do |translation|
53
- exporter.export_string lang, translation.msgid,
54
- translation.msgid_plural,
55
- translation.msgstr_index,
56
- translation.msgstr
57
- end
50
+ data[language.name] = lang_hash = {
51
+ :plural => language.plural,
52
+ :nplural => language.nplural
53
+ }
54
+ language.translations(:fields => Translation.properties.to_a).
55
+ each do |translation|
56
+ lang_hash[translation.msgid] ||= {
57
+ :plural => translation.msgid_plural
58
+ }
59
+ lang_hash[translation.msgid][translation.msgstr_index] =
60
+ translation.msgstr
58
61
  end
59
62
  end
60
63
  end
64
+ data
61
65
  end
62
66
 
63
- def export
67
+ def export(data)
64
68
  ::DataMapper::Transaction.new(Language, Translation) do
65
- Language.all.each {|language| language.destroy}
66
69
  Translation.all.each {|translation| translation.destroy}
67
- yield nil
70
+ Language.all.each {|language| language.destroy}
71
+ data.each do |lang_name, lang|
72
+ lang_obj = Language.create!(:name => lang_name,
73
+ :plural => lang[:plural],
74
+ :nplural => lang[:nplural])
75
+ lang.each do |msgid, msgstr_hash|
76
+ if msgstr_hash.is_a? Hash
77
+ plural = msgstr_hash[:plural]
78
+ msgstr_hash.each do |msgstr_index, msgstr|
79
+ if msgstr_index.nil? or msgstr_index.is_a? Fixnum
80
+ Translation.create!(:language_id => lang_obj.id,
81
+ :msgid => msgid,
82
+ :msgid_plural => plural,
83
+ :msgstr => msgstr,
84
+ :msgstr_index => msgstr_index) or
85
+ raise
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
68
91
  end
69
92
  end
70
93
 
71
- def export_language(export_data, language, nplural, plural)
72
- lang = Language.new :language => language, :nplural => nplural,
73
- :plural => plural
74
- lang.save
75
- raise if lang.new_record?
76
- yield lang.id
77
- end
78
-
79
- def export_string(language_id, msgid, msgid_plural,
80
- msgstr, msgstr_index)
81
- trans = Translation.new :language_id => language_id,
82
- :msgid => msgid,
83
- :msgid_plural => msgid_plural,
84
- :msgstr => msgstr,
85
- :msgstr_index => msgstr_index
86
- trans.save
87
- raise if lang.new_record?
88
- end
89
-
90
94
  # When table structure becomes stable it *should* be documented
91
95
  class Language
92
96
  include ::DataMapper::Resource
@@ -97,7 +101,7 @@ module Merb
97
101
  property :plural, Text, :lazy => false
98
102
  # validates_is_unique :name
99
103
  has n, :translations,
100
- :class_name => "Merb::Global::Providers::DataMapper::Translation",
104
+ :class_name => "Merb::Global::MessageProviders::DataMapper::Translation",
101
105
  :child_key => [:language_id]
102
106
  end
103
107
 
@@ -105,12 +109,6 @@ module Merb
105
109
  include ::DataMapper::Resource
106
110
  storage_names[:default] = 'merb_global_translations'
107
111
  property :language_id, Integer, :nullable => false, :key => true
108
- # Sould it be propery :msgid, :text?
109
- # This form should be faster. However:
110
- # - collision may appear (despite being unpropable)
111
- # - it may be wrong optimalisation
112
- # As far I'll leave it in this form. If anybody could measure the
113
- # speed of both methods it will be appreciate.
114
112
  property :msgid, Text, :nullable => false, :key => true
115
113
  property :msgid_plural, Text, :lazy => true
116
114
  property :msgstr, Text, :nullable => false, :lazy => false