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
data/lib/wrap_it/base.rb CHANGED
@@ -19,59 +19,79 @@ module WrapIt
19
19
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
20
20
  #
21
21
  class Base
22
+ # Documentation includes
23
+ #
24
+ # @!parse extend Arguments::ClassMethods
25
+ # @!parse extend Enums::ClassMethods
26
+ # @!parse extend HTML::ClassMethods
27
+ # @!parse extend Sections::ClassMethods
28
+ # @!parse extend Switches::ClassMethods
29
+
30
+ #
31
+ # include appropriate functionality from modules
22
32
  include DerivedAttributes
23
33
  include Callbacks
24
34
 
25
35
  callback :initialize, :capture, :render
26
36
 
37
+ include Arguments
27
38
  include Sections
28
- include HTMLClass
29
- include HTMLData
39
+ include HTML
30
40
  include Switches
31
41
  include Enums
32
42
  include Renderer
33
43
 
34
44
  @omit_content = false
35
45
 
36
- attr_reader :tag
37
- attr_reader :options
38
-
46
+ attr_reader :helper_name
47
+ option :helper_name
48
+ option :tag
39
49
  section :content, :render_arguments, :render_block
40
50
  place :content, :after, :begin
41
51
  place :render_block, :after, :begin
42
52
  place :render_arguments, :after, :begin
43
53
 
44
54
  def initialize(template, *args, &block)
45
- @template, @arguments, @block = template, args, block
46
- self.options = @arguments.extract_options!
47
-
48
- @helper_name = @options.delete(:helper_name)
49
- @helper_name.is_a?(String) && @helper_name = @helper_name.to_sym
50
-
51
- @arguments.extend ArgumentsArray
55
+ @template, @block = template, block
52
56
  add_default_classes
53
-
54
57
  run_callbacks :initialize do
55
- @tag = @options.delete(:tag) ||
56
- self.class.get_derived(:@default_tag) || 'div'
57
- @tag = @tag.to_s
58
+ capture_arguments!(args, &block)
59
+ # TODO: uncomment following after html_attr implementation finished
60
+ #html_attr.merge!(args.extract_options!)
61
+ self.html_attr = args.extract_options!
62
+ # TODO: find convenient way to save unprocessed arguments
63
+ @arguments = args
58
64
  end
59
65
  end
60
66
 
67
+ def tag
68
+ @tag ||= (self.class.get_derived(:@default_tag) || 'div').to_s
69
+ end
70
+
71
+ def tag=(value)
72
+ value.is_a?(Symbol) && value = value.to_s
73
+ value.is_a?(String) && @tag = value
74
+ end
75
+
76
+ def helper_name=(value)
77
+ value.is_a?(String) && value = value.to_sym
78
+ value.is_a?(Symbol) && @helper_name = value
79
+ end
80
+
61
81
  def omit_content?
62
- self.class.get_derived(:@omit_content)
82
+ self.class.get_derived(:@omit_content) == true
63
83
  end
64
84
 
65
85
  #
66
86
  # Renders element to template
67
87
  #
68
- # @override render([content, ...])
69
- # @param content [String] additional content that will be appended
70
- # to element content
71
- # @yield [element] Runs block after capturing element content and before
72
- # rendering it. Returned value appended to content.
73
- # @yieldparam element [Base] rendering element.
74
- # @yieldreturn [String, nil] content to append to HTML
88
+ # @overload render([content, ...])
89
+ # @param content [String] additional content that will be appended
90
+ # to element content
91
+ # @yield [element] Runs block after capturing element content and before
92
+ # rendering it. Returned value appended to content.
93
+ # @yieldparam element [Base] rendering element.
94
+ # @yieldreturn [String, nil] content to append to HTML
75
95
  #
76
96
  # @return [String] rendered HTML for element
77
97
  def render(*args, &render_block)
@@ -110,17 +130,17 @@ module WrapIt
110
130
  #
111
131
  # If block present, it will be called when wrapper will rendered.
112
132
  #
113
- # @override wrap(wrapper)
114
- # @param wrapper [Base] wrapper instance.
133
+ # @overload wrap(wrapper)
134
+ # @param wrapper [Base] wrapper instance.
115
135
  #
