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/link.rb CHANGED
@@ -1,6 +1,22 @@
1
1
  module WrapIt
2
2
  #
3
- # Link
3
+ # HTML link element
4
+ #
5
+ # You can specify link by `link`, `href` or `url` option or by first String
6
+ # argument. Also includes {TextContainer} module, so you can specify link
7
+ # body with `text` or `body` option or by second String argument or inside
8
+ # block.
9
+ #
10
+ # @example usage
11
+ # link = WrapIt::Link.new(template, 'http://some.url', 'text')
12
+ # link.render # => '<a href="http://some.url">test</a>'
13
+ # link = WrapIt::Link.new(template, link: 'http://some.url', text: 'text')
14
+ # link.render # => '<a href="http://some.url">test</a>'
15
+ # link = WrapIt::Link.new(template, 'text', link: http://some.url')
16
+ # link.render # => '<a href="http://some.url">test</a>'
17
+ #
18
+ # @example in template
19
+ # <%= link 'http://some.url' do %>text<% end %>
4
20
  #
5
21
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
22
  #
@@ -9,33 +25,40 @@ module WrapIt
9
25
 
10
26
  default_tag 'a'
11
27
 
28
+ option :link, if: %i(link href url)
29
+
30
+ # extract first string argument as link only if it not specified in options
31
+ argument(:link, first_only: true, after_options: true,
32
+ if: String, and: ->{ !option_provided?(:link, :href, :url) }
33
+ ) do |_, v|
34
+ self.href = v
35
+ end
36
+
37
+ #
38
+ # Retrieves current link
39
+ #
40
+ # @return [String] link
12
41
  def href
13
- @options[:href]
42
+ html_attr[:href]
14
43
  end
15
44
 
45
+ #
46
+ # Sets link
47
+ # @param value [String] link
48
+ #
49
+ # @return [String] setted link
16
50
  def href=(value)
17
51
  if value.is_a?(Hash)
