wrap_it 0.2.0 → 1.0.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/.yardopts +3 -0
  4. data/README.md +67 -66
  5. data/lib/wrap_it.rb +16 -16
  6. data/lib/wrap_it/arguments.rb +368 -0
  7. data/lib/wrap_it/base.rb +56 -47
  8. data/lib/wrap_it/callbacks.rb +24 -5
  9. data/lib/wrap_it/capture_array.rb +140 -0
  10. data/lib/wrap_it/container.rb +69 -25
  11. data/lib/wrap_it/derived_attributes.rb +22 -6
  12. data/lib/wrap_it/enums.rb +44 -34
  13. data/lib/wrap_it/frameworks.rb +7 -3
  14. data/lib/wrap_it/helpers.rb +66 -1
  15. data/lib/wrap_it/html.rb +149 -0
  16. data/lib/wrap_it/html_class.rb +164 -183
  17. data/lib/wrap_it/html_data.rb +28 -15
  18. data/lib/wrap_it/link.rb +40 -17
  19. data/lib/wrap_it/sections.rb +90 -2
  20. data/lib/wrap_it/switches.rb +33 -29
  21. data/lib/wrap_it/text_container.rb +83 -10
  22. data/lib/wrap_it/version.rb +2 -1
  23. data/spec/frameworks/log/development.log +2108 -0
  24. data/spec/integration/base_spec.rb +2 -2
  25. data/spec/integration/container_spec.rb +3 -3
  26. data/spec/integration/examples_spec.rb +16 -15
  27. data/spec/integration/text_container_spec.rb +3 -3
  28. data/spec/lib/arguments_array_spec.rb +37 -27
  29. data/spec/lib/arguments_spec.rb +153 -0
  30. data/spec/lib/base_spec.rb +2 -25
  31. data/spec/lib/callbacks_spec.rb +1 -1
  32. data/spec/lib/container_spec.rb +1 -1
  33. data/spec/lib/derived_attributes_spec.rb +1 -1
  34. data/spec/lib/enums_spec.rb +2 -3
  35. data/spec/lib/html_class_spec.rb +269 -80
  36. data/spec/lib/html_data_spec.rb +18 -12
  37. data/spec/lib/html_spec.rb +124 -0
  38. data/spec/lib/link_spec.rb +2 -2
  39. data/spec/lib/sections_spec.rb +1 -1
  40. data/spec/lib/switches_spec.rb +3 -3
  41. data/spec/lib/text_container_spec.rb +2 -2
  42. data/spec/support/example_groups/{wrap_it_example_group.rb → wrapped_example_group.rb} +5 -5
  43. data/wrap_it.gemspec +2 -0
  44. metadata +15 -8
  45. data/lib/wrap_it/arguments_array.rb +0 -128
  46. data/lib/wrap_it/module_helpers.rb +0 -23
@@ -1,233 +1,214 @@
1
+ require 'delegate'
2
+
1
3
  module WrapIt
2
4
  #
3
- # Methods for manipulationg with HTML class. For internal usage.
4
- # You should not include this class directly - subclass from
5
- # `WrapIt::Base` instead.
5
+ # Provides array-like access to HTML classes.
6
+ #
7
+ # This class delegate allmost all methods to underlying array with some
8
+ # value checking and modification. Also it restrict a list of methods,
9
+ # exposed below becouse call to theese methods unusefull in context of HTML
10
+ # class list.
11
+ #
12
+ # Some methods, thats described in this document have different manner.
13
+ # See each method description for details.
14
+ #
15
+ # All other methods can be used as with standard array
16
+ #
17
+ # Restricted methods: assoc, bsearch, combination, compact, compact!, fill,
18
+ # flatten, flatten!, insert, pack, permutation, product, rassoc,
19
+ # repeated_combination, rotate, repeated_permutation, reverse reverse!,
20
+ # reverse_each, sample, rotate!, shuffle, shuffle!, sort, sort!, sort_by!,
21
+ # transpose, uniq, uniq!, zip, flat_map, max, max_by, min, min_by, minmax,
22
+ # minmax_by
6
23
  #