116
- # @override wrap(wrapper_class, [arg, ...], options = {})
117
- # @param wrapper_class [Class] WrapIt::Base subclass for wrapper.
118
- # @param arg [String, Symbol] wrapper creation arguments.
119
- # @param options [Hash] wrapper creation options.
136
+ # @overload wrap(wrapper_class, [arg, ...], options = {})
137
+ # @param wrapper_class [Class] WrapIt::Base subclass for wrapper.
138
+ # @param arg [String, Symbol] wrapper creation arguments.
139
+ # @param options [Hash] wrapper creation options.
120
140
  #
121
- # @override wrap([arg, ...], options = {})
122
- # @param arg [String, Symbol] wrapper creation arguments.
123
- # @param options [Hash] wrapper creation options.
141
+ # @overload wrap([arg, ...], options = {})
142
+ # @param arg [String, Symbol] wrapper creation arguments.
143
+ # @param options [Hash] wrapper creation options.
124
144
  #
125
145
  # @return [void]
126
146
  def wrap(*args, &block)
@@ -139,7 +159,6 @@ module WrapIt
139
159
  protected
140
160
 
141
161
  #
142
- # @dsl
143
162
  # Defines or gets default tag name for element. This tag can be changed
144
163
  # soon. Without parameters returns current default_tag value.
145
164
  # @param name [<Symbol, String>] Tag name. Converted to `String`.
@@ -158,17 +177,6 @@ module WrapIt
158
177
  @omit_content = true
159
178
  end
160
179
 
161
- def options=(hash)
162
- hash.is_a?(Hash) || return
163
- hash.symbolize_keys!
164
-
165
- # sanitize class
166
- hash[:class] ||= []
167
- hash[:class] = [hash[:class]] unless hash[:class].is_a?(Array)
168
- hash[:class] = hash[:class].map { |c| c.to_s }.uniq
169
- @options = hash
170
- end
171
-
172
180
  def capture_sections
173
181
  run_callbacks :capture do
174
182
  unless @block.nil?
@@ -199,12 +207,13 @@ module WrapIt
199
207
 
200
208
  def do_render
201
209
  # cleanup options from empty values
202
- @options.select! do |k, v|
210
+ html_attr.select! do |k, v|
203
211
  !v.nil? && (!v.respond_to?(:empty?) || !v.empty?)
204
212
  end
205
213
  @rendered = render_sections
206
214
  run_callbacks :render do
207
- @rendered = content_tag(@tag, @rendered, @options)
215
+ options = html_attr.merge(class: html_class.to_html, data: html_data)
216
+ @rendered = content_tag(tag, @rendered, options)
208
217
  end
209
218
  end
210
219
 
@@ -5,38 +5,57 @@ module WrapIt
5
5
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
6
  #
7
7
  module Callbacks
8
+ # Documentation includes
9
+ # @!parse extend Callbacks::ClassMethods
10
+
11
+ # module implementation
12
+
8
13
  extend DerivedAttributes
9
14
 
15
+ #
10
16
  def self.included(base)
11
17
  base.extend ClassMethods
12
18
  end
13
19
 
20
+ #
21
+ # Runs specified callbacks with block
22
+ #
23
+ # Runs first `before` callbacks in inheritance order, then yields block if
24
+ # it given and then `after` callbacks in reverse order.
25
+ #
26
+ # @param name [Symbol] callback name, that should be defined by
27
+ # {ClassMethods#callback callback} method.
28
+ #
29
+ # @return [void]
14
30
  def run_callbacks(name)
15
31
  self.class.collect_derived("@before_#{name}").each do |cb|
16
32
  if cb.is_a?(Symbol)
17
- # break if send(cb) == false # if respond_to?(cb)
18
33
  send(cb) # if respond_to?(cb)
19
34
  else
20
- # break if instance_eval(&cb) == false
21
35
  instance_eval(&cb)
22
36
  end
23
37
  end
24
38
  yield if block_given?
25
39
  self.class.collect_derived("@after_#{name}").reverse.each do |cb|
26
40
  if cb.is_a?(Symbol)
