qipowl 0.9.3 → 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,10 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  # КТО ЧТО_ДЕЛАЕТ КОМУ ЧТО
4
- # ☺ ⚙
4
+ # ☺ ⚙
5
5
 
6
6
  # КАКОЙ КАК КАКОЙ КАКОЙ
7
- # ☼
7
+ # ☼
8
8
 
9
9
  # ПОКА НЕ ОПРЕДЕЛЕНА ЧАСТЬ РЕЧИ (найти термин для)
10
10
  # ∈
@@ -19,7 +19,7 @@ require_relative '../core/bowler'
19
19
  module Qipowl
20
20
  module Mappers
21
21
  class IspruBowlerMapper < BowlerMapper
22
-
22
+
23
23
  end
24
24
  end
25
25
 
@@ -30,7 +30,7 @@ module Qipowl
30
30
 
31
31
  LANG_FROM = 'es' # FIXME UGLY
32
32
  LANG_TO = 'ru' # FIXME UGLY
33
-
33
+
34
34
  ##############################################################################
35
35
  ### Default handlers for all the types of markup ###
36
36
  ##############################################################################
@@ -73,20 +73,20 @@ module Qipowl
73
73
  # @see {Qipowl::Bowler#serveup}
74
74
  #
75
75
  # Additionally it beatifies the output HTML
76
- #
76
+ #
77
77
  # @param [String] str to be roasted
78
78
  def serveup str
79
- (super str).typo(LANG_TO).strip
79
+ (super str).typo(lang: LANG_TO).strip
80
80
  end
81
81
 
82
82
  private
83
- # Hence we cannot simply declare the DSL for it, we need to handle
83
+ # Hence we cannot simply declare the DSL for it, we need to handle
84
84
  # calls to all the _methods_, starting with those symbols.
85
- #
85
+ #
86
86
  # @param [Symbol] method as specified by caller (`method_missing`.)
87
87
  # @param [Array] args as specified by caller (`method_missing`.)
88
88
  # @param [Proc] block as specified by caller (`method_missing`.)
89
- #
89
+ #
90
90
  # @return [Array] the array of words
91
91
  def special_handler method, *args, &block
92
92
  ∀_word method, args, block
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Qipowl
4
+ BTRACE_COUNT = 6
4
5
  ENTITIES = %w(block alone magnet grip regular self)
5
- end
6
+ end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'typogrowth'
4
+
3
5
  require_relative "../constants"
4
6
  require_relative '../utils/logging'
5
7
 
@@ -34,9 +36,9 @@ module Qipowl::Bowlers
34
36
  #
35
37
  class Bowler
36
38
  include TypoLogging
37
-
39
+
38
40
  attr_reader :in, :out
39
-
41
+
40
42
  # Internal constant for joining/splitting the strings during processing.
41
43
  # Override on your own risk. I can′t imagine why you would need to do so.
42
44
  SEPARATOR = $, || ' '
@@ -99,9 +101,11 @@ module Qipowl::Bowlers
99
101
  key = key.bowl.to_sym
100
102
  tags[key] = value.to_sym
101
103
  self.class.const_get("ENCLOSURES_TAGS")[key] = enclosure_value.to_sym if enclosure_value
104
+ self.class.const_get("ENTITIES")[section.to_sym][key] = value.to_sym
102
105
  self.class.class_eval %Q{
103
106
  alias_method :#{key}, :∀_#{section}
104
107
  } # unless self.class.instance_methods(true).include?(key.bowl)
108
+ @shadows = nil
105
109
  else
106
110
  logger.warn "Trying to add key “#{key}” in an invalid section “#{section}”. Ignoring…"
107
111
  end
@@ -109,21 +113,25 @@ module Qipowl::Bowlers
109
113
 
110
114
  # Removes key from both {Mapping.SPICES} and {Mapping.SALT}. See {#add_spice}
111
115
  #
