qipowl 0.9.3 → 0.9.7

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.
@@ -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