wrap_it 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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