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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -3
  3. data/.rubocop.yml +15 -0
  4. data/.travis.yml +16 -0
  5. data/CHANGELOG.md +86 -3
  6. data/Gemfile +17 -5
  7. data/Gemfile.lock +224 -0
  8. data/LICENSE +20 -0
  9. data/README.md +43 -0
  10. data/Rakefile +19 -0
  11. data/arbre.gemspec +3 -0
  12. data/docs/Gemfile +2 -0
  13. data/docs/_config.yml +7 -0
  14. data/docs/_includes/footer.html +8 -0
  15. data/docs/_includes/google-analytics.html +16 -0
  16. data/docs/_includes/head.html +7 -0
  17. data/docs/_includes/toc.html +12 -0
  18. data/docs/_includes/top-menu.html +8 -0
  19. data/docs/_layouts/default.html +21 -0
  20. data/docs/index.md +106 -0
  21. data/docs/stylesheets/main.css +1152 -0
  22. data/lib/arbre/context.rb +34 -3
  23. data/lib/arbre/element/builder_methods.rb +4 -5
  24. data/lib/arbre/element/proxy.rb +28 -0
  25. data/lib/arbre/element.rb +12 -6
  26. data/lib/arbre/element_collection.rb +1 -1
  27. data/lib/arbre/html/attributes.rb +11 -2
  28. data/lib/arbre/html/class_list.rb +4 -0
  29. data/lib/arbre/html/document.rb +1 -1
  30. data/lib/arbre/html/html5_elements.rb +4 -4
  31. data/lib/arbre/html/tag.rb +24 -9
  32. data/lib/arbre/html/text_node.rb +4 -0
  33. data/lib/arbre/rails/forms.rb +70 -67
  34. data/lib/arbre/rails/template_handler.rb +7 -5
  35. data/lib/arbre/version.rb +1 -1
  36. data/spec/arbre/integration/html_spec.rb +118 -110
  37. data/spec/arbre/unit/component_spec.rb +9 -9
  38. data/spec/arbre/unit/context_spec.rb +8 -8
  39. data/spec/arbre/unit/element_finder_methods_spec.rb +44 -29
  40. data/spec/arbre/unit/element_spec.rb +64 -45
  41. data/spec/arbre/unit/html/class_list_spec.rb +16 -0
  42. data/spec/arbre/unit/html/tag_attributes_spec.rb +20 -18
  43. data/spec/arbre/unit/html/tag_spec.rb +51 -15
  44. data/spec/changelog_spec.rb +27 -0
  45. data/spec/rails/integration/forms_spec.rb +14 -30
  46. data/spec/rails/integration/rendering_spec.rb +46 -20
  47. data/spec/rails/rails_spec_helper.rb +8 -11
  48. data/spec/rails/stub_app/log/.gitignore +1 -1
  49. data/spec/rails/support/mock_person.rb +15 -0
  50. data/spec/rails/templates/arbre/_partial_with_assignment.arb +1 -0
  51. data/spec/rails/templates/arbre/page_with_arb_partial_and_assignment.arb +3 -0
  52. data/tasks/lint.rake +69 -0
  53. data/tasks/release.rake +6 -0
  54. metadata +43 -47
  55. data/.DS_Store +0 -0
  56. data/README.rdoc +0 -69
  57. 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 &block if block_given?
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 respond_to?(method)
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
- is_appendable = !tag.is_a?(Arbre::Element) && tag.respond_to?(:to_s)
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
- is_appendable = false
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
- attr_accessor :parent
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 =~ /#{class_name}/
110
- elements.concat(child.get_elements_by_tag_name(tag_name))
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
- ElementCollection.new([self]) + element
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
 
@@ -18,7 +18,7 @@ module Arbre
18
18
  def to_s
19
19
  self.collect do |element|
20
20
  element.to_s
21
- end.join.html_safe
21
+ end.join('').html_safe
22
22
  end
23
23
  end
24
24
 
@@ -4,13 +4,22 @@ module Arbre
4
4
  class Attributes < Hash
5
5
 
6
6
  def to_s
7
- self.collect do |name, value|
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
@@ -6,6 +6,10 @@ module Arbre
6
6
  # Holds a set of classes
7
7
  class ClassList < Set
8
8
 