27
- # break if send(cb) == false # if respond_to?(cb)
28
41
  send(cb) # if respond_to?(cb)
29
42
  else
30
- # break if instance_eval(&cb) == false
31
43
  instance_eval(&cb)
32
44
  end
33
45
  end
34
46
  end
35
47
 
36
48
  #
37
- # Class methods to include
49
+ # {Callbacks} class methods
38
50
  #
39
51
  module ClassMethods
52
+ #
53
+ # Defines callback
54
+ #
55
+ # @overload callback([name, ...])
56
+ # @param name [Symbol, String] callback name
57
+ #
58
+ # @return [void]
40
59
  def callback(*args)
41
60
  args.each do |name|
42
61
  instance_eval(&Callbacks.define_callback(:before, name))
@@ -0,0 +1,140 @@
1
+ module WrapIt
2
+ #
3
+ # Adds #capture! and #capture_first! methods to array. Theese methods are
4
+ # extracts items from array by some conditions and returns its as separate
5
+ # array for #capture! and as first item for #capture_first!.
6
+ #
7
+ # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
8
+ #
9
+ module CaptureArray
10
+ REQUIRED_METHODS = %i(reject! find_index delete_at)
11
+
12
+ #
13
+ def self.included(base)
14
+ methods = base.methods
15
+ # avoid including in classes thats doen't have methods, used in
16
+ # inplementation
17
+ REQUIRED_METHODS.all? { |m| methods.include?(m) } || fail(
18
+ TypeError,
19
+ "#{self.class.name} can't be included into #{base.class.name}"
20
+ )
21
+ end
22
+
23
+ #
24
+ # Extracts elements from array by conditions, passed in arguments and
25
+ # returns theese elements as new array.
26
+ #
27
+ # Condition can be Regexp, Class, Array, lambdas and any other value.
28
+ # if condition contains labdas, all off them will be called before
29
+ # tests and results of theese calls will be used as conditions.
30
+ #
31
+ # If condition is `Regexp`, all elements of array are tested for matching
32
+ # to this regexp, previously converted to String by their `to_s` method. If
33
+ # condition is an `Array`, all elements tested if it included in these
34
+ # array. If the condition is a class, then elements are tested via `is_a?`
35
+ # method for this class. `true` and `false` conditions do exactly what it
36
+ # mean - `true` will satisfy condition, `false` will not. For any other
37
+ # value, elements are tested with equality operator `==`.
38
+ #
39
+ # You can provide a block. In this case, all arguments are ignored, and
40
+ # block yielded for each element of array. If block returns `true`,
41
+ # element extracted from array.
42
+ #
43
+ # All conditions, passed as arguments are `or`-ed so `String, Symbol` means
44
+ # select Symbol or String elements.
45
+ #
46
+ # You can also specify `and` option, so all tests will be and'ed with its
47
+ # conditions.
48
+ #
49
+ # @overload capture!([condition, ...], opts = {})
50
+ # @param condition [Object] one of `or`-ed conditions for comparing
51
+ # @param opts [Hash] options for extracting
52
+ # @option opts [Object, Array] :and one or array of `and`-ed conditions
53
+ #
54
+ # @overload capture!(&block)
55
+ # @yield [element] Gives each element of array to block. You should return
56
+ # `true` to capture this element or `false` to keep it in array.
57
+ # @yieldparam [Object] element element of array to inspect
58
+ # @yieldreturn [Boolean] whether exclude this element or not
59
+ #
60
+ # @return [Array] array of captured elements
61
+ #
62
+ # @example capture by class
63
+ # arr = [1, 2, 3, 'and', 'string']
64
+ # arr.extend WrapIt::CaptureArray
65
+ # arr.capture(String) #=> ['and', 'string']
66
+ # arr #=> [1, 2, 3]
67
+ #
68
+ # @example capture by value
69
+ # arr = [1, 2, 3, 'and', 'string']
70
+ # arr.extend WrapIt::CaptureArray
71
+ # arr.capture(1, 2) #=> [1, 2]
72
+ # arr #=> [3, 'and', 'string']
73
+ #
74
+ # @example capture by Regexp
75
+ # arr = [1, 2, 3, 'and', 'string', :str]
76
+ # arr.extend WrapIt::CaptureArray
77
+ # arr.capture(/^str/) #=> ['string', :str]
78
+ # arr #=> [1, 2, 3, 'and']
79
+ #
80
+ # @example capture by Array
81
+ # arr = [1, 2, 3, 'and', 'string']
82
+ # arr.extend WrapIt::CaptureArray
83
+ # arr.capture([1, 10, 'and']) #=> [1, 'and']
84
+ # arr #=> [2, 3, 'string']
85
+ #
86
+ # @example capture by block
87
+ # arr = [1, 2, 3, 'and', 'string']
88
+ # arr.extend WrapIt::CaptureArray
89
+ # arr.capture {|x| x < 3} #=> [1, 2]
90
+ # arr #=> [3, 'and', 'string']
91
+ #
92
+ # @example capture with `and` condition
93
+ # arr = [1, 2, 3, 'and', 'string', :str]
94
+ # arr.extend WrapIt::CaptureArray
95
+ # arr.capture(String, and: [/^str/]) #=> ['string']
96
+ # arr #=> [1, 2, 3, 'and', :str]
97
+ def capture!(*args, &block)
98
+ captureed = []
99
+ reject! do |arg|
100
+ do_compare(arg, *args, &block) && (captureed << arg) && true
101
+ end
102
+ captureed
103
+ end
104
+
105
+ #
106
+ # Extracts first element from array that is satisfy conditions, passed in
107
+ # arguments and returns these element.
108
+ #
109
+ # @see #capture!
110
+ def capture_first!(*args, &block)
111
+ index = find_index { |arg| do_compare(arg, *args, &block) }
112
+ index.nil? ? nil : delete_at(index)
113
+ end
114
+
115
+ private
116
+
117
+ def do_compare(target, *compare_args, &block)
118
+ if block_given?
119
+ yield target
120
+ else
121
+ options = compare_args.extract_options!
122
+ compare_args.map! { |x| x.is_a?(Proc) && x.lambda? ? x.call : x }
123
+ result = compare_args.any? do |dest|
124
+ case
125
+ when dest == true || dest == false then dest
126
+ when dest.is_a?(Array) then dest.include?(target)
127
+ when dest.is_a?(Regexp) then dest.match(target.to_s)
128
+ when dest.is_a?(Class) then target.is_a?(dest)
129
+ when dest.is_a?(Proc) then dest.call(dest) == true
130
+ else dest == target
131
+ end
132
+ end
133
+ if options[:and].is_a?(Array)
134
+ result &&= do_compare(target, *options[:and])
135
+ end
136
+ result
137
+ end
138
+ end
139
+ end
140
+ end
@@ -4,7 +4,8 @@ module WrapIt
4
4
  #