7
24
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
8
25
  #
9
- module HTMLClass
10
- extend DerivedAttributes
11
-
12
- def self.included(base)
13
- base == Base || fail(
14
- TypeError,
15
- "#{self.class.name} can be included only into WrapIt::Base"
16
- )
17
- base.extend ClassMethods
18
- end
19
-
26
+ class HTMLClass < DelegateClass(Array)
20
27
  #
21
- # Sanitize HTML class list. Arguments list flatten and filtered for only
22
- # Strings and Symbols. Duplicates are removed.
28
+ # Sanitizes and normalizes HTML class. Makes array of classes flatten,
29
+ # removes all duplicates, splits spaced strings.
23
30
  #
24
- # @override sanitize([html_class, ...])
25
- # @param html_class [Object] HTML class
31
+ # @param values [Object] can be a symbol, string, array of symbols and
32
+ # strings, array of strings, strings can contains spaces.
26
33
  #
27
- # @return [Array<String>] sanitized HTML classes list
28
- def self.sanitize(*args)
29
- args
34
+ # @return [Array<String>] sanitized list of HTML classes
35
+ def self.sanitize(*values)
36
+ values
30
37
  .flatten
31
- .map { |a| a.is_a?(String) || a.is_a?(Symbol) ? a.to_s : nil }
32
- .compact
38
+ .each_with_object([]) do |i, a|
39
+ a << i.to_s if i.is_a?(String) || i.is_a?(Symbol)
40
+ end
41
+ .join(' ')
42
+ .strip
43
+ .split(/\s+/)
33
44
  .uniq
34
45
  end
35
46
 
36
- #
37
- # html class getter.
38
- #
39
- # @return [Array<String>] array of html classes of element.
40
- def html_class
41
- @options[:class]
47
+ def initialize(value = [])
48
+ super(HTMLClass.sanitize(value))
42
49
  end
43
50
 
44
- #
45
- # HTML class prefix getter.
46
- #
47
- # @return [String] HTML class prefix.
48
- def html_class_prefix
49
- @html_class_prefix ||= self.class.html_class_prefix
51
+ # Array overrides
52
+
53
+ # with array argument and new array return
54
+ %i(& + - concat |).each do |method|
55
+ define_method method do |values|
56
+ HTMLClass.new(__getobj__.send(method, HTMLClass.sanitize(values)))
57
+ end
50
58
  end
51
59
 
52
- #
53
- # Sets html class(es) for element.
54
- # @param value [Symbol, String, Array<Symbol, String>] HTML class or list
55
- # of classes. All classes will be converted to Strings, duplicates are
56
- # removed.
57
- # @return [void]
58
- #
59
- # @example
60
- # element.html_class = [:a, 'b', ['c', :d, 'a']]
61
- # element.html_class #=> ['a', 'b', 'c', 'd']
62
- def html_class=(value)
63
- @options[:class] = []
64
- add_html_class(value)
60
+ # array process, returning new array
61
+ %i(collect drop_while map reject select).each do |method|
62
+ define_method method do |&block|
63
+ result = __getobj__.send(method, &block)
64
+ result.is_a?(Array) ? HTMLClass.new(result) : result
65
+ end
65
66
  end
66
67
 
67
- #
68
- # Adds html class(es) to element. Chaining allowed. All classes will be
69
- # converted to Strings, duplicates are removed.
70
- # @override add_html_class([[html_class], ...])
71
- # @param html_class [Symbol, String, Array<Symbol, String>]
72
- # HTML class or list of HTML classes.
73
- # @return [self]
74
- #
75
- # @example
76
- # element.html_class = 'a'
77
- # element.add_html_class :b, :c, ['d', :c, :e, 'a']
78
- # element.html_class #=> ['a', 'b', 'c', 'd', 'e']
79
- def add_html_class(*args)
80
- if @options.key?(:class)
81
- @options[:class].is_a?(Array) || options[:class] = [options[:class]]
82
- args += @options[:class]
68
+ # bang! array process
69
+ %i(collect! map! reject!).each do |method|
70
+ define_method method do |&block|
71
+ obj = __getobj__
72
+ result = obj.send(method, &block)
73
+ obj.replace(HTMLClass.sanitize(obj))
74
+ result.is_a?(Array) ? self : result
83
75
  end
