wrap_it 0.1.5 → 0.2.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +17 -11
  3. data/.rspec +2 -0
  4. data/Gemfile +0 -3
  5. data/Gemfile.rails4 +5 -0
  6. data/Gemfile.sinatra +5 -0
  7. data/README.md +138 -24
  8. data/Rakefile +14 -1
  9. data/lib/wrap_it/base.rb +47 -20
  10. data/lib/wrap_it/callbacks.rb +10 -5
  11. data/lib/wrap_it/container.rb +91 -38
  12. data/lib/wrap_it/enums.rb +1 -1
  13. data/lib/wrap_it/frameworks.rb +24 -0
  14. data/lib/wrap_it/helpers.rb +74 -0
  15. data/lib/wrap_it/module_helpers.rb +23 -0
  16. data/lib/wrap_it/sections.rb +81 -0
  17. data/lib/wrap_it/switches.rb +11 -10
  18. data/lib/wrap_it/text_container.rb +6 -1
  19. data/lib/wrap_it/version.rb +1 -1
  20. data/lib/wrap_it.rb +9 -63
  21. data/log/development.log +174 -0
  22. data/sections_explained.md +70 -0
  23. data/spec/frameworks/log/development.log +1633 -0
  24. data/spec/frameworks/rails_app.rb +61 -0
  25. data/spec/frameworks/sinatra_app.rb +32 -0
  26. data/spec/{rails → integration}/base_spec.rb +7 -7
  27. data/spec/integration/container_spec.rb +92 -0
  28. data/spec/integration/examples_spec.rb +54 -0
  29. data/spec/integration/text_container_spec.rb +13 -0
  30. data/spec/lib/base_spec.rb +0 -6
  31. data/spec/lib/container_spec.rb +1 -6
  32. data/spec/lib/helpers_spec.rb +26 -0
  33. data/spec/lib/html_class_spec.rb +2 -2
  34. data/spec/lib/sections_spec.rb +72 -0
  35. data/spec/lib/text_container_spec.rb +19 -0
  36. data/spec/spec_helper.rb +8 -0
  37. data/spec/support/example_groups/integration_example_group.rb +72 -0
  38. data/spec/support/example_groups/wrap_it_example_group.rb +6 -4
  39. data/wrap_it.gemspec +7 -4
  40. metadata +49 -25
  41. data/spec/internal/log/test.log +0 -619
  42. data/spec/rails/container_spec.rb +0 -30
  43. data/spec/support/example_groups/rails_example_group.rb +0 -79
@@ -4,56 +4,97 @@ module WrapIt
4
4
  #
5
5
  # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
6
  #
7
+ # TODO: single_child
7
8
  class Container < Base
8
- switch :deffered_render
9
+ switch :deffered_render do |_|
10
+ # avoid changing deffered_render after any child added
11
+ if @children.is_a?(Array)
12
+ @children.empty?
13
+ else
14
+ true
15
+ end
16
+ end
9
17
 
10
- after_initialize do
11
- @children = deffered_render? ? [] : empty_html
18
+ attr_reader :children
19
+ attr_writer :extract_children
20
+ section :children
21
+
22
+ def extract_children?
23
+ @extract_children == true
12
24
  end
13
25
 
14
- def self.child(*args, &block)
15
- create_args = args.last.is_a?(Array) ? args.pop : []
16
- klass = args.pop
17
- klass.is_a?(Class) && klass = klass.name
18
- unless klass.is_a?(String)
19
- args.push(klass)
20
- klass = 'WrapIt::Base'
26
+ after_initialize do
27
+ @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)
21
34
  end
22
- args.select! { |n| n.is_a?(Symbol) }
23
- args.size > 0 || fail(ArgumentError, 'No valid method names given')
24
- args.each do |method|
25
- define_method method do |*helper_args, &helper_block|
26
- # We should clone arguments becouse if we have loop in template,
27
- # `extract_options!` below works only for first iterration
28
- default_args = create_args.clone
29
- options = helper_args.extract_options!
30
- options[:helper_name] = method
31
- options.merge!(default_args.extract_options!)
32
- helper_args += default_args + [options]
33
- add_children(klass, block, *helper_args, &helper_block)
35
+ end
36
+
37
+ #
38
+ # Defines child elements helper for creation of child items.
39
+ #
40
+ # @return [String]
41
+ def self.child(name, *args, &block)
42
+ name.is_a?(String) && name.to_sym
43
+ name.is_a?(Symbol) || fail(ArgumentError, 'Wrong child name')
44
+ child_class =
45
+ if args.first.is_a?(String) || args.first.is_a?(Class)
46
+ args.shift
47
+ else
48
+ 'WrapIt::Base'
34
49
  end