5
5
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
6
  #
7
- # TODO: single_child
7
+ # @todo single_child realization
8
+ # @todo refactor code for more clearness
8
9
  class Container < Base
9
10
  switch :deffered_render do |_|
10
11
  # avoid changing deffered_render after any child added
@@ -15,27 +16,64 @@ module WrapIt
15
16
  end
16
17
  end
17
18
 
19
+ # list of children elements
18
20
  attr_reader :children
21
+
22
+ # children can be extracted from normal template flow and rendered in
23
+ # separate section.
19
24
  attr_writer :extract_children
25
+
20
26
  section :children
21
27
 
22
28
  def extract_children?
23
29
  @extract_children == true
24
30
  end
25
31
 
26
- after_initialize do
32
+ before_initialize do
27
33
  @children = []
28
- self.class.extract_from_options.each do |option, name|
29
- args = options.delete(option)
30
- next if args.nil?
31
- args = [args] unless args.is_a?(Array)
32
- self.deffered_render = true
33
- send(name, *args)
34
- end
35
34
  end
36
35
 
37
36
  #
38
- # Defines child elements helper for creation of child items.
37
+ # Defines helper for child elements creation.
38
+ #
39
+ # @example simple usage
40
+ # class Item < WrapIt::Base
41
+ # include TextContainer
42
+ # end
43
+ #
44
+ # class List < WrapIt::Container
45
+ # default_tag 'ul'
46
+ # child :item, tag: 'li'
47
+ # end
48
+ #
49
+ # list = List.new(template)
50
+ # list.item 'list item 1'
51
+ # list.item 'list item 2'
52
+ # list.render # => '<ul><li>list item 1'</li><li>list item 2</li></ul>'
53
+ #
54
+ # @example with option
55
+ # class Button < WrapIt::Container
56
+ # include TextContainer
57
+ # html_class 'btn'
58
+ # child :icon, tag: 'i', option: true
59
+ # end
60
+ #
61
+ # btn = Button.new(template, 'Home', icon: { class: 'i-home' })
62
+ # btn.render # => '<div class="btn">Home<i class="i-home"></i></div>'
63
+ #
64
+ # @overload child(name, class_name = nil, [args, ...], opts = {}, &block)
65
+ # @param name [Symbol, String] helper method name
66
+ # @param class_name [String, Base] class for child elements. If ommited
67
+ # WrapIt::Base will be used
68
+ # @param args [Object] any arguments that will be passed to child
69
+ # element constructor
70
+ # @param opts [Hash] options
71
+ # @option opts [true, Symbol] :option if specified, child can be created
72
+ # via option with same name (if :option is true) or with specified
73
+ # name
74
+ # @option opts [Symbol] :section section to that this children will be
75
+ # rendered. By default children rendered to `children`. Refer to
76
+ # {Sections} module for details.
39
77
  #