84
- @options[:class] = HTMLClass.sanitize(*args)
85
- self # allow chaining
86
76
  end
87
77
 
88
- #
89
- # Removes html class(es) from element. Chaining allowed.
90
- # @override add_html_class([[html_class], ...])
91
- # @param html_class [Symbol, String, Regexp, Array<Symbol, String, Regexp>]
92
- # HTML class or list of HTML classes.
93
- # @return [self]
94
- #
95
- # @example
96
- # element.add_html_class %w(a b c d e)
97
- # element.remove_html_class :b, ['c', :e]
98
- # element.html_class #=> ['a', 'd']
99
- def remove_html_class(*args)
100
- args.flatten!
101
- re = []
102
- args.reject! { |c| c.is_a?(Regexp) && re << c && true }
103
- args = args.uniq.map { |c| c.to_s }
104
- args.size > 0 && @options[:class].reject! { |c| args.include?(c) }
105
- re.is_a?(Array) && re.each do |r|
106
- @options[:class].reject! { |c| r.match(c) }
78
+ # non-bang array process, returning self
79
+ %i(each each_index keep_if select!).each do |method|
80
+ define_method method do |&block|
81
+ result = __getobj__.send(method, &block)
82
+ result.is_a?(Array) ? self : result
107
83
  end
108
- self # allow chaining
109
84
  end
110
85
 
86
+ %i(<< concat).each do |method|
87
+ define_method method do |values|
88
+ data = __getobj__ + HTMLClass.sanitize(values)
89
+ __setobj__(data.uniq)
90
+ self
91
+ end
92
+ end
93
+
94
+ # with any args, returning some obj or new array
95
+ %i([] drop first last pop shift slice! values_at).each do |method|
96
+ define_method method do |*args|
97
+ result = __getobj__.send(method, *args)
98
+ result.is_a?(Array) ? HTMLClass.new(result) : result
99
+ end
100
+ end
101
+ alias_method :slice, :[]
102
+
103
+ # @private
104
+ def clear
105
+ __getobj__.clear
106
+ self
107
+ end
108
+
109
+
111
110
  #
112
- # Determines whether element contains class, satisfied by conditions,
113
- # specified in method arguments.
114
- #
115
- # There are two forms of method call: with list of conditions as arguments
116
- # and with block for comparing. Method makes comparison with html class
117
- # untill first `true` return value or end of list. All conditions should
118
- # be satisfied for `true` return of this method.
119
- #
120
- # In first form, each argument treated as condition. Condition can be a
121
- # `Regexp`, so html classes of element tested for matching to that
122
- # regular expression. If condition is an `Array` then every class will be
123
- # tested for presence in this array. If condition is `Symbol` or `String`
124
- # classes will be compared with it via equality operator `==`.
111
+ # Removes elements from list by some conditions.
125
112
  #
126
- # In second form all arguments are ignored and for each comparison given
127
- # block called with html class as argument. Block return value then used.
113
+ # See {#index} for condition details
128
114
  #
129
- # @overload html_class([condition, ...])
130
- # @param condition [<Regexp, Symbol, String, Array<String>]
131
- # condition for comparison.
115
+ # @overload delete([cond, ...], &block)
116
+ # @param cond [Symbol, String, Array<String>, Regexp] [description]
117
+ # @param &block [Proc] searching block
132
118
  #
133
- # @overload html_class(&block)
134
- # @yield [html_class] Gives each html class to block. You should return
135
- # `true` if element contains this html class.
136
- # @yieldparam html_class [String] html class to inspect.
137
- # @yieldreturn [Boolean] whether element has html class.
119
+ # @return [self]
120
+ def delete(*args, &block)
121
+ obj = __getobj__
122
+ args.each do |x|
123
+ i = index(x)
124
+ next if i.nil? || i.is_a?(Enumerator)
125
+ obj.delete_at(i)
126
+ end
127
+ unless block.nil?
128
+ i = index(&block)
129
+ i.nil? || obj.delete_at(i)
130
+ end
131
+ self
132
+ end
133
+
138
134
  #