50
+ child_class = child_class.name if child_class.is_a?(Class)
51
+ @helpers ||= []
52
+ @helpers << name
53
+ define_method name do |*helper_args, &helper_block|
54
+ # We should clone arguments becouse if we have loop in template,
55
+ # `extract_options!` below works only for first iterration
56
+ default_args = args.clone
57
+ options = helper_args.extract_options!
58
+ options[:helper_name] = name
59
+ options.merge!(default_args.extract_options!)
60
+ helper_args += default_args + [options]
61
+ add_children(name, child_class, block, *helper_args, &helper_block)
35
62
  end
36
63
  end
37
64
 
38
- # protected
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
+ ]
77
+ end
39
78
 
40
79
  after_capture do
41
80
  if deffered_render?
42
- html = Hash[@children.map { |c| [c.object_id, capture { c.render }] }]
43
- if omit_content?
44
- @content = html.values.reduce(empty_html) { |a, e| a << e }
45
- else
46
- safe = html_safe?(@content)
47
- @content = @content
81
+ html = Hash[children.map { |c| [c.object_id, capture { c.render }] }]
82
+ unless omit_content? || extract_children?
83
+ safe = html_safe?(self[:content])
84
+ self[:content] = self[:content]
48
85
  .split(CONTENT_SPLIT_REGEXP)
49
86
  .reduce(empty_html) do |a, e|
50
87
  match = CONTENT_REPLACE_REGEXP.match(e)
51
- safe || e = html_safe(e)
52
- a << match.nil? ? e : html[match[:obj_id].to_i(16)]
88
+ safe && e = html_safe(e)
89
+ str = match.nil? ? e : html.delete(match[:obj_id].to_i(16))
90
+ a << (str || empty_html)
53
91
  end
54
92
  end
55
- else
56
- omit_content? && @content = @children
93
+ # finally add all elements, not captured from markup
94
+ html.each do |id, str|
95
+ obj = ObjectSpace._id2ref(id)
96
+ obj.nil? || self[obj.render_to] << str
97
+ end
57
98
  end
58
99
  end
59
100
 
@@ -62,21 +103,33 @@ module WrapIt
62
103
  CONTENT_SPLIT_REGEXP = /(<!-- WrapIt::Container\(\h+\) -->)/
63
104
  CONTENT_REPLACE_REGEXP = /\A<!-- WrapIt::Container\((?<obj_id>\h+)\) -->\z/
64
105
 
65
- def add_children(helper_class, class_block, *args, &helper_block)
106
+ def add_children(name, helper_class, class_block, *args, &helper_block)
107
+ options = args.extract_options!
108
+ section = options.delete(:section) || :children
109
+ args << options
66
110
  item = Object
67
111
  .const_get(helper_class)
68
112
  .new(@template, *args, &helper_block)
113
+ item.instance_variable_set(:@render_to, section)
114
+ item.instance_variable_set(:@parent, self)
115
+ item.define_singleton_method(:render_to) { @render_to }
116
+ item.define_singleton_method(:render_to=) do |value|
117
+ self.class.sections.include?(value) && @render_to = value
118
+ end
119
+ item.define_singleton_method(:parent) { @parent }
69
120
  class_block.nil? || instance_exec(item, &class_block)
70
121
 
71
- item = item.render unless deffered_render?
72
- @children << item if deffered_render? || omit_content?
73
- if omit_content?
122
+ deffered_render? && @children << item
123
+ if !deffered_render? && (omit_content? || extract_children?)
124
+ self[section] << capture { item.render }
125
+ end
126
+ if omit_content? || extract_children?
74
127
  empty_html
75
128
  else
76
129
  if deffered_render?
77
130
  html_safe("<!-- WrapIt::Container(#{item.object_id.to_s(16)}) -->")
78
131
  else
79
- item
132
+ item.render
80
133
  end
