hammer_builder 0.1.2 → 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.
@@ -1,16 +1,18 @@
1
1
  require 'active_support/core_ext/object/try'
2
2
 
3
- # When extended into a class it enables easy defining and extending classes in extended class.
4
- #
3
+ module HammerBuilder
4
+
5
+ # When extended into a class it enables easy defining and extending classes in the class.
6
+ #
5
7
  # class A
6
8
  # extend DynamicClasses
7
- # dc do
8
- # define :A do
9
+ # dynamic_classes do
10
+ # def_class :A do
9
11
  # def to_s
10
12
  # 'a'
11
13
  # end
12
14
  # end
13
- # define :B, :A do
15
+ # def_class :B, :A do
14
16
  # class_eval <<-RUBYCODE, __FILE__, __LINE__+1
15
17
  # def to_s
16
18
  # super + 'b'
@@ -24,8 +26,8 @@ require 'active_support/core_ext/object/try'
24
26
  # end
25
27
  #
26
28
  # class C < A
27
- # dc do
28
- # extend :A do
29
+ # dynamic_classes do
30
+ # extend_class :A do
29
31
  # def to_s
30
32
  # 'aa'
31
33
  # end
@@ -41,165 +43,169 @@ require 'active_support/core_ext/object/try'
41
43
  #
42
44
  # Last example is the most interesting. It prints 'aab' not 'ab' because of the extension in class C. Class :B has
43
45
  # as ancestor extended class :A from C therefore the two 'a'.
44
- module DynamicClasses
46
+ module DynamicClasses
45
47
 
46
- # Adds ability to describe itself when class is defined without constant
47
- module Describable
48
- def self.included(base)
49
- base.singleton_class.send :alias_method, :original_to_s, :to_s
50
- base.extend ClassMethods
51
- end
48
+ # Adds ability to describe itself when class is defined without constant
49
+ module Describable
50
+ def self.included(base)
51
+ base.singleton_class.send :alias_method, :original_to_s, :to_s
52
+ base.extend ClassMethods
53
+ end
52
54
 
53
- module ClassMethods
54
- # sets +description+
55
- # @param [String] description
56
- def _description=(description)
57
- @_description = description
55
+ module ClassMethods
56
+ # sets +description+
57
+ # @param [String] description
58
+ def _description=(description)
59
+ @_description = description
60
+ end
61
+
62
+ def to_s
63
+ super.gsub(/>$/, "(#{@_description})>")
64
+ end
58
65
  end
59
66
 
60
67
  def to_s
61
- super.gsub(/>$/, "(#{@_description})>")
68
+ klass = respond_to?(:rclass) ? self.rclass : self.class
69
+ super.gsub(klass.original_to_s, klass.to_s)
62
70
  end
63
71
  end
64
72
 
65
- def to_s
66
- klass = respond_to?(:rclass) ? self.rclass : self.class
67
- super.gsub(klass.original_to_s, klass.to_s)
73
+ class DescribableClass
74
+ include Describable
68
75
  end
69
- end
70
-
71
- class DescribableClass
72
- include Describable
73
- end
74
76
 
75
- ClassDefinition = Struct.new(:name, :base, :superclass_or_name, :definition)
76
- ClassExtension = Struct.new(:name, :base, :definition)
77
+ ClassDefinition = Struct.new(:name, :base, :superclass_or_name, :definition)
78
+ ClassExtension = Struct.new(:name, :base, :definition)
77
79
 
78
- class Classes
79
- attr_reader :base, :class_definitions, :classes, :class_extensions
80
+ class Classes
81
+ attr_reader :base, :class_definitions, :classes, :class_extensions
80
82
 
81
- def initialize(base)
82
- raise unless base.is_a? Class
83
- @base = base
84
- @class_definitions = {}
85
- @class_extensions = {}
86
- @classes = {}
87
- end
83
+ def initialize(base)
84
+ raise unless base.is_a? Class
85
+ @base = base
86
+ @class_definitions = { }
87
+ @class_extensions = { }
88
+ @classes = { }
89
+ end
88
90
 