9
+ def self.build_from_string(class_names)
10
+ new.add(class_names)
11
+ end
12
+
9
13
  def add(class_names)
10
14
  class_names.to_s.split(" ").each do |class_name|
11
15
  super(class_name)
@@ -29,7 +29,7 @@ module Arbre
29
29
 
30
30
  def build_head
31
31
  @head = head do
32
- meta :"http-equiv" => "Content-type", :content => "text/html; charset=utf-8"
32
+ meta :"http-equiv" => "Content-type", content: "text/html; charset=utf-8"
33
33
  end
34
34
  end
35
35
 
@@ -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
 
@@ -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
- set_for_attribute(attributes.delete(:for))
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
- get_attribute(:class) || set_attribute(:class, ClassList.new)
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
- %w|meta link|.include?(tag_name)
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
- attributes.any? ? " " + attributes.to_s : nil
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
 
@@ -22,6 +22,10 @@ module Arbre
22
22
  @content = string
23
23
  end
24
24
 
25
+ def class_list
26
+ []
27
+ end
28
+
25
29
  def tag_name
26
30
  nil
27
31
  end
@@ -1,92 +1,95 @@
1
- module SplitOpenAndCloseOn
1
+ module Arbre
2
+ module Rails
3
+ module Forms
2
4
 
3
- def split_open_and_close_on(string, match)
4
- return "" unless string && match
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
- def opening_tag
10
- @opening_tag || ""
11
- end
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
- def closing_tag
14
- @closing_tag || ""
15
- end
15
+ def select(*args)
16
+ proxy_call_to_form :select, *args
17
+ end
16
18
 
17
- end
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
- class FormBuilderProxy < Arbre::Component
20
- attr_reader :form_builder
27
+ private
21
28
 
22
- # Since label and select are Arbre Elements already, we must
23
- # override it here instead of letting method_missing
24
- # deal with it
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
- def select(*args)
30
- proxy_call_to_form :select, *args
31
- end
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
- def respond_to?(name)
34
- if form_builder && form_builder.respond_to?(name)
35
- true
36
- else
37
- super
38
- end
39
- end
41
+ end
40
42
 
41
- private
43
+ class FormForProxy < FormBuilderProxy
44
+ builder_method :form_for
42
45
 
43
- def proxy_call_to_form(method, *args, &block)
44
- text_node form_builder.send(method, *args, &block)
45
- end
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
- def method_missing(method, *args, &block)
48
- if form_builder && form_builder.respond_to?(method)
49
- proxy_call_to_form(method, *args, &block)
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
- end
55
+ def fields_for(*args, &block)
56
+ insert_tag FieldsForProxy, form_builder, *args, &block
57
+ end
56
58
 
57
- class FormForProxy < FormBuilderProxy
58
- builder_method :form_for
59
- include SplitOpenAndCloseOn
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
- def build(resource, form_options = {}, &block)
62
- form_string = helpers.form_for(resource, form_options) do |f|
63
- @form_builder = f
64
- end
65
+ def opening_tag
66
+ @opening_tag || ""
67
+ end
65
68
 
66
- split_open_and_close_on form_string, "</form>"
69
+ def closing_tag
70
+ @closing_tag || ""
71
+ end
67
72
 
68
- super(&block)
69
- end
73
+ end
70
74
 
71
- def fields_for(*args, &block)
72
- insert_tag FieldsForProxy, form_builder, *args, &block
73
- end
75
+ class FieldsForProxy < FormBuilderProxy
74
76
 
75
- end
77
+ def build(form_builder, *args, &block)
78
+ form_builder.fields_for(*args) do |f|
79
+ @form_builder = f
80
+ end
76
81
 
77
- class FieldsForProxy < FormBuilderProxy
82
+ super(&block)
83
+ end
78
84
 
79
- def build(form_builder, *args, &block)
80
- form_builder.fields_for(*args) do |f|
81
- @form_builder = f
82
- end
85
+ def to_s
86
+ children.to_s
87
+ end
83
88
 
84
- super(&block)
85
- end
89
+ end
86
90
 
87
- def to_s
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
- def call(template)
7
- "Arbre::Context.new(assigns, self){ #{template.source} }"
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
@@ -1,3 +1,3 @@
1
1
  module Arbre
2
- VERSION = "1.0.0.rc4"
2
+ VERSION = "1.2.1"
3
3
  end