139
- # @return [Boolean] whether element has class with specified conditions.
135
+ # Searches HTML classes by conditions
140
136
  #
141
- # @example with `Symbol` or `String` conditions
142
- # element.html_class = [:a, :b, :c]
143
- # element.html_class?(:a) #=> true
144
- # element.html_class?(:d) #=> false
145
- # element.html_class?(:a, 'b') #=> true
146
- # element.html_class?(:a, :d) #=> false
137
+ # Conditions can be a Symbol, String, Array of strings or Regexp. Or you
138
+ # can provide block for searching.
147
139
  #
148
- # @example with `Regexp` conditions
149
- # element.html_class = [:some, :test]
150
- # element.html_class?(/some/) #=> true
151
- # element.html_class?(/some/, /bad/) #=> false
152
- # element.html_class?(/some/, :test) #=> true
140
+ # For Strings and Symbols array-like search used (symbols converted to
141
+ # strings). For Array conditions, any value from this array will match.
142
+ # For Regexp - regular expression matcher will used.
153
143
  #
154
- # @example with `Array` conditions
155
- # element.html_class = [:a, :b, :c]
156
- # element.html_class?(%w(a d)) #=> true
157
- # element.html_class?(%w(e d)) #=> false
144
+ # @param value [nil, Symbol, String, Array<String>, Regexp] condition
145
+ # @param block [Proc] searching block
158
146
  #
159
- # @example with block
160
- # element.html_class = [:a, :b, :c]
161
- # element.html_class? { |x| x == 'a' } #=> true
162
- def html_class?(*args, &block)
163
- args.all? { |c| inspect_class(:any?, c, &block) }
147
+ # @return [nil, Number] index of finded item or nil
148
+ def index(value = nil, &block)
149
+ value.is_a?(Symbol) && value = value.to_s
150
+ value.is_a?(Array) && value.map! { |x| x.to_s }
151
+ case
152
+ when value.is_a?(Regexp) then __getobj__.index { |x| value =~ x }
153
+ when value.is_a?(Array) then __getobj__.index { |x| value.include?(x) }
154
+ when block_given? then __getobj__.index(&block)
155
+ when value.nil? then __getobj__.index
156
+ else __getobj__.index(value)
157
+ end
164
158
  end
159
+ alias_method :rindex, :index
165
160
 
166
161
  #
167
- # Determines whether element doesn't contains class, satisfied by
168
- # conditions, specified in method arguments.
162
+ # Determines whether HTML classes have class, matching conditions
163
+ #
164
+ # @overload include?([cond, ...])
165
+ # @param cond [Symbol, String, Array<String>, Regexp] [description]
169
166
  #
170
- # @see html_class?
171
- def no_html_class?(*args, &block)
172
- args.all? { |c| inspect_class(:none?, c, &block) }
167
+ # @return [Boolean] whether HTML classes include specified class
168
+ def include?(*args)
169
+ args.all? do |x|
170
+ x.is_a?(Proc) ? !index(&x).nil? : !index(x).nil?
171
+ end
173
172
  end
174
173
 
175
- protected
176
-
177
- def add_default_classes
178
- add_html_class(self.class.collect_derived(:@html_class))
174
+ #
175
+ # Combines all classes, ready to insert in HTML.
176
+ #
177
+ # Actually just join all values with spaces
178
+ #
179
+ # @return [String] html string
180
+ def to_html
181
+ __getobj__.join(' ')
179
182
  end
180
183
 
181
- private
182
-
183
- def inspect_class(with, value = nil, &block)
184
- if block_given?
185
- @options[:class].send(with, &block)
186
- else
187
- case
188
- when value.is_a?(Regexp)
189
- @options[:class].send(with) { |c| value.match(c) }
190
- when value.is_a?(String) || value.is_a?(Symbol)
191
- @options[:class].send(with) { |c| value.to_s == c }
192
- when value.is_a?(Array)
193
- @options[:class].send(with) { |c| value.include?(c) }
194
- else
195
- false
196
- end
184
+ %i(replace).each do |method|
185
+ define_method method do |*values|
186
+ __getobj__.send(method, HTMLClass.sanitize(values))
187
+ self
197
188
  end
