humanized 0.0.1.alpha → 0.0.1
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/lib/humanized.rb +23 -3
- data/lib/humanized/compiler.rb +36 -19
- data/lib/humanized/core_ext/array.rb +1 -1
- data/lib/humanized/core_ext/hash.rb +6 -2
- data/lib/humanized/core_ext/module.rb +2 -2
- data/lib/humanized/core_ext/nilclass.rb +1 -1
- data/lib/humanized/core_ext/object.rb +7 -1
- data/lib/humanized/core_ext/string.rb +1 -1
- data/lib/humanized/core_ext/symbol.rb +1 -1
- data/lib/humanized/humanizer.rb +188 -44
- data/lib/humanized/interpolater.rb +71 -0
- data/lib/humanized/interpolation/conjunctions.rb +6 -0
- data/lib/humanized/interpolation/date.rb +11 -3
- data/lib/humanized/{ref.rb → interpolation/default.rb} +9 -9
- data/lib/humanized/interpolation/german.rb +8 -6
- data/lib/humanized/interpolation/kng.rb +15 -7
- data/lib/humanized/interpolation/number.rb +69 -8
- data/lib/humanized/{scope.rb → query.rb} +77 -49
- data/lib/humanized/source.rb +37 -54
- data/lib/more/humanized/parser.rb +89 -0
- data/lib/more/humanized/parser/date.rb +0 -0
- data/lib/more/humanized/parser/numeric.rb +80 -0
- data/lib/more/humanized/parsing_humanizer.rb +73 -0
- metadata +59 -93
- data/lib/humanized/core_ext/date.rb +0 -21
- data/lib/humanized/core_ext/numeric.rb +0 -21
- data/lib/humanized/core_ext/time.rb +0 -21
data/lib/humanized.rb
CHANGED
@@ -14,7 +14,7 @@
|
|
14
14
|
#
|
15
15
|
# (c) 2011 by Hannes Georg
|
16
16
|
#
|
17
|
-
|
17
|
+
require 'logger'
|
18
18
|
require "facets/module/home.rb"
|
19
19
|
require "facets/module/basename.rb"
|
20
20
|
require "facets/module/anonymous.rb"
|
@@ -24,6 +24,27 @@ require "facets/module/alias_method_chain.rb"
|
|
24
24
|
# readable output.
|
25
25
|
module Humanized
|
26
26
|
|
27
|
+
class FailedInterpolation < String
|
28
|
+
|
29
|
+
ERROR_STRING = '[error]'.freeze
|
30
|
+
|
31
|
+
attr_reader :exception, :string, :variables
|
32
|
+
|
33
|
+
def initialize(exception, string, variables)
|
34
|
+
@exception = exception
|
35
|
+
@string = string
|
36
|
+
@variables = variables
|
37
|
+
super(ERROR_STRING)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
attr_accessor :logger
|
44
|
+
end
|
45
|
+
|
46
|
+
self.logger = Logger.new(STDERR)
|
47
|
+
|
27
48
|
module HasNaturalGenus
|
28
49
|
|
29
50
|
def self.included(base)
|
@@ -44,9 +65,8 @@ module Humanized
|
|
44
65
|
end
|
45
66
|
|
46
67
|
end
|
47
|
-
require "humanized/ref"
|
48
68
|
require "humanized/humanizer"
|
49
|
-
require "humanized/
|
69
|
+
require "humanized/query"
|
50
70
|
Dir[File.expand_path('humanized/core_ext/*.rb', File.dirname(__FILE__))].each do |file|
|
51
71
|
require file
|
52
72
|
end
|
data/lib/humanized/compiler.rb
CHANGED
@@ -16,28 +16,30 @@
|
|
16
16
|
#
|
17
17
|
|
18
18
|
module Humanized
|
19
|
-
|
20
|
-
|
21
|
-
class Compiled < Proc
|
22
|
-
|
23
|
-
attr_accessor :str
|
24
|
-
|
25
|
-
def initialize(str)
|
26
|
-
@str = str
|
27
|
-
super()
|
28
|
-
return self
|
29
|
-
end
|
19
|
+
|
20
|
+
class Compiled < Proc
|
30
21
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
attr_accessor :str
|
23
|
+
|
24
|
+
def initialize(str)
|
25
|
+
@str = str
|
26
|
+
super()
|
27
|
+
return self
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@str
|
35
32
|
end
|
33
|
+
|
34
|
+
end
|
36
35
|
|
36
|
+
class CompilerBase
|
37
|
+
|
37
38
|
def initialize
|
38
39
|
@compiled = Hash.new{|hsh,x| hsh[x] = compile!(x)}
|
39
40
|
end
|
40
41
|
|
42
|
+
|
41
43
|
# Compiles a String into a Proc
|
42
44
|
# @param [String] str A formated String
|
43
45
|
# @return [Compiled] A Proc, which will handle interpolation.
|
@@ -45,6 +47,16 @@ class Compiler
|
|
45
47
|
def compile(str)
|
46
48
|
@compiled[str]
|
47
49
|
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
def compile!(str)
|
53
|
+
raise NoMethodError, "Please implement a compile!-method."
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
class Compiler < CompilerBase
|
59
|
+
|
48
60
|
protected
|
49
61
|
|
50
62
|
VAR_REGEXP = /^%([a-z_]+)/
|
@@ -53,18 +65,23 @@ protected
|
|
53
65
|
|
54
66
|
TRANSFORMER = lambda{|token|
|
55
67
|
if token.kind_of? Array
|
56
|
-
"[#{token.map(&TRANSFORMER).join(',')}].join()"
|
68
|
+
"[#{token.map(&TRANSFORMER).join(',')}].map(&:to_s).join()"
|
57
69
|
elsif token.kind_of? String
|
58
70
|
token.inspect
|
59
71
|
elsif token.kind_of? Symbol
|
60
72
|
"variables[#{token.inspect}]"
|
61
73
|
elsif token.kind_of? Hash
|
62
|
-
|
74
|
+
if token[:args].any?
|
75
|
+
"o.#{token[:method]}(humanizer,#{token[:args].map(&TRANSFORMER).join(',')})"
|
76
|
+
else
|
77
|
+
"o.#{token[:method]}(humanizer)"
|
78
|
+
end
|
63
79
|
end
|
64
80
|
}
|
65
81
|
|
66
82
|
def compile!(str)
|
67
|
-
|
83
|
+
str = str.dup.freeze
|
84
|
+
return eval('Compiled.new(str) do |humanizer,variables| o = humanizer.interpolater.object ;' + TRANSFORMER.call(read(str)) +' end')
|
68
85
|
end
|
69
86
|
|
70
87
|
def read(str)
|
@@ -124,4 +141,4 @@ protected
|
|
124
141
|
|
125
142
|
end
|
126
143
|
|
127
|
-
end
|
144
|
+
end
|
@@ -16,10 +16,14 @@
|
|
16
16
|
#
|
17
17
|
class Hash
|
18
18
|
def _(*args,&block)
|
19
|
-
if
|
20
|
-
Humanized::
|
19
|
+
if humanized_variables?
|
20
|
+
Humanized::Query::Root._(*args,&block).with_variables(self)
|
21
21
|
else
|
22
22
|
super
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
def humanized_variables?
|
27
|
+
self.class == Hash
|
28
|
+
end
|
25
29
|
end
|
@@ -16,7 +16,7 @@
|
|
16
16
|
#
|
17
17
|
class Module
|
18
18
|
|
19
|
-
# Generates a {Humanized::
|
19
|
+
# Generates a {Humanized::Query query} for a Module or Class. This will be used by default by
|
20
20
|
# this Module and by all Objects of this Class.
|
21
21
|
def humanization_key!
|
22
22
|
if self.anonymous?
|
@@ -26,7 +26,7 @@ class Module
|
|
26
26
|
if h != Object and h.respond_to? :humanization_key
|
27
27
|
result = h.humanization_key + self.basename.downcase.to_sym
|
28
28
|
else
|
29
|
-
result = Humanized::
|
29
|
+
result = Humanized::Query::Root.+(*self.name.split('::').map{|s| s.downcase.to_sym })
|
30
30
|
end
|
31
31
|
thiz = self
|
32
32
|
if defined? thiz.superclass and self.superclass != Object
|
@@ -14,9 +14,15 @@
|
|
14
14
|
#
|
15
15
|
# (c) 2011 by Hannes Georg
|
16
16
|
#
|
17
|
+
require 'facets/object/dup'
|
17
18
|
class Object
|
18
19
|
def humanization_key
|
19
|
-
self.
|
20
|
+
if self.frozen? or !self.dup?
|
21
|
+
i = self
|
22
|
+
else
|
23
|
+
i = self.dup.freeze
|
24
|
+
end
|
25
|
+
self.class.humanization_key.optionally(:instance).with_variables({:self => i })
|
20
26
|
end
|
21
27
|
def _(*args,&block)
|
22
28
|
self.humanization_key._(*args,&block)
|
data/lib/humanized/humanizer.rb
CHANGED
@@ -17,9 +17,11 @@
|
|
17
17
|
require 'facets/array/extract_options.rb'
|
18
18
|
require 'facets/hash/deep_merge.rb'
|
19
19
|
require 'sync'
|
20
|
+
require 'logger'
|
20
21
|
require 'set'
|
21
22
|
require 'humanized/compiler.rb'
|
22
23
|
require 'humanized/source.rb'
|
24
|
+
require 'humanized/interpolater.rb'
|
23
25
|
module Humanized
|
24
26
|
# A Humanizer has one simple task: <b>create strings in its language/dialect/locale/whatever!</b>
|
25
27
|
#
|
@@ -31,63 +33,211 @@ module Humanized
|
|
31
33
|
# The most important method you may use is {#[]}.
|
32
34
|
class Humanizer
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
IS_STRING = lambda{|x| x.kind_of? String }
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Defines a component on this class and all subclasses.
|
43
|
+
def component( name, options = {}, &initializer )
|
44
|
+
@components ||= {}
|
45
|
+
options = options.dup
|
46
|
+
if !block_given?
|
47
|
+
initializer = lambda{|value, old| value}
|
48
|
+
end
|
49
|
+
options[:initializer] = initializer
|
50
|
+
options.freeze
|
51
|
+
@components[name.to_sym] = options
|
52
|
+
attr_accessor name
|
53
|
+
public name.to_sym
|
54
|
+
protected "#{name.to_s}=".to_sym
|
55
|
+
if options[:delegate]
|
56
|
+
if options[:delegate].kind_of? Hash
|
57
|
+
options[:delegate].each do | from, to |
|
58
|
+
module_eval <<RB
|
59
|
+
def #{from.to_s}(*args, &block)
|
60
|
+
#{name.to_s}.#{to.to_s}(*args,&block)
|
61
|
+
end
|
62
|
+
RB
|
63
|
+
end
|
64
|
+
elsif options[:delegate].respond_to? :each
|
65
|
+
options[:delegate].each do | from |
|
66
|
+
module_eval <<RB
|
67
|
+
def #{from.to_s}(*args, &block)
|
68
|
+
#{name.to_s}.#{from.to_s}(*args,&block)
|
69
|
+
end
|
70
|
+
RB
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
# TODO: iterater superclass combinations
|
76
|
+
if @component_combinations
|
77
|
+
cc = @component_combinations
|
78
|
+
@component_combinations = []
|
79
|
+
cc.each do | rest, block |
|
80
|
+
when_combining_components(rest, block)
|
81
|
+
end
|
82
|
+
end
|
38
83
|
end
|
39
84
|
|
40
|
-
|
85
|
+
def when_combining_components(*args,&block)
|
86
|
+
rest = args.map( &:to_sym ).uniq - self.components.to_a
|
87
|
+
if rest.none?
|
88
|
+
block.call( self )
|
89
|
+
else
|
90
|
+
@component_combinations = [] unless @component_combinations
|
91
|
+
@component_combinations << [ rest, block ]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
public
|
96
|
+
def each_component
|
97
|
+
klass = self
|
98
|
+
components = Set.new
|
99
|
+
while( klass )
|
100
|
+
a = klass.instance_variable_get("@components")
|
101
|
+
if a
|
102
|
+
a.each do |name, options|
|
103
|
+
unless components.include?(name)
|
104
|
+
yield(name, options) if block_given?
|
105
|
+
components << name
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
klass = klass.superclass
|
110
|
+
end
|
111
|
+
return components
|
112
|
+
end
|
41
113
|
|
114
|
+
alias_method :components, :each_component
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
component :interpolater do |value, old|
|
119
|
+
value || Interpolater.new
|
120
|
+
end
|
121
|
+
|
122
|
+
component :source, :delegate =>[:package, :load , :<<, :get ] do |value, old|
|
123
|
+
if value.kind_of? Source
|
124
|
+
value
|
125
|
+
elsif value.kind_of? Hash
|
126
|
+
Source.new(value)
|
127
|
+
elsif value.nil?
|
128
|
+
Source.new
|
129
|
+
else
|
130
|
+
raise ArgumentError, "Expected :source to be a kind of Humanized::Source, Hash or nil."
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
component :compiler do |value, old|
|
135
|
+
value || Compiler.new
|
42
136
|
end
|
43
137
|
|
44
|
-
|
138
|
+
component :logger do |value, old|
|
139
|
+
if value.kind_of? Logger
|
140
|
+
value
|
141
|
+
elsif value.respond_to? :write and value.respond_to? :close
|
142
|
+
Logger.new(value)
|
143
|
+
elsif value.nil?
|
144
|
+
Humanized.logger
|
145
|
+
elsif value.kind_of? FalseClass
|
146
|
+
value
|
147
|
+
else
|
148
|
+
raise ArgumentError, "Expected :logger to be a kind of Logger, IO, nil or false."
|
149
|
+
end
|
150
|
+
end
|
45
151
|
|
46
152
|
# Creates a new Humanizer
|
47
153
|
#
|
48
154
|
# @option components [Object] :interpolater This object which has all interpolation methods defined as public methods.
|
49
155
|
# @option components [Compiler] :compiler A compiler which can compile strings into procs. (see Compiler)
|
50
156
|
# @option components [Source] :source A source which stores translated strings. (see Source)
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
157
|
+
# @option components [Logger, IO, false] :logger A logger for this Humanizer or false to disable logging.
|
158
|
+
def initialize(*args)
|
159
|
+
|
160
|
+
components = args.last.kind_of?(Hash) ? args.pop : {}
|
161
|
+
|
162
|
+
used_keys = Set.new
|
163
|
+
|
164
|
+
args.each do | mod |
|
165
|
+
if mod.kind_of? Module
|
166
|
+
extend mod
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if args.first.kind_of? Humanized::Humanizer
|
171
|
+
|
172
|
+
humanizer = args.first
|
173
|
+
|
174
|
+
modules = (class << humanizer; included_modules ; end ) - self.class.included_modules
|
175
|
+
|
176
|
+
modules.each do | mod |
|
177
|
+
|
178
|
+
extend mod
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
(class << self ; self ; end).each_component do |name, options|
|
183
|
+
if humanizer.respond_to? name
|
184
|
+
if components.key? name
|
185
|
+
self.send("#{name}=".to_sym, options[:initializer].call(components[name],humanizer.respond_to?(name) ? humanizer.send(name) : nil ))
|
186
|
+
else
|
187
|
+
self.send("#{name}=".to_sym, humanizer.send(name))
|
188
|
+
end
|
189
|
+
else
|
190
|
+
self.send("#{name}=".to_sym, options[:initializer].call(components[name],nil))
|
191
|
+
end
|
192
|
+
used_keys << name
|
193
|
+
end
|
194
|
+
else
|
195
|
+
|
196
|
+
(class << self ; self ; end).each_component do |name, options|
|
197
|
+
self.send("#{name}=".to_sym, options[:initializer].call(components[name], nil))
|
198
|
+
used_keys << name
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
|
203
|
+
components.each do | k, v |
|
204
|
+
warn "Unused key #{k.inspect} with value #{v.inspect} in Humanizer.initialize" unless used_keys.include? k
|
205
|
+
end
|
206
|
+
|
207
|
+
|
55
208
|
end
|
56
209
|
|
210
|
+
|
57
211
|
# Creates a new Humanizer which uses the interpolater, compiler and source of this Humanizer unless other values for them were specified.
|
58
212
|
# @see #initialize
|
59
|
-
def
|
60
|
-
self.class.new(
|
213
|
+
def new(components)
|
214
|
+
self.class.new(self, components)
|
61
215
|
end
|
62
216
|
|
63
217
|
# Creates a String from the input. This will be the most used method in application code.
|
64
|
-
# It expects a {
|
218
|
+
# It expects a {Query} as argument. Anything that is not a {Query} will be converted into a {Query} using the "_"-method.
|
65
219
|
# This enables you to pass any object to this method. The result is mainly determined by result of the "_"-method.
|
66
220
|
# For
|
67
221
|
#
|
68
|
-
# @param [
|
222
|
+
# @param [Query, #_, Object] *args
|
69
223
|
# @return [String]
|
70
224
|
def [](*args)
|
71
225
|
it = args._
|
72
226
|
|
73
227
|
vars = it.variables
|
74
228
|
default = it.default
|
75
|
-
result = @source.get(it, default)
|
229
|
+
result = @source.get(it, :default=>default, :accepts=>IS_STRING)
|
76
230
|
result = default unless result.kind_of? String
|
77
231
|
if result.kind_of? String
|
78
232
|
return interpolate(result,vars)
|
79
|
-
|
80
|
-
|
233
|
+
else
|
234
|
+
if logger
|
235
|
+
logger.error do
|
236
|
+
"Expected to retrieve a String, but got: #{result.inspect}\n\tQuery: #{it.inspect}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
return ""
|
81
240
|
end
|
82
|
-
return result
|
83
|
-
end
|
84
|
-
|
85
|
-
# This is a wrapper for @source.get.
|
86
|
-
# The only thing it does additionally is converting all params into a Scope.
|
87
|
-
# @see Source#get
|
88
|
-
def get(base,*rest)
|
89
|
-
it = base._(*rest)
|
90
|
-
return @source.get(it, it.default)
|
91
241
|
end
|
92
242
|
|
93
243
|
# Stores a translation
|
@@ -96,26 +246,20 @@ class Humanizer
|
|
96
246
|
@source.store(it._(*rest).first,last)
|
97
247
|
end
|
98
248
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
# This is an alias for @source.load
|
106
|
-
# @see Source#load
|
107
|
-
def load(*args,&block)
|
108
|
-
@source.load(*args,&block)
|
109
|
-
end
|
110
|
-
|
111
|
-
# This is an alias for @source.<<
|
112
|
-
# @see Source#<<
|
113
|
-
def <<(x)
|
114
|
-
@source << x
|
249
|
+
def interpolate(str,vars={})
|
250
|
+
return @compiler.compile(str).call(self,vars)
|
251
|
+
rescue Exception => e
|
252
|
+
return handle_interpolation_exception(e, str, vars)
|
115
253
|
end
|
116
254
|
|
117
|
-
|
118
|
-
|
255
|
+
protected
|
256
|
+
def handle_interpolation_exception(e, str, vars)
|
257
|
+
if logger
|
258
|
+
logger.error do
|
259
|
+
"Failed interpolating \"#{str}\"\n\tVariables: #{vars.inspect}\n\tMessage: #{e.message}\n\tTrace:\t" + e.backtrace.join("\n\t\t")
|
260
|
+
end
|
261
|
+
end
|
262
|
+
return FailedInterpolation.new(e, str, vars)
|
119
263
|
end
|
120
264
|
|
121
265
|
end
|