18
- defined?(Rails) || fail(
52
+ WrapIt.rails? || fail(
19
53
  ArgumentError,
20
54
  'Hash links supported only in Rails env'
21
55
  )
22
56
  value = @template.url_for(value)
23
57
  end
24
58
  value.is_a?(String) || fail(ArgumentError, 'Wrong link type')
25
- @options[:href] = value
26
- end
27
-
28
- before_initialize do
29
- link = @options[:link] || @options[:href] || @options[:url]
30
- @options.delete(:link)
31
- @options.delete(:href)
32
- @options.delete(:url)
33
- unless link.is_a?(String) || link.is_a?(Hash)
34
- @block.nil? && tmp = @arguments.extract_first!(String)
35
- link = @arguments.extract_first!(String)
36
- tmp.nil? || @arguments.unshift(tmp)
37
- end
38
- link.nil? || self.href = link
59
+ html_attr[:href] = value
39
60
  end
61
+ alias_method :link=, :href=
62
+ alias_method :url=, :href=
40
63
  end
41
64
  end
@@ -1,12 +1,60 @@
1
1
  module WrapIt
2
2
  #
3
- # Adds sections functionality
3
+ # Sections is a smart way to make complex components with inheritance.
4
+ #
5
+ # Sections is just array of named HTML markup pieces. You can place any
6
+ # section before or after another at class level and change their content
7
+ # at instance level.
8
+ #
9
+ # Each component have three stages. First is initialization, then sections
10
+ # capturing and rendering. You can change any sections content until
11
+ # rendering stage begins. Finally, renderer joins all sections in order,
12
+ # that they have at render time.
13
+ #
14
+ # {WrapIt::Base} provides following sections: main section is `:content`.
15
+ # All text from block captured there. `:render_arguments` and `:render_block`
16
+ # also provided, so arguments and block passed to render method captured
17
+ # here.
18
+ #
19
+ # Access to sections at instance level performed throw hash-like getter and
20
+ # setter ([] and []=) of self.
21
+ #
22
+ # With this functionality you can easy organize you inheritance, so any
23
+ # descendant can change sections order or any section content without
24
+ # changes to unrelated sections.
25
+ #
26
+ # @example sections usage
27
+ # class IconedButton < WrapIt::Base
28
+ # include TextContainer
29
+ # html_class 'btn'
30
+ # section :icon
31
+ # place :icon, before: :content
32
+ #
33
+ # after_capture do
34
+ # self[:icon] = html_safe('<i class="my-icon"></i>')
35
+ # end
36
+ # end
37
+ #
38
+ # class RightIconedButton < IconedButton
39
+ # place :icon, after: :content
40
+ # end
41
+ #
42
+ # b1 = IconedButton.new(template, 'text')
43
+ # b2 = RightIconedButton.new(template, 'text')
44
+ # b1.render # => '<div class="btn"><i class="my-icon"></i>text</div>'
45
+ # b2.render # => '<div class="btn">text<i class="my-icon"></i></div>'
4
46
  #
5
47
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
48
  #
7
49
  module Sections
50
+ # Documentation includes
51
+ # @!parse extend Sections::ClassMethods
52
+
53
+ # module implementation
54
+
8
55
  extend DerivedAttributes
9
56
 
57
+ #
10
58
  def self.included(base)
11
59
  base == Base || fail(
12
60
  TypeError,
@@ -15,6 +63,11 @@ module WrapIt
15
63
  base.extend ClassMethods
16
64
  end
17
65
 
66
+ #
67
+ # Retrieves specified section content
68
+ # @param name [Symbol] section name
69
+ #
70
+ # @return [String] section content
18
71
  def [](name)
19
72
  @section_names ||= self.class.sections
20
73
  return nil unless @section_names.include?(name)
@@ -22,6 +75,12 @@ module WrapIt
22
75
  @sections[name] ||= empty_html
23
76
  end
24
77
 
78
+ #
79
+ # Sets specified section content
80
+ # @param name [Symbol] section name
81
+ # @param value [String] content
82
+ #
83
+ # @return [String] section content
25
84
  def []=(name, value)
26
85
  @section_names ||= self.class.sections
27
86
  return unless @section_names.include?(name)
@@ -30,13 +89,24 @@ module WrapIt
30
89
  end
31
90
 
32
91
  #
33
- # Class methods to include
92
+ # {Sections} class methods
34
93
  #
35
94
  module ClassMethods
95
+ #
96
+ # Retrieves all sections, including ancestors
97
+ #
98
+ # @return [Array<Symbol>] array of sections
36
99
  def sections
37
100
  collect_derived(:@sections)
38
101
  end
39
102
 
103
+ #
104
+ # Defines new section or sections. Places its to end of section list
105
+ #
106
+ # @overload section([name, ...])
107
+ # @param name [Symbol, String] section name
108
+ #
109
+ # @return [void]
40
110
  def section(*args)
41
111
  @sections ||= []
42
112
  args.flatten.each do |name|
@@ -49,6 +119,10 @@ module WrapIt
49
119
  end
50
120
  end
51
121
 
122
+ #
123
+ # Retrieves section names in current order
124
+ #
125
+ # @return [Array<Symbol>] ordered sections array
52
126
  def placement
53
127
  @placement ||=
54
128
  if self == Base
@@ -59,6 +133,20 @@ module WrapIt
59
133
  end
60
134
  end
61
135
 
136
+ #
137
+ # Places specific section in specified place
138
+ #
139
+ # @overload place(src, to)
140
+ # @param src [Symbol] section name to place
141
+ # @param to [Hash] single key-value hash. Key can be `:before` or
142
+ # `after`, value can be `:begin`, `:end` or section name
143
+ #
144
+ # @overload place(src, at, dst)
145
+ # @param src [Symbol] section name to place
146
+ # @param at [Symbol] can be `:before` or `:after`
147
+ # @param dst [Symbol] can be `:begin`, `:end` or section name
148
+ #
149
+ # @return [void]
62
150
  def place(src, at, dst = nil)
63
151
  if dst == nil && at.is_a?(Hash) && at.keys.size == 1
64
152
  dst = at.values[0]
@@ -5,40 +5,28 @@ module WrapIt
5
5
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
6
  #
7
7
  module Switches
8
+ # Documentation includes
9
+ # @!parse extend Switches::ClassMethods
10
+
11
+ # module implementation
12
+
8
13
  extend DerivedAttributes
9
14
 
15
+ #
10
16
  def self.included(base)
11
17
  base == Base || fail(
12
18
  TypeError,
13
19
  "#{self.class.name} can be included only into WrapIt::Base"
14
20
  )
15
21
  base.extend ClassMethods
16
- base.after_initialize :switches_init
17
- end
18
-
19
- private
20
-
21
- def switches_init
22
- @switches = {}
23
- keys = switches.keys
24
- @options.keys.select { |o| keys.include?(o) }.each do |switch|
25
- send("#{switches[switch][:name]}=", @options.delete(switch) == true)
26
- end
27
- @arguments.extract!(Symbol, and: [keys]).each do |switch|
28
- send("#{switches[switch][:name]}=", true)
29
- end
30
- end
31
-
32
- def switches
33
- @switches_hash ||= self.class.collect_derived(:@switches, {}, :merge)
22
+ base.before_initialize { @switches = {} }
34
23
  end
35
24
 
36
25
  #
37
- # Class methods to include
26
+ # {Switches} class methods
38
27
  #
39
28
  module ClassMethods
40
29
  #
41
- # @dsl
42
30
  # Adds `switch`. Switch is a boolean flag. When element created, creation
43
31
  # arguments will be scanned for `Symbol`, that equals to `name`. If
44
32
  # it founded, switch turned on. Also creation options inspected. If
@@ -57,11 +45,11 @@ module WrapIt
57
45
  # on or removed in other case.
58
46
  #
59
47
  # @param name [String, Symbol] Switch name. Converted to `Symbol`.
60
- # @param opts [Hash] Switch options
61
- # @options opts [true, String, Symbol, Array<String, Symbol>] :html_class
48
+ # @param options [Hash] Switch options
49
+ # @option options [true, String, Symbol, Array<String, Symbol>] :html_class
62
50
  # HTML classes list that will automatically added to element if switch
63
51
  # is on or removed from element if switch id off.
64
- # @options opts [Symbol, Array<Symbol>] :aliases list of aliases.
52
+ # @option options [Symbol, Array<Symbol>] :aliases list of aliases.
65
53
  # Warning! Values are not converted - pass only `Symbols` here.
66
54
  # @yield [state] Runs block when switch state changed, gives it to block.
67
55
  # @yieldparam state [Boolean] Whether switch is on or off.
@@ -83,11 +71,28 @@ module WrapIt
83
71
  end
84
72
  end
85
73
  end
86
- names = [name] + [[options[:aliases]] || []].flatten.compact
74
+
87
75
  define_method("#{name}?") { @switches[name] == true }
88
76
  define_method("#{name}=", &Switches.setter(name, &block))
89
77
  @switches ||= {}
90
- names.each { |n| @switches[n] = options }
78
+
79
+ @switches[name] = options
80
+
81
+ o_params = {}
82
+ a_params = { if: Symbol, and: name }
83
+ if options.key?(:aliases)
84
+ aliases = [options[:aliases]].flatten.compact
85
+ o_params[:if] = [name] + aliases
86
+ a_params[:and] = [name] + aliases
87
+ end
88
+
89
+ option(name, **o_params) do |_, v|
90
+ send("#{options[:name]}=", v == true)
91
+ end
92
+
93
+ argument(name, **a_params) do |_, _|
94
+ send("#{options[:name]}=", true)
95
+ end
91
96
  end
92
97
  end
93
98
 
@@ -96,19 +101,18 @@ module WrapIt
96
101
  #
97
102
  # Makes switch setter block
98
103
  # @param name [String] switch name
99
- # @param &block [Proc] switch block
100
104
  #
101
105
  # @return [Proc] switch setter block
102
106
  def self.setter(name, &block)
103
107
  proc do |value|
104
- opts = switches[name]
108
+ opts = self.class.collect_derived(:@switches, {}, :merge)[name]
105
109
  cb_return = block.nil? || instance_exec(value == true, &block)
106
110
  unless cb_return == false
107
111
  @switches[name] = value == true
108
112
  if value == true
109
- opts.key?(:html_class) && add_html_class(*opts[:html_class])
113
+ opts.key?(:html_class) && html_class << opts[:html_class]
110
114
  else
111
- opts.key?(:html_class) && remove_html_class(*opts[:html_class])
115
+ opts.key?(:html_class) && html_class.delete(*opts[:html_class])
112
116
  end
113
117
  end
114
118
  end
@@ -1,30 +1,103 @@
1
1
  module WrapIt
2
2
  #
3
- # TextContainer
3
+ # Provides functionality for text-contained components.
4
+ #
5
+ # Text can be captured from `:text` or `:body` option, or as first unparsed
6
+ # String argument, or in block, provided to constructor.
7
+ #
8
+ # If block given, text will be captured from it in priority, so String
9
+ # arguments and options will not parsed. You can cancel this manner by
10
+ # calling {ClassMethods#text_in_block text_in_block(false)} method.
11
+ #
12
+ # This module adds `body` section before base `content` section.
4
13
  #
5
14
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
15
  #
7
16
  module TextContainer
17
+ #
8
18
  def self.included(base)
9
19
  base.class_eval do
20
+ extend ClassMethods
21
+
10
22
  default_tag 'p', false
11
23
 
24
+ option(:body, if: %i(body text)) { |_, v| body << v }
25
+ argument(:body, first_only: true, after_options: true,
26
+ if: String,
27
+ and: ->{ !block_provided? || !text_in_block? }) do |_, v|
28
+ self.class.html_safe? && v = html_safe(v)
29
+ body << v
30
+ end
31
+
12
32
  section :body
13
33
  place :body, :before, :content
14
34
 
15
- after_initialize do
16
- @body = @arguments.extract_first!(String) || empty_html
17
- @body += @options[:body] || @options[:text] || empty_html
18
- @options.delete(:body)
19
- @options.delete(:text)
20
- end
21
-
22
35
  after_capture do
23
- self[:body] = html_safe(@body) unless @body.nil?
36
+ self[:body] = html_safe(@body) unless @body.nil? || @body.empty?
24
37
  end
25
38
  end
26
39
  end
27
40
 
28
- attr_accessor :body
41
+ #
42
+ # Retrieves body text
43
+ #
44
+ # @return [String] text
45
+ def body
46
+ @body ||= empty_html
47
+ end
48
+
49
+ module ClassMethods
50
+ #
51
+ # Sets priotiy of text source
52
+ #
53
+ # @param value [Boolean] `true` means if block present - text will
54
+ # be captured from there. `false` means first to inspect arguments and
55
+ # options and if it ommited retirieve text from block.
56
+ #
57
+ # @return [Boolean] current value
58
+ def text_in_block(value = nil)
59
+ if value.nil?
60
+ @text_in_block.nil? && @text_in_block = true
61
+ @text_in_block
62
+ else
63
+ @text_in_block = value == true
64
+ end
65
+ end
66
+
67
+ #
68
+ # Retrieves block priority
69
+ #
70
+ # @return [Boolean] current value
71
+ def text_in_block?
72
+ @text_in_block.nil? && @text_in_block = true
73
+ @text_in_block
74
+ end
75
+
76
+ #
77
+ # Sets whether text from arguments are html-safe
78
+ # @param value [Boolean] `true` means that text from arguments have
79
+ # proper markup and component will mark it as save via html_safe
80
+ # method. `flase` means, that this values can contain unsafe content,
81
+ # so user should make html-safe string by itself.
82
+ #
83
+ # @return [Boolean] current value
84
+ def html_safe(value = nil)
85
+ if value.nil?
86
+ @html_safe.nil? && @html_safe = true
87
+ @html_safe
88
+ else
89
+ @html_safe = value == true
90
+ end
91
+ end
92
+
93
+ #
94
+ # Retrieves whether text from attributes are html-safe
95
+ #
96
+ # @return [Boolean] current value
97
+ def html_safe?
98
+ @html_safe.nil? && @html_safe = true
99
+ @html_safe
100
+ end
101
+ end
29
102
  end
30
103
  end