81
134
  end
82
135
  end
data/lib/wrap_it/enums.rb CHANGED
@@ -61,7 +61,7 @@ module WrapIt
61
61
  # This method also adds getter and setter for this enum.
62
62
  #
63
63
  # @param name [String, Symbol] Enum name. Converted to `Symbol`.
64
- # @param options = {} [Hash] Enum options
64
+ # @param options [Hash] Enum options
65
65
  # @options options [String, Symbol] :html_class_prefix prefix of HTML
66
66
  # class that will automatically added to element if enum changes its
67
67
  # value.
@@ -0,0 +1,24 @@
1
+ #
2
+ # Framework detection methods
3
+ #
4
+ module WrapIt
5
+ def self.framework
6
+ return @framework unless @framework.nil?
7
+ gems = Gem.loaded_specs.keys
8
+ if gems.include?('rails')
9
+ @framework = :rails
10
+ elsif gems.include?('sinatra')
11
+ @framework = :sinatra
12
+ else
13
+ @framework = :unknown
14
+ end
15
+ end
16
+
17
+ def self.rails?
18
+ framework == :rails
19
+ end
20
+
21
+ def self.sinatra?
22
+ framework == :sinatra
23
+ end
24
+ end
@@ -0,0 +1,74 @@
1
+ #
2
+ # Helper registering
3
+ #
4
+ # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
5
+ #
6
+ module WrapIt
7
+ def self.register_module(*args)
8
+ options = args.extract_options!
9
+ options.symbolize_keys!
10
+ options = {prefix: ''}.merge(options)
11
+ mod = args.shift
12
+ mod.is_a?(Module) || mod = Module.new
13
+ mod.instance_eval do
14
+ define_singleton_method(:register, &WrapIt.register_block(options))
15
+ define_singleton_method(:unregister, &WrapIt.unregister_block(options))
16
+ end
17
+ mod
18
+ end
19
+
20
+ private
21
+
22
+ def self.register_block(options)
23
+ # Runs in helpers module class context
24
+ proc do |*args|
25
+ class_name = args.pop
26
+ class_name.is_a?(String) || class_name.is_a?(Class) || fail(
27
+ ArgumentError,
28
+ "Last argument for #{name}.register_helper should be a class name"
29
+ )
30
+ class_name.is_a?(Class) && class_name = class_name.name
31
+ helpers = instance_methods
32
+ args.each do |helper|
33
+ !helper.is_a?(Symbol) && fail(
34
+ ArgumentError,
35
+ "First arguments for WrapIt.register" \
36
+ " should be Symbols with helper names"
37
+ )
38
+ helpers.include?(helper) && fail(
39
+ ArgumentError, "Helper #{helper} for WrapIt.register allready exists"
40
+ )
41
+ define_method(
42
+ "#{options[:prefix]}#{helper}",
43
+ WrapIt.method_block(helper, class_name)
44
+ )
45
+ end
46
+ end
47
+ end
48
+
49
+ def self.unregister_block(options)
50
+ # Runs in helpers module class context
51
+ proc do |*list|
52
+ helpers = instance_methods
53
+ list.each do |helper|
54
+ helper.is_a?(String) && helper = helper.to_sym
55
+ next unless helper.is_a?(Symbol)
56
+ helper_name = "#{options[:prefix]}#{helper}"
57
+ next unless helpers.include?(helper_name)
58
+ remove_method helper_name
59
+ end
60
+ end
61
+ end
62
+
63
+ def self.method_block(name, class_name)
64
+ # Runs in helpers module context
65
+ proc do |*args, &block|
66
+ opts = args.extract_options!
67
+ opts[:helper_name] = name
68
+ args << opts
69
+ obj = Object.const_get(class_name).new(self, *args, &block)
70
+ obj.render
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,23 @@
1
+ module WrapIt
2
+ module ModuleHelpers
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def placement(name)
9
+ define_method(
10
+ "#{name}_placement", &ModuleHelpers.placement_block(name)
11
+ )
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def placement_block(name)
18
+ proc do |hash = {before: :content}|
19
+
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,81 @@
1
+ module WrapIt
2
+ #
3
+ # Adds sections functionality
4
+ #
5
+ # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
6
+ #
7
+ module Sections
8
+ extend DerivedAttributes
9
+
10
+ def self.included(base)
11
+ base == Base || fail(
12
+ TypeError,
13
+ "#{self.class.name} can be included only into WrapIt::Base"
14
+ )
15
+ base.extend ClassMethods
16
+ end
17
+
18
+ def [](name)
19
+ @section_names ||= self.class.sections
20
+ return nil unless @section_names.include?(name)
21
+ @sections ||= {}
22
+ @sections[name] ||= empty_html
23
+ end
24
+
25
+ def []=(name, value)
26
+ @section_names ||= self.class.sections
27
+ return unless @section_names.include?(name)
28
+ @sections ||= {}
29
+ @sections[name] = value
30
+ end
31
+
32
+ #
33
+ # Class methods to include
34
+ #
35
+ module ClassMethods
36
+ def sections
37
+ collect_derived(:@sections)
38
+ end
39
+
40
+ def section(*args)
41
+ @sections ||= []
42
+ args.flatten.each do |name|
43
+ name.is_a?(String) && name = name.to_sym
44
+ next unless name.is_a?(Symbol)
45
+ next if (sections + [:begin, :end]).include?(name)
46
+ @sections << name
47
+ placement << name unless placement.include?(name)
48
+ place name, before: :end
49
+ end
50
+ end
51
+
52
+ def placement
53
+ @placement ||=
54
+ if self == Base
55
+ sections.clone
56
+ else
57
+ parent = ancestors[1..-1].find { |a| a.respond_to?(:placement) }
58
+ parent.nil? ? sections.clone : parent.placement.clone
59
+ end
60
+ end
61
+
62
+ def place(src, at, dst = nil)
63
+ if dst == nil && at.is_a?(Hash) && at.keys.size == 1
64
+ dst = at.values[0]
65
+ at = at.keys[0]
66
+ end
67
+ return unless placement.include?(src) &&
68
+ (dst == :begin || dst == :end || placement.include?(dst)) &&
69
+ (at == :before || at == :after)
70
+ item = placement.delete_at(placement.index(src))
71
+ case dst
72
+ when :begin then placement.unshift(item)
73
+ when :end then placement.push(item)
74
+ else
75
+ x = at == :before ? 0 : 1
76
+ placement.insert(placement.index(dst) + x, item)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -21,7 +21,6 @@ module WrapIt
21
21
  def switches_init
