arbre 1.0.0.rc4 → 1.2.1
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.
- checksums.yaml +7 -0
- data/.gitignore +6 -3
- data/.rubocop.yml +15 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +86 -3
- data/Gemfile +17 -5
- data/Gemfile.lock +224 -0
- data/LICENSE +20 -0
- data/README.md +43 -0
- data/Rakefile +19 -0
- data/arbre.gemspec +3 -0
- data/docs/Gemfile +2 -0
- data/docs/_config.yml +7 -0
- data/docs/_includes/footer.html +8 -0
- data/docs/_includes/google-analytics.html +16 -0
- data/docs/_includes/head.html +7 -0
- data/docs/_includes/toc.html +12 -0
- data/docs/_includes/top-menu.html +8 -0
- data/docs/_layouts/default.html +21 -0
- data/docs/index.md +106 -0
- data/docs/stylesheets/main.css +1152 -0
- data/lib/arbre/context.rb +34 -3
- data/lib/arbre/element/builder_methods.rb +4 -5
- data/lib/arbre/element/proxy.rb +28 -0
- data/lib/arbre/element.rb +12 -6
- data/lib/arbre/element_collection.rb +1 -1
- data/lib/arbre/html/attributes.rb +11 -2
- data/lib/arbre/html/class_list.rb +4 -0
- data/lib/arbre/html/document.rb +1 -1
- data/lib/arbre/html/html5_elements.rb +4 -4
- data/lib/arbre/html/tag.rb +24 -9
- data/lib/arbre/html/text_node.rb +4 -0
- data/lib/arbre/rails/forms.rb +70 -67
- data/lib/arbre/rails/template_handler.rb +7 -5
- data/lib/arbre/version.rb +1 -1
- data/spec/arbre/integration/html_spec.rb +118 -110
- data/spec/arbre/unit/component_spec.rb +9 -9
- data/spec/arbre/unit/context_spec.rb +8 -8
- data/spec/arbre/unit/element_finder_methods_spec.rb +44 -29
- data/spec/arbre/unit/element_spec.rb +64 -45
- data/spec/arbre/unit/html/class_list_spec.rb +16 -0
- data/spec/arbre/unit/html/tag_attributes_spec.rb +20 -18
- data/spec/arbre/unit/html/tag_spec.rb +51 -15
- data/spec/changelog_spec.rb +27 -0
- data/spec/rails/integration/forms_spec.rb +14 -30
- data/spec/rails/integration/rendering_spec.rb +46 -20
- data/spec/rails/rails_spec_helper.rb +8 -11
- data/spec/rails/stub_app/log/.gitignore +1 -1
- data/spec/rails/support/mock_person.rb +15 -0
- data/spec/rails/templates/arbre/_partial_with_assignment.arb +1 -0
- data/spec/rails/templates/arbre/page_with_arb_partial_and_assignment.arb +3 -0
- data/tasks/lint.rake +69 -0
- data/tasks/release.rake +6 -0
- metadata +43 -47
- data/.DS_Store +0 -0
- data/README.rdoc +0 -69
- data/lib/.DS_Store +0 -0
data/lib/arbre/context.rb
CHANGED
@@ -1,8 +1,39 @@
|
|
1
1
|
require 'arbre/element'
|
2
2
|
|
3
3
|
module Arbre
|
4
|
+
|
5
|
+
# The Arbre::Context class is the frontend for using Arbre.
|
6
|
+
#
|
7
|
+
# The simplest example possible:
|
8
|
+
#
|
9
|
+
# html = Arbre::Context.new do
|
10
|
+
# h1 "Hello World"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# html.to_s #=> "<h1>Hello World</h1>"
|
14
|
+
#
|
15
|
+
# The contents of the block are instance eval'd within the Context
|
16
|
+
# object. This means that you lose context to the outside world from
|
17
|
+
# within the block. To pass local variables into the Context, use the
|
18
|
+
# assigns param.
|
19
|
+
#
|
20
|
+
# html = Arbre::Context.new({one: 1}) do
|
21
|
+
# h1 "Your number #{one}"
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# html.to_s #=> "Your number 1"
|
25
|
+
#
|
4
26
|
class Context < Element
|
5
27
|
|
28
|
+
# Initialize a new Arbre::Context
|
29
|
+
#
|
30
|
+
# @param [Hash] assigns A hash of objecs that you would like to be
|
31
|
+
# availble as local variables within the Context
|
32
|
+
#
|
33
|
+
# @param [Object] helpers An object that has methods on it which will become
|
34
|
+
# instance methods within the context.
|
35
|
+
#
|
36
|
+
# @yield [] The block that will get instance eval'd in the context
|
6
37
|
def initialize(assigns = {}, helpers = nil, &block)
|
7
38
|
assigns = assigns || {}
|
8
39
|
@_assigns = assigns.symbolize_keys
|
@@ -11,7 +42,7 @@ module Arbre
|
|
11
42
|
@_current_arbre_element_buffer = [self]
|
12
43
|
|
13
44
|
super(self)
|
14
|
-
instance_eval
|
45
|
+
instance_eval(&block) if block_given?
|
15
46
|
end
|
16
47
|
|
17
48
|
def arbre_context
|
@@ -36,8 +67,8 @@ module Arbre
|
|
36
67
|
end
|
37
68
|
alias :length :bytesize
|
38
69
|
|
39
|
-
def
|
40
|
-
super || cached_html.respond_to?(method)
|
70
|
+
def respond_to_missing?(method, include_all)
|
71
|
+
super || cached_html.respond_to?(method, include_all)
|
41
72
|
end
|
42
73
|
|
43
74
|
# Webservers treat Arbre::Context as a string. We override
|
@@ -65,17 +65,16 @@ module Arbre
|
|
65
65
|
# Returns true if the object should be converted into a text node
|
66
66
|
# and appended into the DOM.
|
67
67
|
def appendable_tag?(tag)
|
68
|
-
|
69
|
-
|
70
|
-
# In ruby 1.9, Arraay.new.to_s prints out an empty array ("[]"). In
|
68
|
+
# Array.new.to_s prints out an empty array ("[]"). In
|
71
69
|
# Arbre, we append the return value of blocks to the output, which
|
72
70
|
# can cause empty arrays to show up within the output. To get
|
73
71
|
# around this, we check if the object responds to #empty?
|
74
72
|
if tag.respond_to?(:empty?) && tag.empty?
|
75
|
-
|
73
|
+
false
|
74
|
+
else
|
75
|
+
!tag.is_a?(Arbre::Element) && tag.respond_to?(:to_s)
|
76
76
|
end
|
77
77
|
|
78
|
-
is_appendable
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Arbre
|
2
|
+
class Element
|
3
|
+
class Proxy < BasicObject
|
4
|
+
undef_method :==
|
5
|
+
undef_method :equal?
|
6
|
+
|
7
|
+
def initialize(element)
|
8
|
+
@element = element
|
9
|
+
end
|
10
|
+
|
11
|
+
def respond_to?(method, include_all = false)
|
12
|
+
if method.to_s == 'to_ary'
|
13
|
+
false
|
14
|
+
else
|
15
|
+
super || @element.respond_to?(method, include_all)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing(method, *args, &block)
|
20
|
+
if method.to_s == 'to_ary'
|
21
|
+
super
|
22
|
+
else
|
23
|
+
@element.__send__ method, *args, &block
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/arbre/element.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'arbre/element/builder_methods'
|
2
|
+
require 'arbre/element/proxy'
|
2
3
|
require 'arbre/element_collection'
|
3
4
|
|
4
5
|
module Arbre
|
@@ -6,12 +7,13 @@ module Arbre
|
|
6
7
|
class Element
|
7
8
|
include BuilderMethods
|
8
9
|
|
9
|
-
|
10
|
+
attr_reader :parent
|
10
11
|
attr_reader :children, :arbre_context
|
11
12
|
|
12
13
|
def initialize(arbre_context = Arbre::Context.new)
|
13
14
|
@arbre_context = arbre_context
|
14
15
|
@children = ElementCollection.new
|
16
|
+
@parent = nil
|
15
17
|
end
|
16
18
|
|
17
19
|
def assigns
|
@@ -46,7 +48,7 @@ module Arbre
|
|
46
48
|
|
47
49
|
if child.respond_to?(:parent)
|
48
50
|
# Remove the child
|
49
|
-
child.parent.remove_child(child) if child.parent
|
51
|
+
child.parent.remove_child(child) if child.parent && child.parent != self
|
50
52
|
# Set ourselves as the parent
|
51
53
|
child.parent = self
|
52
54
|
end
|
@@ -106,8 +108,8 @@ module Arbre
|
|
106
108
|
def get_elements_by_class_name(class_name)
|
107
109
|
elements = ElementCollection.new
|
108
110
|
children.each do |child|
|
109
|
-
elements << child if child.class_list
|
110
|
-
elements.concat(child.
|
111
|
+
elements << child if child.class_list.include?(class_name)
|
112
|
+
elements.concat(child.get_elements_by_class_name(class_name))
|
111
113
|
end
|
112
114
|
elements
|
113
115
|
end
|
@@ -129,6 +131,10 @@ module Arbre
|
|
129
131
|
[to_s].each(&block)
|
130
132
|
end
|
131
133
|
|
134
|
+
def inspect
|
135
|
+
to_s
|
136
|
+
end
|
137
|
+
|
132
138
|
def to_str
|
133
139
|
to_s
|
134
140
|
end
|
@@ -143,11 +149,11 @@ module Arbre
|
|
143
149
|
else
|
144
150
|
element = Arbre::HTML::TextNode.from_string(element)
|
145
151
|
end
|
146
|
-
|
152
|
+
to_ary + element
|
147
153
|
end
|
148
154
|
|
149
155
|
def to_ary
|
150
|
-
ElementCollection.new [self]
|
156
|
+
ElementCollection.new [Proxy.new(self)]
|
151
157
|
end
|
152
158
|
alias_method :to_a, :to_ary
|
153
159
|
|
@@ -4,13 +4,22 @@ module Arbre
|
|
4
4
|
class Attributes < Hash
|
5
5
|
|
6
6
|
def to_s
|
7
|
-
|
7
|
+
map do |name, value|
|
8
|
+
next if value_empty?(value)
|
8
9
|
"#{html_escape(name)}=\"#{html_escape(value)}\""
|
9
|
-
end.join
|
10
|
+
end.compact.join ' '
|
11
|
+
end
|
12
|
+
|
13
|
+
def any?
|
14
|
+
super{ |k,v| !value_empty?(v) }
|
10
15
|
end
|
11
16
|
|
12
17
|
protected
|
13
18
|
|
19
|
+
def value_empty?(value)
|
20
|
+
value.respond_to?(:empty?) ? value.empty? : !value
|
21
|
+
end
|
22
|
+
|
14
23
|
def html_escape(s)
|
15
24
|
ERB::Util.html_escape(s)
|
16
25
|
end
|
data/lib/arbre/html/document.rb
CHANGED
@@ -7,11 +7,11 @@ module Arbre
|
|
7
7
|
:dfn, :div, :dl, :dt, :em, :embed, :fieldset, :figcaption, :figure,
|
8
8
|
:footer, :form, :h1, :h2, :h3, :h4, :h5, :h6, :head, :header, :hgroup,
|
9
9
|
:hr, :html, :i, :iframe, :img, :input, :ins, :keygen, :kbd, :label,
|
10
|
-
:legend, :li, :link, :map, :mark, :menu, :meta, :meter, :nav, :noscript,
|
11
|
-
:object, :ol, :optgroup, :option, :output, :pre, :progress, :q,
|
10
|
+
:legend, :li, :link, :map, :mark, :menu, :menuitem, :meta, :meter, :nav, :noscript,
|
11
|
+
:object, :ol, :optgroup, :option, :output, :param, :pre, :progress, :q,
|
12
12
|
:s, :samp, :script, :section, :select, :small, :source, :span,
|
13
|
-
:strong, :style, :sub, :summary, :sup, :table, :tbody, :td,
|
14
|
-
:textarea, :tfoot, :th, :thead, :time, :title, :tr, :ul, :var, :video ]
|
13
|
+
:strong, :style, :sub, :summary, :sup, :svg, :table, :tbody, :td,
|
14
|
+
:textarea, :tfoot, :th, :thead, :time, :title, :tr, :track, :ul, :var, :video, :wbr ]
|
15
15
|
|
16
16
|
HTML5_ELEMENTS = [ :p ] + AUTO_BUILD_ELEMENTS
|
17
17
|
|
data/lib/arbre/html/tag.rb
CHANGED
@@ -6,6 +6,10 @@ module Arbre
|
|
6
6
|
class Tag < Element
|
7
7
|
attr_reader :attributes
|
8
8
|
|
9
|
+
# See: http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements
|
10
|
+
SELF_CLOSING_ELEMENTS = [ :area, :base, :br, :col, :embed, :hr, :img, :input, :keygen, :link,
|
11
|
+
:menuitem, :meta, :param, :source, :track, :wbr ]
|
12
|
+
|
9
13
|
def initialize(*)
|
10
14
|
super
|
11
15
|
@attributes = Attributes.new
|
@@ -16,7 +20,10 @@ module Arbre
|
|
16
20
|
attributes = extract_arguments(args)
|
17
21
|
self.content = args.first if args.first
|
18
22
|
|
19
|
-
|
23
|
+
for_value = attributes[:for]
|
24
|
+
unless for_value.is_a?(String) || for_value.is_a?(Symbol)
|
25
|
+
set_for_attribute(attributes.delete(:for))
|
26
|
+
end
|
20
27
|
|
21
28
|
attributes.each do |key, value|
|
22
29
|
set_attribute(key, value)
|
@@ -77,7 +84,16 @@ module Arbre
|
|
77
84
|
end
|
78
85
|
|
79
86
|
def class_list
|
80
|
-
|
87
|
+
list = get_attribute(:class)
|
88
|
+
|
89
|
+
case list
|
90
|
+
when ClassList
|
91
|
+
list
|
92
|
+
when String
|
93
|
+
set_attribute(:class, ClassList.build_from_string(list))
|
94
|
+
else
|
95
|
+
set_attribute(:class, ClassList.new)
|
96
|
+
end
|
81
97
|
end
|
82
98
|
|
83
99
|
def to_s
|
@@ -95,7 +111,7 @@ module Arbre
|
|
95
111
|
end
|
96
112
|
|
97
113
|
INDENT_SIZE = 2
|
98
|
-
|
114
|
+
|
99
115
|
def indent(open_tag, child_content, close_tag)
|
100
116
|
spaces = ' ' * indent_level * INDENT_SIZE
|
101
117
|
|
@@ -119,11 +135,11 @@ module Arbre
|
|
119
135
|
|
120
136
|
html
|
121
137
|
end
|
122
|
-
|
138
|
+
|
123
139
|
def self_closing_tag?
|
124
|
-
|
140
|
+
SELF_CLOSING_ELEMENTS.include?(tag_name.to_sym)
|
125
141
|
end
|
126
|
-
|
142
|
+
|
127
143
|
def no_child?
|
128
144
|
children.empty?
|
129
145
|
end
|
@@ -132,9 +148,8 @@ module Arbre
|
|
132
148
|
children.size == 1 && children.first.is_a?(TextNode)
|
133
149
|
end
|
134
150
|
|
135
|
-
|
136
151
|
def attributes_html
|
137
|
-
|
152
|
+
" #{attributes}" if attributes.any?
|
138
153
|
end
|
139
154
|
|
140
155
|
def set_for_attribute(record)
|
@@ -149,7 +164,7 @@ module Arbre
|
|
149
164
|
if record.class.respond_to?(:model_name)
|
150
165
|
record.class.model_name.singular
|
151
166
|
else
|
152
|
-
record.class.underscore.gsub("/", "_")
|
167
|
+
record.class.name.underscore.gsub("/", "_")
|
153
168
|
end
|
154
169
|
end
|
155
170
|
|
data/lib/arbre/html/text_node.rb
CHANGED
data/lib/arbre/rails/forms.rb
CHANGED
@@ -1,92 +1,95 @@
|
|
1
|
-
module
|
1
|
+
module Arbre
|
2
|
+
module Rails
|
3
|
+
module Forms
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
@opening_tag = string.split(Regexp.new("#{match}\\z")).first
|
6
|
-
@closing_tag = match
|
7
|
-
end
|
5
|
+
class FormBuilderProxy < Arbre::Component
|
6
|
+
attr_reader :form_builder
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# Since label and select are Arbre Elements already, we must
|
9
|
+
# override it here instead of letting method_missing
|
10
|
+
# deal with it
|
11
|
+
def label(*args)
|
12
|
+
proxy_call_to_form :label, *args
|
13
|
+
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
def select(*args)
|
16
|
+
proxy_call_to_form :select, *args
|
17
|
+
end
|
16
18
|
|
17
|
-
|
19
|
+
def respond_to_missing?(method, include_all)
|
20
|
+
if form_builder && form_builder.respond_to?(method, include_all)
|
21
|
+
true
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
18
26
|
|
19
|
-
|
20
|
-
attr_reader :form_builder
|
27
|
+
private
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
def label(*args)
|
26
|
-
proxy_call_to_form :label, *args
|
27
|
-
end
|
29
|
+
def proxy_call_to_form(method, *args, &block)
|
30
|
+
text_node form_builder.send(method, *args, &block)
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
def method_missing(method, *args, &block)
|
34
|
+
if form_builder && form_builder.respond_to?(method)
|
35
|
+
proxy_call_to_form(method, *args, &block)
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
32
40
|
|
33
|
-
|
34
|
-
if form_builder && form_builder.respond_to?(name)
|
35
|
-
true
|
36
|
-
else
|
37
|
-
super
|
38
|
-
end
|
39
|
-
end
|
41
|
+
end
|
40
42
|
|
41
|
-
|
43
|
+
class FormForProxy < FormBuilderProxy
|
44
|
+
builder_method :form_for
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
+
def build(resource, form_options = {}, &block)
|
47
|
+
form_string = helpers.form_for(resource, form_options) do |f|
|
48
|
+
@form_builder = f
|
49
|
+
end
|
46
50
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
else
|
51
|
-
super
|
52
|
-
end
|
53
|
-
end
|
51
|
+
@opening_tag, @closing_tag = split_string_on(form_string, "</form>")
|
52
|
+
super(&block)
|
53
|
+
end
|
54
54
|
|
55
|
-
|
55
|
+
def fields_for(*args, &block)
|
56
|
+
insert_tag FieldsForProxy, form_builder, *args, &block
|
57
|
+
end
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
59
|
+
def split_string_on(string, match)
|
60
|
+
return "" unless string && match
|
61
|
+
part_1 = string.split(Regexp.new("#{match}\\z")).first
|
62
|
+
[part_1, match]
|
63
|
+
end
|
60
64
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
+
def opening_tag
|
66
|
+
@opening_tag || ""
|
67
|
+
end
|
65
68
|
|
66
|
-
|
69
|
+
def closing_tag
|
70
|
+
@closing_tag || ""
|
71
|
+
end
|
67
72
|
|
68
|
-
|
69
|
-
end
|
73
|
+
end
|
70
74
|
|
71
|
-
|
72
|
-
insert_tag FieldsForProxy, form_builder, *args, &block
|
73
|
-
end
|
75
|
+
class FieldsForProxy < FormBuilderProxy
|
74
76
|
|
75
|
-
|
77
|
+
def build(form_builder, *args, &block)
|
78
|
+
form_builder.fields_for(*args) do |f|
|
79
|
+
@form_builder = f
|
80
|
+
end
|
76
81
|
|
77
|
-
|
82
|
+
super(&block)
|
83
|
+
end
|
78
84
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
end
|
85
|
+
def to_s
|
86
|
+
children.to_s
|
87
|
+
end
|
83
88
|
|
84
|
-
|
85
|
-
end
|
89
|
+
end
|
86
90
|
|
87
|
-
|
88
|
-
children.to_s
|
91
|
+
end
|
89
92
|
end
|
90
|
-
|
91
93
|
end
|
92
94
|
|
95
|
+
|
@@ -1,14 +1,16 @@
|
|
1
1
|
module Arbre
|
2
2
|
module Rails
|
3
|
-
|
4
3
|
class TemplateHandler
|
4
|
+
def call(template, source = nil)
|
5
|
+
source = template.source unless source
|
5
6
|
|
6
|
-
|
7
|
-
|
7
|
+
<<-END
|
8
|
+
Arbre::Context.new(assigns, self) {
|
9
|
+
#{source}
|
10
|
+
}.to_s
|
11
|
+
END
|
8
12
|
end
|
9
|
-
|
10
13
|
end
|
11
|
-
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
data/lib/arbre/version.rb
CHANGED