89
- # define a class
90
- # @param [Symbol] name
91
- # @param [Symbol, Class, nil] superclass_or_name
92
- # when Symbol then dynamic class is found
93
- # when Class then this class is used
94
- # when nil then Object is used
95
- # @yield definition block is evaluated inside the class defining it
96
- def define(name, superclass_or_name = nil, &definition)
97
- raise ArgumentError, "name is not a Symbol" unless name.is_a?(Symbol)
98
- unless superclass_or_name.is_a?(Symbol) || superclass_or_name.is_a?(Class) || superclass_or_name.nil?
99
- raise ArgumentError, "superclass_or_name is not a Symbol, Class or nil"
91
+ # define a class
92
+ # @param [Symbol] name
93
+ # @param [Symbol, Class, nil] superclass_or_name
94
+ # when Symbol then dynamic class is found
95
+ # when Class then this class is used
96
+ # when nil then Object is used
97
+ # @yield definition block is evaluated inside the class defining it
98
+ def def_class(name, superclass_or_name = nil, &definition)
99
+ raise ArgumentError, "name is not a Symbol" unless name.is_a?(Symbol)
100
+ unless superclass_or_name.is_a?(Symbol) || superclass_or_name.is_a?(Class) || superclass_or_name.nil?
101
+ raise ArgumentError, "superclass_or_name is not a Symbol, Class or nil"
102
+ end
103
+ raise ArgumentError, "definition is nil" unless definition
104
+ raise ArgumentError, "Class #{name} already defined" if class_definition(name)
105
+ @class_definitions[name] = ClassDefinition.new(name, base, superclass_or_name, definition)
100
106
  end
101
- raise ArgumentError, "definition is nil" unless definition
102
- raise ArgumentError, "Class #{name} already defined" if class_definition(name)
103
- @class_definitions[name] = ClassDefinition.new(name, base, superclass_or_name, definition)
104
- end
105
107
 
106
- # extends already defined class by adding a child,
107
- # @param [Symbol] name
108
- # @yield definition block is evaluated inside the class extending it
109
- def extend(name, &definition)
110
- raise ArgumentError, "name is not a Symbol" unless name.is_a?(Symbol)
111
- raise ArgumentError, "definition is nil" unless definition
112
- raise ArgumentError, "Class #{name} not defined" unless class_definition(name)
113
- @class_extensions[name] = ClassExtension.new(name, base, definition)
114
- end
108
+ # extends already defined class by adding a child,
109
+ # @param [Symbol] name
110
+ # @yield definition block is evaluated inside the class extending it
111
+ def extend_class(name, &definition)
112
+ raise ArgumentError, "name is not a Symbol" unless name.is_a?(Symbol)
113
+ raise ArgumentError, "definition is nil" unless definition
114
+ raise ArgumentError, "Class #{name} not defined" unless class_definition(name)
115
+ @class_extensions[name] = ClassExtension.new(name, base, definition)
116
+ end
115
117
 
116
- # triggers loading of all defined classes
117
- def load!
118
- class_names.each {|name| self[name] }
119
- end
118
+ # triggers loading of all defined classes
119
+ def load!
120
+ class_names.each { |name| self[name] }
121
+ end
120
122
 
121
- # @return [Class] defined class
122
- def [](name)
123
- return @classes[name] if @classes[name]
124
- return nil unless klass_definition = class_definition(name)
125
-
126
- superclass = case klass_definition.superclass_or_name
127
- when Symbol then self[klass_definition.superclass_or_name]
128
- when Class then
129
- klass = Class.new(klass_definition.superclass_or_name)
130
- klass.send :include, Describable
131
- klass._description = "Describable#{klass_definition.superclass_or_name}"
132
- klass
133
- when nil then DescribableClass
123
+ # @return [Class] defined class
124
+ def [](name)
125
+ return @classes[name] if @classes[name]
126
+ return nil unless klass_definition = class_definition(name)
127
+
128
+ superclass = case klass_definition.superclass_or_name
129
+ when Symbol then
130
+ self[klass_definition.superclass_or_name]
131
+ when Class then
132
+ klass = Class.new(klass_definition.superclass_or_name)
133
+ klass.send :include, Describable
134
+ klass._description = "Describable#{klass_definition.superclass_or_name}"
135
+ klass
136
+ when nil then
137
+ DescribableClass
138
+ end
139
+
140
+ klass = Class.new(superclass, &klass_definition.definition)
141
+ klass._description = "#{base}.dc[:#{klass_definition.name}]"
142
+
143
+ class_extensions(name).each do |klass_extension|
144
+ klass = Class.new(klass, &klass_extension.definition)
145
+ klass._description = "#{base}.dc[:#{klass_extension.name}]"
146
+ end
147
+
148
+ @classes[name] = klass
134
149
  end