22
22
  @switches = {}
23
23
  keys = switches.keys
24
- # keys.each { |switch| @switches[switch] = false }
25
24
  @options.keys.select { |o| keys.include?(o) }.each do |switch|
26
25
  send("#{switches[switch][:name]}=", @options.delete(switch) == true)
27
26
  end
@@ -58,7 +57,7 @@ module WrapIt
58
57
  # on or removed in other case.
59
58
  #
60
59
  # @param name [String, Symbol] Switch name. Converted to `Symbol`.
61
- # @param opts = {} [Hash] Switch options
60
+ # @param opts [Hash] Switch options
62
61
  # @options opts [true, String, Symbol, Array<String, Symbol>] :html_class
63
62
  # HTML classes list that will automatically added to element if switch
64
63
  # is on or removed from element if switch id off.
@@ -66,7 +65,8 @@ module WrapIt
66
65
  # Warning! Values are not converted - pass only `Symbols` here.
67
66
  # @yield [state] Runs block when switch state changed, gives it to block.
68
67
  # @yieldparam state [Boolean] Whether switch is on or off.
69
- # @yieldreturn [void]
68
+ # @yieldreturn [Object, FalseClass] if you return `false`, value will
69
+ # ommited.
70
70
  #
71
71
  # @return [void]
72
72
  def switch(name, options = {}, &block)
@@ -102,13 +102,14 @@ module WrapIt
102
102
  def self.setter(name, &block)
103
103
  proc do |value|
104
104
  opts = switches[name]
105
- @switches[name] = value == true
106
- if value == true
107
- opts.key?(:html_class) && add_html_class(*opts[:html_class])
108
- block.nil? || instance_exec(true, &block)
109
- else
110
- opts.key?(:html_class) && remove_html_class(*opts[:html_class])
111
- block.nil? || instance_exec(false, &block)
105
+ cb_return = block.nil? || instance_exec(value == true, &block)
106
+ unless cb_return == false
107
+ @switches[name] = value == true
108
+ if value == true
109
+ opts.key?(:html_class) && add_html_class(*opts[:html_class])
110
+ else
111
+ opts.key?(:html_class) && remove_html_class(*opts[:html_class])
112
+ end
112
113
  end