112
- # @param [Symbol] key the key to be removed
113
- def remove_entity key
116
+ # @param [Symbol] entity the key to be removed
117
+ def remove_entity entity
118
+ key = entity.to_sym
114
119
  Qipowl::ENTITIES.each { |section|
115
120
  next unless (curr_sect = self.class.const_get("#{section.upcase}_TAGS") rescue nil)
116
- next unless send :"∃_#{section}_tag", key.to_sym
117
-
121
+ curr_tag = send(:"∃_#{section}", key)
122
+ next unless curr_tag
123
+
118
124
  curr_sect.delete key
119
125
  self.class.const_get("ENCLOSURES_TAGS").delete key
126
+ self.class.const_get("ENTITIES")[section.to_sym].delete key
127
+
120
128
  self.class.class_eval %Q{
121
129
  remove_method :#{key.bowl}
122
130
  }
131
+ @shadows = nil
123
132
  }
124
133
  end
125
134
 
126
-
127
135
  protected
128
136
  Qipowl::ENTITIES.each { |section|
129
137
  define_method "∀_#{section}".to_sym, ->(*args) {
@@ -132,7 +140,7 @@ module Qipowl::Bowlers
132
140
  }
133
141
 
134
142
  def defreeze str
135
- str
143
+ str.typo(sections: :quotes).defuse
136
144
  end
137
145
 
138
146
  def roast str
@@ -143,7 +151,10 @@ module Qipowl::Bowlers
143
151
  rescue Exception => e
144
152
  msg = e.message.dup
145
153
  logger.error '='*78
146
- logger.error "Could not roast dish [#{msg.force_encoding(Encoding::UTF_8)}].\nWill return as is… Dish follows:\n\n"
154
+ logger.error "Could not roast dish [#{msg.force_encoding(Encoding::UTF_8)}].\nWill return as is… Backtrace:\n"
155
+ logger.error e.backtrace.take(Qipowl::BTRACE_COUNT).join("\n")
156
+ logger.error '-'*78
157
+ logger.error "Dish follows:\n"
147
158
  logger.error '-'*78
148
159
  logger.error dish
149
160
  logger.error '='*78
@@ -153,16 +164,16 @@ module Qipowl::Bowlers
153
164
  @yielded.pop(@yielded.size).reverse.join(SEPARATOR)
154
165
  }.join($/).uncarriage.un␚ify.unspacefy.unbowl
155
166
  end
156
-
167
+
157
168
  def serveup str
158
- str.gsub(/⌦./, '').gsub(/.⌫/, '')
169
+ str.gsub(/⌦./, '').gsub(/.⌫/, '').typo
159
170
  end
160
171
 
161
172
  protected
162
173
  # The handler of the last “orphaned” text block in the input string.
163
174
  #
164
175
  # E.g.:
165
- #
176
+ #
166
177
  # Here goes a quite significant list:
167
178
  #
168
179
  # • line item 1
@@ -195,25 +206,25 @@ module Qipowl::Bowlers
195
206
  # After we have this part of input processed, it should be considered
196
207
  # “done.” So block processors call {#harvest} to store processed parts.
197
208
  #
198
- # @param [Symbol] callee of this method. Qipowl hardly relies on method namings and sometimes we may need to know if the call was made by, say, lineitem DSL (`•`), not datalist (`▷`).
209
+ # @param [Symbol] cally of this method. Qipowl hardly relies on method namings and sometimes we may need to know if the call was made by, say, lineitem DSL (`•`), not datalist (`▷`).
199
210
  # @param [String] str string to yield
200
211
  #
201
212
  # @return nil
202
- def harvest callee, str
213
+ def harvest cally, str
203
214
  @yielded << str unless str.vacant?
204
215
  nil
205
216
  end
206
-
217
+
207
218
  private
208
219
  # Prepares blocks in the input for the execution
209
220
  def block str
210
221
  return str unless self.class.const_defined?(:BLOCK_TAGS)
211
222
  result = str.dup