135
- klass = Class.new(superclass, &klass_definition.definition)
136
- klass._description = "#{base}.dc[:#{klass_definition.name}]"
137
150
 
138
- class_extensions(name).each do |klass_extension|
139
- klass = Class.new(klass, &klass_extension.definition)
140
- klass._description = "#{base}.dc[:#{klass_extension.name}]"
151
+ def class_names
152
+ ancestors.map(&:class_definitions).map(&:keys).flatten
141
153
  end
142
154
 
143
- @classes[name] = klass
144
- end
155
+ private
145
156
 
146
- private
157
+ def class_definition(name)
158
+ @class_definitions[name] || ancestor.try(:class_definition, name)
159
+ end
147
160
 
148
- def class_names
149
- ancestors.map(&:class_definitions).map(&:keys).flatten
150
- end
161
+ def class_extensions(name)
162
+ ([*ancestor.try(:class_extensions, name)] + [@class_extensions[name]]).compact
163
+ end
151
164
 
152
- def class_definition(name)
153
- @class_definitions[name] || ancestor.try(:class_definition, name)
165
+ def ancestors
166
+ ([self] + [*ancestor.try(:ancestors)]).compact
167
+ end
168
+
169
+ def ancestor
170
+ @base.superclass.dynamic_classes if @base.superclass.kind_of?(DynamicClasses)
171
+ end
154
172
  end
155
173
 
156
- def class_extensions(name)
157
- ( [*ancestor.try(:class_extensions, name)] + [@class_extensions[name]] ).compact
174
+ # hook to create Classes instance
175
+ def self.extended(base)
176
+ base.send :create_dynamic_classes
177
+ super
158
178
  end
159
179
 
160
- def ancestors
161
- ( [self] + [*ancestor.try(:ancestors)] ).compact
180
+ # hook to create Classes instance in descendants
181
+ def inherited(base)
182
+ base.send :create_dynamic_classes
183
+ super
162
184
  end
163
185
 
164
- def ancestor
165
- @base.superclass.dynamic_classes if @base.superclass.kind_of?(DynamicClasses)
186
+ # call this to get access to Classes instance to define/extend classes inside +definition+
187
+ # calls Classes#load! to preload defined classes
188
+ # @yield [Proc, nil] definition
189
+ # a Proc enables writing class definitions/extensions
190
+ # @return [Classes] when definition is nil
191
+ def dynamic_classes(&definition)
192
+ if definition
193
+ @dynamic_classes.instance_eval &definition
194
+ # @dynamic_classes.load!
195
+ nil
196
+ else
197
+ @dynamic_classes
198
+ end
166
199
  end
167
- end
168
200
 
169
- # hook to create Classes instance
170
- def self.extended(base)
171
- base.send :create_dynamic_classes
172
- super
173
- end
201
+ alias_method :dc, :dynamic_classes
174
202
 
175
- # hook to create Classes instance in descendants
176
- def inherited(base)
177
- base.send :create_dynamic_classes
178
- super
179
- end
203
+ private
180
204
 
181
- # call this to get access to Classes instance to define/extend classes inside +definition+
182
- # calls Classes#load! to preload defined classes
183
- # @yield [Proc, nil] definition
184
- # a Proc enables writing class definitions/extensions
185
- # @return [Classes] when definition is nil
186
- def dynamic_classes(&definition)
187
- if definition
188
- @dynamic_classes.instance_eval &definition
189
- @dynamic_classes.load!
190
- nil
191
- else
192
- @dynamic_classes
205
+ def create_dynamic_classes
206
+ @dynamic_classes = Classes.new(self)
193
207
  end
194
208
  end
195
-
196
- alias :dc :dynamic_classes
197
-
198
- private
199
-
200
- def create_dynamic_classes
201
- @dynamic_classes = Classes.new(self)
202
- end
203
209
  end
204
210
 
205
211
 