198
189
  end
199
190
 
200
- #
201
- # Class methods to include
202
- #
203
- module ClassMethods
204
- #
205
- # @dsl
206
- # Adds default html classes, thats are automatically added when element
207
- # created.
208
- # @override html_class([html_class, ...])
209
- # @param html_class [String, Symbol, Array<String, Symbol>] HTML class.
210
- # Converted to `String`
211
- #
212
- # @return [void]
213
- def html_class(*args)
214
- @html_class.nil? || args += @html_class
215
- @html_class = HTMLClass.sanitize(*args)
191
+ %i(push unshift).each do |method|
192
+ define_method method do |*values|
193
+ __getobj__.send(method, *HTMLClass.sanitize(values))
194
+ __getobj__.uniq!
195
+ self
216
196
  end
197
+ end
217
198
 
218
- #
219
- # @dsl
220
- # Sets HTML class prefix. It used in switchers and enums
221
- # @param prefix [String] HTML class prefix
222
- #
223
- # @return [void]
224
- def html_class_prefix(prefix = nil)
225
- return(get_derived(:@html_class_prefix) || '') if prefix.nil?
226
- prefix.is_a?(String) || prefix.is_a?(Symbol) || fail(
227
- ArgumentError, 'prefix should be a String or Symbol'
228
- )
229
- @html_class_prefix = prefix.to_s
199
+ # Restricted functions
200
+ %i(
201
+ * []= assoc bsearch combination compact compact! fill flatten flatten!
202
+ insert pack permutation product rassoc repeated_combination rotate
203
+ repeated_permutation reverse reverse! reverse_each sample rotate! shuffle
204
+ shuffle! sort sort! sort_by! transpose uniq uniq! zip flat_map max max_by
205
+ min min_by minmax minmax_by
206
+ ).each do |method|
207
+ define_method(method) do
208
+ raise "Method #{method} is not supported for HTMLClass"
230
209
  end
231
210
  end
211
+
212
+ # Enumerable overrides
232
213
  end
233
214
  end
@@ -1,25 +1,38 @@
1
+ require 'delegate'
2
+
1
3
  module WrapIt
2
4
  #
3
- # Provides methods related to HTML `data` attribute
5
+ # Provides hash-like access to HTML data.
4
6
  #
5
7
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
8
  #
7
- module HTMLData
8
- def self.included(base)
9
- base == Base || fail(
10
- TypeError,
11
- "#{self.class.name} can be included only into WrapIt::Base"
12
- )
13
- end
14
-
15
- def set_html_data(name, value)
16
- @options[:data] ||= {}
17
- @options[:data][name.to_sym] = value
9
+ class HTMLData < DelegateClass(Hash)
10
+ #
11
+ # Sanitizes html data
12
+ #
13
+ # @overload sanitize(values = {})
14
+ # @param values [Hash] hash to sanitize
15
+ #
16
+ # @return [Hash] sanitized hash
17
+ def self.sanitize(**values)
18
+ Hash[values
19
+ .map do |k, v|
20
+ k = k.to_s
21
+ if k.include?('-')
22
+ k, n = k.split(/-/, 2)
23
+ v = sanitize(n.to_sym => v)
24
+ else
25
+ k = k.downcase.gsub(/[^a-z0-9_]+/, '').gsub(/\A\d+/, '')
26
+ v = v.is_a?(Hash) ? sanitize(v) : v.to_s
27
+ end
28
+ k.empty? ? nil : [k.to_sym, v]
29
+ end
30
+ .compact
31
+ ]
18
32
  end
19
33
 
20
- def remove_html_data(name)
21
- return unless @options[:data].is_a?(Hash)
22
- @options[:data].delete(name.to_sym)
34
+ def initialize(**value)
35
+ super(HTMLData.sanitize(**value))
23
36
  end
24
37
  end
25
38
  end