212
223
  self.class::BLOCK_TAGS.each { |tag, value|
213
- result.gsub!(/(#{tag})\s*(\S*\s?|$)(.*?)(#{tag}|\Z)/m) {
224
+ result.gsub!(/(#{tag})(.*?)(?:#{tag}|\Z)/m) { |m|
214
225
  %Q{
215
226
 
216
- #{$1} #{$2.strip.bowl} #{$3.bowl.carriage}
227
+ #{$1} #{Base64.encode64($2).carriage(false).bowl}
217
228
 
218
229
  }
219
230
  }
@@ -245,14 +256,12 @@ module Qipowl::Bowlers
245
256
  }
246
257
  result.unbowl
247
258
  end
248
-
259
+
249
260
  def split str
250
261
  (block str).split(/\R{2,}/).map { |para|
251
- self.class.const_defined?(:BLOCK_TAGS) &&
252
- (para =~ /\A(#{self.class::BLOCK_TAGS.keys.join('|')})\s+/) ?
253
- para : (grip custom para)
262
+ (grip custom para)
254
263
  }
255
264
  end
256
-
265
+
257
266
  end
258
267
  end
@@ -8,7 +8,7 @@ require_relative '../utils/logging'
8
8
 
9
9
  # @author Alexei Matyushkin
10
10
  module Qipowl::Mappers
11
-
11
+
12
12
  # Operates +mapping+ for loaded +YAML+ rules files.
13
13
  #
14
14
  # - For top level sections, each section name
@@ -27,30 +27,34 @@ module Qipowl::Mappers
27
27
  @hash
28
28
  end
29
29
  def merge! input
30
- input = load_yaml(input) if input.is_one_of?(String, IO)
31
- raise ArgumentError.new "Invalid map for merge in Mapper" \
32
- unless input.respond_to? :to_hash
30
+ map = load_yaml(input) if input.is_one_of?(String, IO)
31
+ raise ArgumentError.new "Invalid map (#{input} @ #{Qipowl.bowlers}) for merge in Mapper.\nCurrent dir: [#{Dir.pwd}].\n" \
32
+ unless map.respond_to? :to_hash
33
33
 
34
- incs = input.delete(:includes)
34
+ incs = map.delete(:includes)
35
35
 
36
36
  @entities_dirty = true
37
- @hash.rmerge!(input.to_hash)
37
+ @hash.rmerge!(map.to_hash)
38
38
  incs.each { |inc|
39
39
  merge! inc
40
40
  } rescue NoMethodError # FIXME WTF rescueing here?
41
41
  end
42
42
  private
43
- # FIXME Make file search more flexible!!!
43
+ def load_yaml_file name
44
+ [*Qipowl.bowlers].each { |b|
45
+ (return YAML.load_file("#{b}/#{name}.yaml")) rescue next
46
+ }
47
+ nil
48
+ end
49
+
44
50
  def load_yaml input
45
- IO === input ?
46
- YAML.load_stream(input) :
47
- YAML.load_file("#{Qipowl.bowlers_dir}/#{input.downcase}.yaml")
51
+ IO === input ? YAML.load_stream(input) : load_yaml_file("#{input.downcase}")
48
52
  end
49
53
  end
50
-
54
+
51
55
  class BowlerMapper < Mapper
52
56
  include TypoLogging
53
-
57
+
54
58
  def initialize input = nil
55
59
  input = self.class.name.split('::').last.downcase.gsub(/bowlermapper\Z/, '') if input.nil?
56
60
  super input
@@ -67,11 +71,12 @@ module Qipowl::Mappers
67
71
  # Append keys
68
72
  @entities[key][k.bowl] = v.dup
69
73
  if Hash === v
70
- @entities[key][k.bowl].delete(:synonyms)
74
+ @entities[key][k.bowl].delete(:synonyms)
71
75
  # Append explicit synonyms
72
76
  v[:synonyms].each { |syn|
73
- (@entities[key][syn.bowl] = v.dup).delete(:synonyms)
74
- @entities[key][syn.bowl][:marker] = k.bowl
77
+ syn = syn.bowl.to_sym
78
+ (@entities[key][syn] = v.dup).delete(:synonyms)
79
+ @entities[key][syn][:marker] = k.bowl
75
80
  } if v[:synonyms]
76
81
  end
77
82
  }
@@ -81,10 +86,3 @@ module Qipowl::Mappers
81
86
  end
82
87
  end
83
88
  end
84
-
85
- if __FILE__ == $0
86
- require '../../qipowl'
87
- y = Qipowl::Mappers::BowlerMapper.new 'html'
88
- require 'awesome_print'
89
- ap y[:mail]
90
- end
@@ -18,12 +18,12 @@ module Qipowl
18
18
  end
19
19
 
20
20
  # Bowling string means producing interpreter-safe text basing on ascii input.
21
- #
21
+ #
22
22
  class ::String
23
23
  NBSP = "\u{00A0}"
24
-
24
+
25
25
  SYMBOL_FOR_SPACE = "\u{2420}" # ␠
26
-
26
+
27
27
  WIDESPACE = "\u{FF00}"
28
28
  EN_SPACE = "\u{2002}"
29
29
  EM_SPACE = "\u{2003}"
@@ -38,8 +38,8 @@ module Qipowl
38
38
  NARROW_NO_BREAK_SPACE = "\u{202F}"
39
39
  MEDIUM_MATHEMATICAL_SPACE = "\u{205F}"
40
40
  ZERO_WIDTH_NO_BREAK_SPACE = "\u{FEFF}"
41
- IDEOGRAPHIC_SPACE = "\u{3000}"
42
-
41
+ IDEOGRAPHIC_SPACE = "\u{3000}"
42
+
43
43
  CARRIAGE_RETURN = '␍'
44
44
  NULL = '␀'
45
45
  ASCII_SYMBOLS, ASCII_DIGITS, ASCII_LETTERS_SMALL, ASCII_LETTERS_CAP = [
@@ -58,14 +58,14 @@ module Qipowl
58
58
  }.flatten]
59
59
  }
60
60
  UTF_ALL = [UTF_SYMBOLS.values, UTF_DIGITS.values, UTF_LETTERS_SMALL.values, UTF_LETTERS_CAP.values]
61
-
61
+
62
62
  UTF_ASCII = UTF_SYMBOLS.merge(UTF_DIGITS).merge(UTF_LETTERS_SMALL).merge(UTF_LETTERS_CAP)
63
63
  ASCII_UTF = UTF_ASCII.invert
64
-
64
+
65
65
  def vacant?
66
66
  nil? || empty?
67
67
  end
68
-
68
+
69
69
  def hsub! hash
70
70
  self.gsub!(/#{hash.keys.join('|')}/, hash)
71
71
  end
@@ -101,41 +101,34 @@ module Qipowl
101
101
  (out = self.dup).unspacefy!
102
102
  out
103
103
  end
104
-
104
+
105
105
  def unuglify
106
106
  self.unbowl.unspacefy.uncarriage.strip
107
107
  end
108
108
 
109
109
  HTML_ENTITIES = Hash[[['<', 'lt'], ['>', 'gt'], ['&', 'amp']].map { |k, v| [k.bowl, "&#{v};"] }]
110
110
 
111
- def carriage
112
- self.gsub(/\R/, " #{CARRIAGE_RETURN} ")
113
- end
114
- def carriage!
115
- self.gsub!(/\R/, " #{CARRIAGE_RETURN} ")
111
+ def carriage spaces = true
112
+ self.gsub(/\R/, spaces ? " #{CARRIAGE_RETURN} " : "#{CARRIAGE_RETURN}")
116
113
  end
117
- def uncarriage
118
- self.gsub(/\s?#{CARRIAGE_RETURN}\s?/, %Q(
119
- ))
120
- end
121
- def uncarriage!
122
- self.gsub!(/\s?#{CARRIAGE_RETURN}\s?/, %Q(
114
+ def uncarriage spaces = true
115
+ self.gsub(spaces ? /\s?#{CARRIAGE_RETURN}\s?/ : /#{CARRIAGE_RETURN}/, %Q(
123
116
  ))
124
117
  end
125
118
 
126
119
  def un␚ify
127
120
  self.gsub(/␚(.*?)␚/, '')
128
121
  end
129
-
122
+
130
123
  def wstrip
131
124
  self.gsub(/#{NBSP}/, '')
132
125
  end
133
-
126
+
134
127
  def to_filename
135
128
  self.gsub(/[#{Regexp.quote(ASCII_SYMBOLS.join)}]/, UTF_ASCII).gsub(/\s/, "#{NBSP}")[0..50]
136
129
  end
137
130
  end
138
-
131
+
139
132
  class ::Symbol
140
133
  def dup
141
134
  self.to_s.dup.to_sym
@@ -159,11 +152,11 @@ module Qipowl
159
152
  self.to_s.wstrip.to_sym
160
153
  end
161
154
  end
162
-
155
+
163
156
  class ::Fixnum
164
157
  def ␚ify
165
158
  "␚#{self}␚"
166
159
  end
167
160
  end
168
-
161
+
169
162
  end
@@ -5,7 +5,7 @@ require_relative '../utils/logging'
5
5
 
6
6
  # @author Alexei Matyushkin
7
7
  module Qipowl
8
-
8
+
9
9
  # Operates +mapping+ for loaded +YAML+ rules files.
10
10
  #
11
11
  # - For top level sections, each section name
@@ -14,18 +14,18 @@ module Qipowl
14
14
  # method which is simply loads rules from the respective file
15
15
  #
16
16
  # Mapping may be loaded from YAML file, as well as be merged
17
- # against other YAML file, hash or `Ruler` instance.
17
+ # against other YAML file, hash or `Ruler` instance.
18
18
  module Ruler
19
19
  include TypoLogging
20
20
  extend self
21
21
 
22
22
  @@bowlers = {} # FIXME REDIS!!!!
23
-
23
+
24
24
  def get_bowler id: nil, type: nil
25
25
  @@bowlers[id] || new_bowler(type, true)
26
26
  end
27
-
28
- def new_bowler type, persistent = false
27
+
28
+ def new_bowler type, persistent: false, additional_maps: []
29
29
  yaml, clazz = \
30
30
  case type
31
31
  when Class
@@ -33,63 +33,73 @@ module Qipowl
33
33
  when String, Symbol
34
34
  ["#{type.to_s.downcase}", Qipowl::Bowlers.const_get(type.to_s.capitalize.to_sym)]
35
35
  end
36
-
36
+
37
37
  raise NameError.new("Invalid bowler type: #{type}") \
38
38
  unless clazz.is_a?(Class) && clazz < Qipowl::Bowlers::Bowler
39
-
39
+
40
40
  id = "#{Time.now.to_i}#{rand(1..1_000_000_000)}"
41
41
  name = "#{clazz.name.split('::').last}_#{id}"
42
42
  clazz = Qipowl::Bowlers.const_set(name, Class.new(clazz))
43
43
 
44
- teach_class clazz, get_yaml(yaml)
44
+ teach_class clazz, get_yaml(yaml, additional_maps: additional_maps)
45
45
 
46
46
  persistent ? [@@bowlers[id] = clazz.new, id] : clazz.new
47
47
  end
48
-
48
+
49
49
  private
50
- def get_yaml yaml
50
+ def get_yaml yaml, additional_maps: []
51
51
  clazz = Qipowl::Mappers.const_get("#{yaml.capitalize}BowlerMapper")
52
52
  raise NameError.new("Invalid mapper type: #{clazz}") \
53
53
  unless clazz.is_a?(Class) && clazz < Qipowl::Mappers::BowlerMapper
54
-
55
- clazz.new
54
+
55
+ result = clazz.new
56
+ [*additional_maps].each { |map|
57
+ result.merge! map
58
+ }
59
+ result
56
60
  end
57
-
58
- # FIXME Make contants PER-EIGENCLASS
59
- def teach_class clazz, mapper
60
- clazz.const_set("CUSTOM_TAGS", mapper.to_hash[:custom])
61
- clazz.const_set("ENCLOSURES_TAGS", mapper.to_hash[:enclosures])
62
- clazz.const_set("ENTITIES", mapper.entities)
63
- clazz.const_set("TAGS", {})
64
- clazz.class_eval %Q{
65
- def ∃_enclosures entity
66
- self.class::ENCLOSURES_TAGS.each { |k, v|
61
+
62
+ def teach_class_prepare clazz
63
+ clazz.const_set('CUSTOM_TAGS', {}) unless clazz.const_defined? 'CUSTOM_TAGS'
64
+ clazz.const_set('ENCLOSURES_TAGS', {}) unless clazz.const_defined? 'ENCLOSURES_TAGS'
65
+ Qipowl::ENTITIES.each { |section|
66
+ clazz.const_set("#{section.upcase}_TAGS", {}) \
67
+ unless clazz.const_defined? "#{section.upcase}_TAGS"
68
+ }
69
+ clazz.const_set('ENTITIES', {}) unless clazz.const_defined? 'ENTITIES'
70
+ clazz.const_set('TAGS', {}) unless clazz.const_defined? 'TAGS'
71
+ end
72
+
73
+ def ∃_template section
74
+ %Q{
75
+ def ∃_#{section} entity
76
+ self.class::#{section.upcase}_TAGS.each { |k, v|
67
77
  next unless k == entity
68
78
  v = {:tag => v} unless Hash === v
69
79
  v[:origin] = self.class::TAGS[v[:tag]]
80
+ v[:section] = k
70
81
  return v
71
82
  }
72
83
  nil
73
84
  end
74
85
  }
86
+ end
87
+
88
+ def teach_class clazz, mapper
89
+ teach_class_prepare clazz
90
+
91
+ clazz::CUSTOM_TAGS.rmerge! mapper.to_hash[:custom] if mapper.to_hash[:custom]
92
+ clazz::ENCLOSURES_TAGS.rmerge! mapper.to_hash[:enclosures] if mapper.to_hash[:enclosures]
93
+ clazz::ENTITIES.rmerge! mapper.entities if mapper.entities
94
+
95
+ clazz.class_eval ∃_template 'enclosures'
96
+
75
97
  Qipowl::ENTITIES.each { |section|
76
98
  next unless mapper.entities && mapper.entities[section.to_sym]
77
- clazz.const_set("#{section.upcase}_TAGS", mapper.entities[section.to_sym])
78
- clazz.class_eval %Q{
79
- self::TAGS.rmerge! self::#{section.upcase}_TAGS
80
- def ∃_#{section} entity
81
- self.class::#{section.upcase}_TAGS.each { |k, v|
82
- next unless k == entity
83
- v = {:tag => v} unless Hash === v
84
- v[:section] = k
85
- return v
86
- }
87
- nil
88
- end
89
- def ∃_#{section}_tag entity
90
- ∃_#{section}(entity)[:tag] if ∃_#{section}(entity)
91
- end
92
- }
99
+ clazz.const_get("#{section.upcase}_TAGS").rmerge! mapper.entities[section.to_sym]
100
+ clazz.const_get('TAGS').rmerge! clazz.const_get("#{section.upcase}_TAGS")
101
+ clazz.class_eval ∃_template section
102
+
93
103
  mapper.entities[section.to_sym].each { |key, value|
94
104
  tag = Hash === value && value[:marker] ? value[:marker] : "∀_#{section}"
95
105
  clazz.class_eval %Q{
@@ -97,11 +107,6 @@ module Qipowl
97
107
  } unless clazz.instance_methods.include?(key.to_sym)
98
108
  }
99
109
  }
100
- clazz.class_eval %Q{
101
- def ∀_tags
102
- self.class::TAGS.keys
103
- end
104
- }
105
110
  end
106
111
  end
107
112
  end