@@ -0,0 +1,43 @@
1
+ require 'hammer_builder/standard'
2
+
3
+ module HammerBuilder
4
+ # Builder implementation with formatting (indented by ' ')
5
+ # Slow down is less then 1%
6
+ class Formatted < Standard
7
+
8
+ dynamic_classes do
9
+ extend_class :AbstractTag do
10
+ def open(attributes = nil)
11
+ @output << Strings::NEWLINE << Strings::SPACES.fetch(@stack.size, Strings::SPACE) << Strings::LT << @tag_name
12
+ @builder.current = self
13
+ attributes(attributes)
14
+ default
15
+ self
16
+ end
17
+ end
18
+
19
+ extend_class :AbstractDoubleTag do
20
+ def with
21
+ flush_classes
22
+ @output << Strings::GT
23
+ @content = nil
24
+ @builder.current = nil
25
+ yield
26
+ #if (content = yield).is_a?(String)
27
+ # @output << EscapeUtils.escape_html(content, false)
28
+ #end
29
+ @builder.flush
30
+ @output << Strings::NEWLINE << Strings::SPACES.fetch(@stack.size-1, Strings::SPACE) << Strings::SLASH_LT <<
31
+ @stack.pop << Strings::GT
32
+ nil
33
+ end
34
+ end
35
+ end
36
+
37
+ def comment(comment)
38
+ flush
39
+ @_output << Strings::NEWLINE << Strings::SPACES.fetch(@_stack.size, Strings::SPACE) << Strings::COMMENT_START <<
40
+ comment.to_s << Strings::COMMENT_END
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ module HammerBuilder
2
+ module Helper
3
+
4
+ # adds instance method to the class. Method accepts any instance of builder and returns it after rendering.
5
+ # @param [Symbol] method_name
6
+ # @yield [self] builder_block is evaluated inside builder and accepts instance of a rendered object as parameter
7
+ # @example
8
+ # class User
9
+ # # ...
10
+ # include HammerBuilder::Helper
11
+ #
12
+ # builder :menu do |user|
13
+ # li user.name
14
+ # end
15
+ # end
16
+ #
17
+ # User.new.menu(HammerBuilder::Standard.get).to_html! #=> "<li>Name</li>"
18
+ def builder(method_name, &builder_block)
19
+ define_method(method_name) do |builder, *args|
20
+ builder.dive(self, *args, &builder_block)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,72 @@
1
+ module HammerBuilder
2
+
3
+ # Creating builder instances is expensive, therefore you can use Pool to go around that
4
+ # @example
5
+ # pool = Pool.new Formatted
6
+ # pool.get.go_in do
7
+ # # some rendering
8
+ # end.to_xhtml! # => output and releases the builder to pool
9
+ class Pool
10
+
11
+ module Helper
12
+ def release
13
+ @_origin.release self
14
+ end
15
+
16
+ # @return [String] output and releases the builder to pool
17
+ def to_html!
18
+ to_html
19
+ ensure
20
+ release
21
+ end
22
+ end
23
+
24
+ attr_reader :klass
25
+
26
+ def initialize(klass)
27
+ @klass = klass
28
+ @pool = []
29
+ klass.send :include, Helper
30
+ end
31
+
32
+ # This the preferred way of getting new Builder. If you forget to release it, it does not matter -
33
+ # builder gets GCed after you lose reference
34
+ # @return [Abstract]
35
+ def get
36
+ if @pool.empty?
37
+ @klass.new.instance_exec(self) { |origin| @_origin = origin; self }
38
+ else
39
+ @pool.pop
40
+ end
41
+ end
42
+
43
+ # returns +builder+ back into pool *DONT* forget to lose the reference to the +builder+
44
+ # @param [Abstract]
45
+ def release(builder)
46
+ raise TypeError unless builder.is_a? @klass
47
+ builder.reset
48
+ @pool.push builder
49
+ nil
50
+ end
51
+
52
+ def size
53
+ @pool.size
54
+ end
55
+ end
56
+
57
+ class SynchronizedPool < Pool
58
+ def initialize(klass)
59
+ super(klass)
60
+ @mutex = Mutex.new
61
+ end
62
+
63
+ def get
64
+ @mutex.synchronize { super }
65
+ end
66
+
67
+ def release(builder)
68
+ @mutex.synchronize { super(builder) }
69
+ end
70
+
71
+ end
72
+ end