wrap_it 0.1.5 → 0.2.0

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