113
114
  end
114
115
  end
@@ -9,6 +9,9 @@ module WrapIt
9
9
  base.class_eval do
10
10
  default_tag 'p', false
11
11
 
12
+ section :body
13
+ place :body, :before, :content
14
+
12
15
  after_initialize do
13
16
  @body = @arguments.extract_first!(String) || empty_html
14
17
  @body += @options[:body] || @options[:text] || empty_html
@@ -17,9 +20,11 @@ module WrapIt
17
20
  end
18
21
 
19
22
  after_capture do
20
- @content = html_safe(@body) + @content unless @body.nil?
23
+ self[:body] = html_safe(@body) unless @body.nil?
21
24
  end
22
25
  end
23
26
  end
27
+
28
+ attr_accessor :body
24
29
  end
25
30
  end
@@ -1,4 +1,4 @@
1
1
  #
2
2
  module WrapIt
3
- VERSION = '0.1.5'
3
+ VERSION = '0.2.0'
4
4
  end
data/lib/wrap_it.rb CHANGED
@@ -1,76 +1,22 @@
1
- require 'wrap_it/no_rails' unless defined? Rails
1
+ require 'wrap_it/frameworks'
2
2
 
3
- #
4
- # Main routines
5
- #
6
- # @author Alexey Ovchinnikov <alexiss@cybernetlab.ru>
7
- #
8
- module WrapIt
9
- #
10
- module Helpers; end
11
-
12
- def self.helpers(*list)
13
- @helpers ||= {}
14
- options = list.extract_options!
15
- list.empty? && list = @helpers.keys
16
- prefix = options[:prefix].blank? ? '' : "#{options[:prefix]}_"
17
- helpers_hash = @helpers
18
- Helpers.module_eval do
19
- list.each do |helper|
20
- define_method "#{prefix}#{helper}" do |*args, &block|
21
- opts = args.extract_options!
22
- opts[:helper_name] = helper
23
- args.push opts
24
- obj = Object.const_get(helpers_hash[helper]).new(self, *args, &block)
25
- obj.render
26
- end
27
- end
28
- end
29
- Helpers
30
- end
31
-
32
- def self.register(*args)
33
- class_name = args.pop
34
- !class_name.is_a?(String) && fail(
35
- ArgumentError,
36
- "Last argument for #{name}.register_helper should be a class name"
37
- )
38
- @helpers ||= {}
39
- args.each do |arg|
40
- !arg.is_a?(Symbol) && fail(
41
- ArgumentError,
42
- "First arguments for #{name}.register_helper" \
43
- " should be Symbols with helper names"
44
- )
45
- @helpers.key?(arg) && fail(
46
- ArgumentError,
47
- "Helper #{arg} for #{name}.register_helper allready exists"
48
- )
49
- @helpers[arg] = class_name
50
- end
51
- end
52
-
53
- def self.unregister(*helpers)
54
- @helpers ||= {}
55
- helpers.each do |helper|
56
- helper.is_a?(String) && helper = helper.to_sym
57
- next unless helper.is_a? Symbol
58
- @helpers.delete(helper)
59
- end
60
- end
3
+ if WrapIt.rails?
4
+ require 'rails'
5
+ require 'wrap_it/rails'
6
+ else
7
+ require 'wrap_it/no_rails'
61
8
  end
62
9
 
10
+ require 'wrap_it/helpers'
11
+
63
12
  require 'wrap_it/derived_attributes'
64
13
  require 'wrap_it/callbacks'
14
+ require 'wrap_it/sections'
65
15
  require 'wrap_it/arguments_array'
66
16
  require 'wrap_it/html_class'
67
17
  require 'wrap_it/html_data'
68
18
  require 'wrap_it/switches'
69
19
  require 'wrap_it/enums'
70
- if defined? Rails
71
- require 'wrap_it/rails'
72
- else
73
- end
74
20
  require 'wrap_it/base'
75
21
  require 'wrap_it/container'
76
22
  require 'wrap_it/text_container'