humanized 0.0.1.alpha → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,71 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # This program is free software: you can redistribute it and/or modify
3
+ # it under the terms of the Affero GNU General Public License as published by
4
+ # the Free Software Foundation, either version 3 of the License, or
5
+ # (at your option) any later version.
6
+ #
7
+ # This program is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
+ # GNU General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU General Public License
13
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
+ #
15
+ # (c) 2011 by Hannes Georg
16
+ #
17
+ module Humanized
18
+ class Interpolater
19
+
20
+ UNCHANGED_METHODS = Set.new([:__send__,:object_id])
21
+ PRIVATE_METHODS = Set.new([:extend,:send,:eval,:instance_exec,:instance_eval, :respond_to? ,:__id__,:method])
22
+
23
+ class LockedDown
24
+ public_instance_methods.each do |meth|
25
+ if UNCHANGED_METHODS.include? meth.to_sym
26
+ next
27
+ elsif PRIVATE_METHODS.include? meth.to_sym
28
+ private meth
29
+ else
30
+ undef_method meth
31
+ end
32
+ end
33
+
34
+ def lock!
35
+ m = method(:send)
36
+ class << self
37
+ undef :lock!
38
+ end
39
+ return m
40
+ end
41
+ end
42
+
43
+ PRIVATE_METHODS.each do |meth|
44
+ class_eval(<<RB)
45
+ alias_method :real_#{meth}, #{meth.inspect}
46
+
47
+ def #{meth.to_s}(*args,&block)
48
+ @masterkey.send(#{meth.inspect},*args,&block)
49
+ end
50
+ RB
51
+ end
52
+
53
+ def initialize
54
+ @object = LockedDown.new
55
+ @masterkey = @object.lock!
56
+ end
57
+
58
+ def <<(mod)
59
+ @masterkey.call(:extend, mod)
60
+ return self
61
+ end
62
+
63
+ def object; @object ; end
64
+
65
+ def inspect
66
+ "#<#{self.class.name}:#{self.object_id.to_s}>"
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -21,12 +21,18 @@ module Conjunctions
21
21
  def and(humanizer, *args)
22
22
  args = args.flatten
23
23
  last = args.pop
24
+ if args.size == 0
25
+ return last.to_s
26
+ end
24
27
  return [args.join(', '), last].join(' '+humanizer[:and]+' ')
25
28
  end
26
29
 
27
30
  def or(humanizer, *args)
28
31
  args = args.flatten
29
32
  last = args.pop
33
+ if args.size == 0
34
+ return last.to_s
35
+ end
30
36
  return [args.join(', '), last].join(' '+humanizer[:or]+' ')
31
37
  end
32
38
 
@@ -19,18 +19,26 @@ module Humanized
19
19
  module Date
20
20
 
21
21
  def date(humanizer, date, format = 'default')
22
- if format == 'default'
22
+ if format == 'default' or format.nil?
23
23
  it = date._(:format,:default)
24
24
  else
25
+ format = format.to_sym
25
26
  it = date._.format( format._ | :default._ )
26
27
  end
27
28
  f = humanizer.get(it)
29
+ if humanizer.respond_to? :calendar
30
+
31
+ return humanizer.calendar.format( f )
32
+
33
+ end
28
34
  if f.kind_of? String
29
35
  return date.strftime( f )
30
36
  end
31
- warn 'Unable to find Date format: #{it.inspect}.'
37
+ if humanizer.logger
38
+ humanizer.logger.error "Unable to find Date format: #{it.inspect}."
39
+ end
32
40
  return ''
33
41
  end
34
42
 
35
43
  end
36
- end
44
+ end
@@ -14,16 +14,16 @@
14
14
  #
15
15
  # (c) 2011 by Hannes Georg
16
16
  #
17
-
17
+ require 'humanized/wrapper'
18
18
  module Humanized
19
+ module Default
19
20
 
20
- # A Reference can be used to redirect lookups for certain paths.
21
- class Ref < Array
22
-
23
- def inspect
24
- '!ref'+super
25
- end
26
-
21
+ def capitalize(humanizer, *str)
22
+ return Humanized::Wrapper.wrap(*str) do |s|
23
+ s[0...1].upcase + s[1..-1]
27
24
  end
28
-
25
+ end
26
+
27
+
28
+ end
29
29
  end
@@ -14,35 +14,37 @@
14
14
  #
15
15
  # (c) 2011 by Hannes Georg
16
16
  #
17
+ require 'humanized'
18
+ require 'humanized/query'
17
19
  require 'humanized/interpolation/kng.rb'
18
20
  module Humanized
19
21
  module German
20
22
 
21
23
  module Articles
22
24
 
23
- ArticleScope = Scope::Meta.articles
25
+ ArticleQuery = Query::Meta.articles
24
26
 
25
27
  def a(humanizer, *args)
26
28
  Wrapper.wrap(args) do |t|
27
- humanizer[ArticleScope.indefinite.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
29
+ humanizer[ArticleQuery.indefinite.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
28
30
  end
29
31
  end
30
32
 
31
33
  def the(humanizer, *args)
32
34
  Wrapper.wrap(args) do |t|
33
- humanizer[ArticleScope.definite.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
35
+ humanizer[ArticleQuery.definite.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
34
36
  end
35
37
  end
36
38
 
37
39
  def some(humanizer, *args)
38
40
  Wrapper.wrap(args) do |t|
39
- humanizer[ArticleScope.partitive.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
41
+ humanizer[ArticleQuery.partitive.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
40
42
  end
41
43
  end
42
44
 
43
45
  def none(humanizer, *args)
44
46
  Wrapper.wrap(args) do |t|
45
- humanizer[ArticleScope.negative.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
47
+ humanizer[ArticleQuery.negative.optionally(x_to_genus(humanizer, t))._(x_to_numerus(humanizer, t), x_to_kasus(humanizer, t))] + ' ' + t.to_s
46
48
  end
47
49
  end
48
50
 
@@ -71,4 +73,4 @@ module German
71
73
  ].freeze
72
74
 
73
75
  end
74
- end
76
+ end
@@ -43,11 +43,17 @@ module KNG
43
43
  end
44
44
 
45
45
  def to_s
46
+ return @kng_humanizer.get(__getobj__._(__generate_paths__))
47
+ end
48
+
49
+ protected
50
+
51
+ def __generate_paths__
52
+ a = Query::Root
46
53
  if @kng_genus
47
- return @kng_humanizer[__getobj__._.optionally(@kng_genus)._(@kng_numerus, @kng_kasus)]
48
- else
49
- return @kng_humanizer[__getobj__._(@kng_numerus, @kng_kasus)]
54
+ a = a.optionally(@kng_genus)
50
55
  end
56
+ return a.optionally(@kng_numerus).optionally(@kng_kasus)
51
57
  end
52
58
 
53
59
  end
@@ -170,9 +176,11 @@ protected
170
176
  return x.kng_kasus
171
177
  end
172
178
  i = x.to_i
173
- c = meta_class.const_get :KASUS
174
- if i > 0 and i <= c.size
175
- return c[i-1].to_sym
179
+ if x.kind_of? Numeric
180
+ c = meta_class.const_get :KASUS
181
+ if i > 0 and i <= c.size
182
+ return c[i-1].to_sym
183
+ end
176
184
  end
177
185
  return abbrev_kasus[x]
178
186
  end
@@ -214,4 +222,4 @@ protected
214
222
 
215
223
  end
216
224
 
217
- end
225
+ end
@@ -15,22 +15,83 @@
15
15
  # (c) 2011 by Hannes Georg
16
16
  #
17
17
 
18
+ require 'facets/numeric/round.rb'
19
+
18
20
  module Humanized
19
21
  module Number
20
22
 
21
- def number(humanizer, number, format = 'default')
22
- if format == 'default'
23
+ class PartitionEnumerator
24
+
25
+ include Enumerable
26
+
27
+ def initialize(range, size)
28
+ @range = range
29
+ @size = size
30
+ end
31
+ def each
32
+ i = @range.first
33
+ size = @range.last - @range.first
34
+ e = @range.end
35
+ if @range.exclude_end?
36
+ e = e - 1
37
+ else
38
+ size = size + 1
39
+ end
40
+ m = size.modulo(@size)
41
+ if m != 0
42
+ yield(i...(i+m))
43
+ i = i+m
44
+ end
45
+ while( i <= e )
46
+ yield(i...(i+@size))
47
+ i = i+@size
48
+ end
49
+ end
50
+ end
51
+
52
+
53
+ def number(humanizer, number, format = 'default', precision='')
54
+
55
+ if format == 'default' or format.nil?
23
56
  it = number._(:format,:default)
24
57
  else
58
+ format = format.to_sym
25
59
  it = number._.format( format._ | :default._ )
26
60
  end
27
- f = humanizer.get(it)
28
- if f.kind_of? String
29
- return sprintf(f,number)
61
+
62
+ if precision.kind_of? String and precision.length > 0
63
+ precision = x_to_i(precision)
64
+ end
65
+
66
+ unless precision.kind_of? Integer
67
+ precision = humanizer.get( it.precision , :default=>0 )
68
+ end
69
+
70
+ num = number.round_at(precision).to_s
71
+ full, frac = num.split('.', 2)
72
+
73
+ if( full.length > 3 )
74
+ separator = humanizer.get( it.separator , :default=>'' )
75
+ if separator.length > 0
76
+ full = PartitionEnumerator.new(0...full.length, 3).map{|rng|
77
+ full[rng]
78
+ }.join(separator)
79
+ end
30
80
  end
31
- warn "Unable to find Number format: #{it.inspect}."
32
- return ''
81
+
82
+ if( precision > 0 )
83
+ delimiter = humanizer.get( it.delimiter , :default=>'.' )
84
+ if frac.length > precision
85
+ frac = frac[0...precision]
86
+ else
87
+ frac = frac.ljust(precision, '0')
88
+ end
89
+ return [full, frac].join(delimiter)
90
+ end
91
+
92
+ return full
93
+
33
94
  end
34
95
 
35
96
  end
36
- end
97
+ end
@@ -15,28 +15,30 @@
15
15
  # (c) 2011 by Hannes Georg
16
16
  #
17
17
 
18
+ require 'facets/hash/graph.rb'
19
+
18
20
  module Humanized
19
- # A {Scope} is _the_ way to tell a {Humanizer} what you want from it.
21
+ # A {Query} is _the_ way to tell a {Humanizer} what you want from it.
20
22
  # It contains of three parts:
21
23
  # * {#path a list of paths}, which will be looked up in a {Source source}
22
24
  # * {#default a default}, which will be used if nothing was found
23
25
  # * {#variables variables}, which will be used to interpolate a found string
24
26
  # That's all you need!
25
- # The good thing: you'll unlikly create a scope by hand, that's done automatically with "_"!
27
+ # The good thing: you'll unlikly create a query by hand, that's done automatically with "_"!
26
28
  #
27
29
  # == Examples
28
30
  #
29
31
  # The basic steps:
30
- # # Creates a scope which looks up ":a" with no default and no variables:
32
+ # # Creates a query which looks up ":a" with no default and no variables:
31
33
  # :a._
32
- # # Creates a scope which looks up nothing, has a default of "String" but no variables:
34
+ # # Creates a query which looks up nothing, has a default of "String" but no variables:
33
35
  # "String"._
34
- # # Creates a scope which looks up nothing, has no default but the variable :foo = "bar"
36
+ # # Creates a query which looks up nothing, has no default but the variable :foo = "bar"
35
37
  # {:foo => 'bar'}._
36
38
  #
37
39
  # Combining these steps brings the power:
38
40
  #
39
- # # Creates a scope which looks up ":a", has a default of "String" and the variable :foo = "bar"
41
+ # # Creates a query which looks up ":a", has a default of "String" and the variable :foo = "bar"
40
42
  # :a._ + "String"._ + {:foo => 'bar'}._
41
43
  # # Shorthand for this:
42
44
  # :a._("String", :foo => 'bar')
@@ -49,7 +51,7 @@ module Humanized
49
51
  # class Admin < User
50
52
  # end
51
53
  # end
52
- # # Creates a scope matching ":site, :admin" or ":site, :user":
54
+ # # Creates a query matching ":site, :admin" or ":site, :user":
53
55
  # Site::Admin._
54
56
  # # This creates the same:
55
57
  # Site::Admin.new._
@@ -58,32 +60,32 @@ module Humanized
58
60
  # # This matches ":a, :b, :c":
59
61
  # [:a, :b, :c]._
60
62
  #
61
- # Finally for Scopes itself:
62
- # # Given scope is a Scope this is always true:
63
- # scope._._ == scope._
63
+ # Finally for Querys itself:
64
+ # # Given query is a Query this is always true:
65
+ # query._._ == query._
64
66
  #
65
67
  # I could continue the whole day ...
66
68
  #
67
69
  # == Tricks
68
- # A Scope responds to any method giving a new Scope suffixed by the method name
70
+ # A Query responds to any method giving a new Query suffixed by the method name
69
71
  # # Looks up ":a, :b, :c"
70
72
  # :a._.b.c
71
- # "_" can also take a block which is instance evaled on the scope:
73
+ # "_" can also take a block which is instance evaled on the query:
72
74
  # # Looks up ":a, :b, :c"
73
75
  # :a._{ b.c }
74
76
  # # Looks up ":a, :x" or ":a, :y"
75
77
  # :a._{ x | y }
76
- # There are two special scopes:
78
+ # There are two special querys:
77
79
  # # Looks up "", which will we be the whole source
78
- # Humanized::Scope::Root
80
+ # Humanized::Query::Root
79
81
  # # Looks up nothing
80
- # Humanized::Scope::None
82
+ # Humanized::Query::None
81
83
  #
82
- class Scope
84
+ class Query
83
85
 
84
86
  include Enumerable
85
87
  # @private
86
- UNMAGIC_METHODS = [:to_ary]
88
+ UNMAGIC_METHODS = [:to_ary, :to_s, :to_sym, :freeze]
87
89
  # @private
88
90
  NAME_REGEX = /[a-z_]+/.freeze
89
91
  # @private
@@ -92,25 +94,28 @@ module Humanized
92
94
  attr_reader :path, :depth, :variables, :default
93
95
 
94
96
  def self.from_str(str)
95
- Scope.new([ str.explode('.').map(&:to_sym) ])
97
+ Query.new([ str.explode('.').map(&:to_sym) ])
96
98
  end
97
99
 
98
- def initialize(path = [[]], depth = 1, variables = {}, default = nil)
100
+ def initialize(path = [[]], depth = nil, variables = {}, default = nil)
99
101
  @path = path.uniq
100
- @path.each do |path|
101
- path.freeze
102
+ @path.each do |p|
103
+ p.freeze
102
104
  end
103
105
  @path.freeze
104
- @depth = depth
105
- @variables = variables
106
+ if !depth.nil? and path.size != @path.size
107
+ depth -= ( path.size - @path.size )
108
+ end
109
+ @depth = (depth || @path.size)
110
+ @variables = variables.graph do |k,v| [k.to_sym, v] end
106
111
  @default = default
107
112
  end
108
113
 
109
114
  # This method is a here to enable awesome DSL.
110
115
  #== Example
111
- # s = Scope.new
112
- # s.defining.a.scope.using_methods # gives: (defining.a.scope.using_methods)
113
- # s.defining(:a,:scope,:using_methods) # gives: (defining.a.scope.using_methods)
116
+ # s = Query.new
117
+ # s.defining.a.query.using_methods # gives: (defining.a.query.using_methods)
118
+ # s.defining(:a,:query,:using_methods) # gives: (defining.a.query.using_methods)
114
119
  # s.this{ is.awesome | is.awful } # gives: (this.is.awesome , this.is.awful)
115
120
  #
116
121
  def method_missing(name, *args, &block)
@@ -126,17 +131,21 @@ module Humanized
126
131
  end
127
132
 
128
133
  def ==(other)
129
- return false unless other.kind_of? Scope
130
- return @path == other.path
134
+ return false unless other.kind_of? Query
135
+ if @path == other.path and @variables == other.variables and @default == other.default and @depth == other.depth
136
+ return true
137
+ else
138
+ return false
139
+ end
131
140
  end
132
141
 
133
- # Creates a {Scope scope} which matches either self or the other scope.
142
+ # Creates a {Query query} which matches either self or the other query.
134
143
  # @example
135
144
  # # this will match ":to_be" and ":not_to_be":
136
145
  # ( :to_be._ | :not_to_be._ )
137
146
  #
138
- # @param [Scope] other another scope
139
- # @return [Scope] a new scope
147
+ # @param [Query] other another query
148
+ # @return [Query] a new query
140
149
  def |(other)
141
150
  return other if @path.none?
142
151
  return self.dup if other.none?
@@ -153,19 +162,38 @@ module Humanized
153
162
  i = i + sd
154
163
  j = j + od
155
164
  end
156
- return Scope.new( result, sd + od , self.variables.merge(other.variables), other.default)
165
+ return Query.new( result, sd + od , self.variables.merge(other.variables), other.default)
157
166
  end
158
167
 
159
- # Creates a new scope which will optionally match this scope suffixed with the key.
168
+ # Creates a new query which will optionally match this query suffixed with the key.
169
+ #
170
+ # @example
171
+ # # this will match ":borat_is_stupid, :not" and ":borat_is_stupid":
172
+ # :borat_is_stupid._.optionally(:not)
160
173
  #
161
174
  # @example
162
175
  # # this will match ":borat_is_stupid, :not" and ":borat_is_stupid":
163
176
  # :borat_is_stupid._.optionally(:not)
164
177
  #
165
178
  # @param key
166
- # @return [Scope] a new scope
167
- def optionally(key)
168
- return self._(key) | self
179
+ # @return [Query] a new query
180
+ def optionally(*keys)
181
+
182
+ return self if keys.none?
183
+
184
+ q = self._(*keys)
185
+
186
+ begin
187
+
188
+ keys.pop
189
+
190
+ q |= q._(*keys)
191
+
192
+ end while keys.any?
193
+
194
+ q |= self
195
+
196
+ return q
169
197
  end
170
198
 
171
199
  def [](*args)
@@ -179,21 +207,21 @@ module Humanized
179
207
  result << path + [arg]
180
208
  end
181
209
  end
182
- return Scope.new( result, args.size )
210
+ return Query.new( result, args.size )
183
211
  end
184
212
 
185
- # Chain scopes together
213
+ # Chain querys together
186
214
  # @example
187
215
  # # this will match ":a,:b,:c"
188
216
  # :a._ + :b._ + :c._
189
217
  #
190
- # @param *args an array of scopes for chaining
191
- # @return [Scope]
218
+ # @param *args an array of querys for chaining
219
+ # @return [Query]
192
220
  def +(*args)
193
221
  return self if args.none?
194
- if( args.first.kind_of? Scope )
222
+ if( args.first.kind_of? Query )
195
223
  s = args.first
196
- return Scope.new(@path, @depth, variables.merge(s.variables), self.default || s.default ) if @path.none? or s.path.none?
224
+ return Query.new(@path, @depth, variables.merge(s.variables), self.default || s.default ) if @path.none? or s.path.none?
197
225
  # TODO: maybe modify depth too?
198
226
  new_path = []
199
227
  @path.each do |x|
@@ -201,12 +229,12 @@ module Humanized
201
229
  new_path << x + path
202
230
  end
203
231
  end
204
- return Scope.new(new_path, s.depth, variables.merge(s.variables), self.default || s.default )
232
+ return Query.new(new_path, s.depth, variables.merge(s.variables), self.default || s.default )
205
233
  end
206
234
  if @path.none?
207
235
  return self
208
236
  end
209
- return Scope.new( @path.map{|x| x + args} , @depth , @variables, @default)
237
+ return Query.new( @path.map{|x| x + args} , @depth , @variables, @default)
210
238
  end
211
239
 
212
240
  def _(*args,&block)
@@ -215,9 +243,9 @@ module Humanized
215
243
  loop do
216
244
  break if args.none?
217
245
  arg = args.shift
218
- if arg.kind_of? Symbol or arg.kind_of? Scope
246
+ if arg.kind_of? Symbol or arg.kind_of? Query
219
247
  thiz += arg
220
- elsif arg.class == Hash
248
+ elsif arg.respond_to? :humanized_variables? and arg.humanized_variables?
221
249
  vars = arg
222
250
  else
223
251
  thiz += arg._
@@ -234,11 +262,11 @@ module Humanized
234
262
  end
235
263
 
236
264
  def with_variables(vars)
237
- Scope.new(@path, @depth, variables.merge(vars), @default)
265
+ Query.new(@path, @depth, variables.merge(vars), @default)
238
266
  end
239
267
 
240
268
  def with_default(default)
241
- Scope.new(@path, @depth, @variables, default)
269
+ Query.new(@path, @depth, @variables, default)
242
270
  end
243
271
 
244
272
  def inspect
@@ -261,4 +289,4 @@ module Humanized
261
289
 
262
290
 
263
291
  end
264
- end
292
+ end