40
78
  # @return [String]
41
79
  def self.child(name, *args, &block)
@@ -48,8 +86,11 @@ module WrapIt
48
86
  'WrapIt::Base'
49
87
  end
50
88
  child_class = child_class.name if child_class.is_a?(Class)
51
- @helpers ||= []
52
- @helpers << name
89
+
90
+ opts = args.extract_options!
91
+ extract = opts.delete(:option)
92
+ args << opts
93
+
53
94
  define_method name do |*helper_args, &helper_block|
54
95
  # We should clone arguments becouse if we have loop in template,
55
96
  # `extract_options!` below works only for first iterration
@@ -60,20 +101,21 @@ module WrapIt
60
101
  helper_args += default_args + [options]
61
102
  add_children(name, child_class, block, *helper_args, &helper_block)
62
103
  end
63
- end
64
104
 
65
- def self.extract_from_options(*args)
66
- return @extract_from_options || [] if args.size == 0
67
- hash = args.extract_options!
68
- args.size.odd? && fail(ArgumentError, 'odd arguments number')
69
- args.each_with_index { |arg, i| i.even? && hash[arg] = args[i + 1] }
70
- @helpers ||= []
71
- hash.symbolize_keys!
72
- @extract_from_options = Hash[
73
- hash.select do |k, v|
74
- (v.is_a?(String) || v.is_a?(Symbol)) && @helpers.include?(k)
75
- end.map { |k, v| [k, v.to_sym] }
76
- ]
105
+ unless extract.nil?
106
+ extract.is_a?(Array) || extract = [extract]
107
+ extract.each do |opt_name|
108
+ opt_name = name if opt_name == true
109
+ option(opt_name) do |_, arguments|
110
+ self.deffered_render = true
111
+ arguments.is_a?(Array) || arguments = [arguments]
112
+ o = arguments.extract_options!
113
+ o.merge!(extracted: true)
114
+ arguments << o
115
+ send name, *arguments
116
+ end
117
+ end
118
+ end
77
119
  end
78
120
 
79
121
  after_capture do
@@ -106,6 +148,7 @@ module WrapIt
106
148
  def add_children(name, helper_class, class_block, *args, &helper_block)
107
149
  options = args.extract_options!
108
150
  section = options.delete(:section) || :children
151
+ extracted = options.delete(:extracted) == true
109
152
  args << options
110
153
  item = Object
111
154
  .const_get(helper_class)
@@ -120,6 +163,7 @@ module WrapIt
120
163
  class_block.nil? || instance_exec(item, &class_block)
121
164
 
122
165
  deffered_render? && @children << item
166
+ return if extracted
123
167
  if !deffered_render? && (omit_content? || extract_children?)
124
168
  self[section] << capture { item.render }
125
169
  end