elementor 0.0.4 → 0.0.5

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.
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def extract_options!
3
+ last.is_a?(Hash) ? pop : { }
4
+ end
5
+ end unless [].respond_to?(:extract_options!)
@@ -1,10 +1,16 @@
1
1
  module Kernel
2
- def blank_context(ivars={}, &block)
2
+ def blank_context(*args, &block)
3
+ ivars = args.extract_options!
4
+
5
+ args.push(/(^__)|instance_/)
6
+
3
7
  klass = Class.new do
4
- instance_methods.each { |m| undef_method(m) unless m =~ /^(__|instance_|meta)/ }
8
+ instance_methods.each do |m|
9
+ undef_method(m) unless args.any? { |pattern| m =~ pattern }
10
+ end
5
11
  end
6
12
 
7
- klass.class_eval(&block)
13
+ klass.class_eval(&block) if block_given?
8
14
  instance = klass.new
9
15
  ivars.each { |key, value| instance.instance_variable_set("@#{key}", value) }
10
16
  instance
@@ -1,8 +1,4 @@
1
1
  class Object
2
- def try(sym, *args, &block)
3
- respond_to?(sym) ? send(sym, *args, &block) : nil
4
- end
5
-
6
2
  def metaclass
7
3
  class << self; self end
8
4
  end
data/lib/elementor.rb CHANGED
@@ -3,6 +3,7 @@ $LOAD_PATH << File.dirname(__FILE__) + '/elementor'
3
3
 
4
4
  require 'rubygems'
5
5
  require 'nokogiri'
6
+ require 'array'
6
7
  require 'kernel'
7
8
  require 'object'
8
9
  require 'symbol'
@@ -11,8 +12,6 @@ require 'element_set'
11
12
 
12
13
  module Elementor
13
14
  def elements(opts={}, &block)
14
- namer = Result.new(self, opts, &block)
15
- namer.define_elements!
16
- namer.dispatcher
15
+ Result.new(self, opts, &block).dispatcher
17
16
  end
18
17
  end
@@ -1,41 +1,54 @@
1
1
  module Elementor
2
+ # ElementSet objects wrap a Nokogiri #search result and
3
+ # add additional functionality such as chained filtering.
2
4
  class ElementSet < Array
3
5
  attr_accessor :result, :selector
4
6
 
7
+ # A simple filter for selecting only elements with content
8
+ # that either includes a String passed in, or matches a Regexp.
5
9
  def with_text(matcher)
6
- replace(select { |item|
10
+ filter do |item|
7
11
  case matcher
8
12
  when Regexp then item.text =~ matcher
9
13
  when String then item.text.include?(matcher)
14
+ else item.text.include?(matcher.to_s)
10
15
  end
11
- }) ; self
16
+ end
12
17
  end
13
18
 
19
+ alias_method :text, :with_text
20
+
21
+ # Attribute filtering using hashes. See the specs for examples.
14
22
  def with_attrs(options={})
15
- replace(select { |item|
16
- options.all? { |key, value|
23
+ filter do |item|
24
+ options.all? do |key, value|
17
25
  case value
18
26
  when Regexp then item[key.to_s] =~ value
19
27
  when String then item[key.to_s] == value
28
+ else item[key.to_s] == value.to_s
20
29
  end
21
- }
22
- }) ; self
23
- end
24
-
25
- def inspect
26
- map(&:text).inspect
30
+ end
31
+ end
27
32
  end
28
33
 
34
+ alias_method :attrs, :with_attrs
35
+
29
36
  def method_missing(sym, *args, &block)
30
- result.try(sym, doc, *args) || super
37
+ result.respond_to?(sym) ? result.send(sym, doc, *args) : super
31
38
  end
32
39
 
33
40
  def respond_to?(sym)
34
41
  result.respond_to?(sym) || super
35
42
  end
36
43
 
44
+ private
45
+
37
46
  def doc
38
47
  result.doc.search(selector)
39
48
  end
49
+
50
+ def filter(&block)
51
+ replace(select(&block)) ; return self
52
+ end
40
53
  end
41
54
  end
@@ -8,63 +8,97 @@ module Elementor
8
8
  @context = context
9
9
  @doc_ready = false
10
10
  block.call(naming_context)
11
+ define_elements!
11
12
  end
12
13
 
14
+ # Allows for the parsing of raw markup that doesn't come
15
+ # from the :from option.
13
16
  def parse!(markup)
14
17
  doc(markup)
15
18
  end
16
19
 
17
- def naming_context
18
- @naming_context ||= blank_context(:this => self) do
19
- def method_missing(sym, *args)
20
- @this.element_names[sym] = *args
20
+ # Returns a blank slate object that delegates to either an
21
+ # instance of Result or the original Nokogiri doc.
22
+ def dispatcher
23
+ @dispatcher ||= blank_context(:this => self) do
24
+ def method_missing(sym, *args, &block)
25
+ @this.doc_ready!
26
+ [@this, @this.doc].each do |context|
27
+ next unless context.respond_to?(sym)
28
+ return context.send(sym, *args, &block)
29
+ end
30
+ super # raise NoMethodError if no context can handle
21
31
  end
22
32
  end
23
33
  end
24
34
 
25
- def dispatcher
26
- @dispatcher ||= blank_context(:this => self) do
27
- def method_missing(sym, *args, &block)
28
- @this.doc_ready = true
29
- @this.try(sym, *args, &block) || @this.doc.try(sym, *args, &block) || super
35
+ # The list of name/selector pairs you specify in the
36
+ # elements block.
37
+ def element_names
38
+ @element_names ||= { }
39
+ end
40
+
41
+ # Returns the raw Nokogiri doc once a method has been called
42
+ # on the dispatcher. Up until that point, returns nil.
43
+ def doc(markup=nil)
44
+ if html = markup || content
45
+ @doc = nil if markup
46
+ @doc ||= Nokogiri(html)
47
+ end
48
+ end
49
+
50
+ # Indicates whether or not the dispatcher has received messages,
51
+ # meaning the content method can be called.
52
+ def doc_ready?
53
+ @doc_ready
54
+ end
55
+
56
+ def doc_ready!
57
+ @doc_ready = true
58
+ end
59
+
60
+ private
61
+
62
+ # Blank slate context for defining element names.
63
+ def naming_context
64
+ @naming_context ||= blank_context(:this => self) do
65
+ def method_missing(sym, *args)
66
+ @this.element_names[sym] = *args
30
67
  end
31
68
  end
32
69
  end
33
-
70
+
71
+ # Takes element names and defines methods that return ElementSet
72
+ # objects with can be chained and filtered.
34
73
  def define_elements!
35
74
  element_names.each do |name, selector|
36
- meta_def(name) do |*filters|
37
- set = ElementSet.new scope(filters).search(selector)
38
- set.result = self
39
- set.selector = selector
40
- filters.empty? ? set : filters.inject(set) { |result, fn| fn[result] }
41
- end
75
+ metaclass.class_eval(<<-END, __FILE__, __LINE__)
76
+ def #{name}(*filters, &block)
77
+ make_element_set(#{name.inspect}, #{selector.inspect}, *filters, &block)
78
+ end
79
+ END
42
80
  end
43
81
  end
44
82
 
83
+ def make_element_set(name, selector, *filters, &block)
84
+ set = ElementSet.new scope(filters).search(selector)
85
+ set.result = self
86
+ set.selector = selector
87
+ set = filters.empty? ? set : filters.inject(set) { |result, fn| fn[result] }
88
+ set = block.call(set) if block_given?
89
+ set
90
+ end
91
+
92
+ # Enables the chaining of element selector methods to only search
93
+ # within the scope of a certain ElementSet.
45
94
  def scope(filters)
46
95
  scope = filters.first.is_a?(Proc) ? nil : filters.shift
47
96
  scope || doc
48
97
  end
49
98
 
50
- def element_names
51
- @element_names ||= { }
52
- end
53
-
54
- def doc(markup=nil)
55
- if html = markup || content
56
- @doc = nil if markup
57
- @doc ||= Nokogiri(html)
58
- end
59
- end
60
-
61
99
  def content
62
100
  return unless doc_ready?
63
101
  @content ||= context.send(opts[:from] || :body)
64
102
  end
65
-
66
- def doc_ready?
67
- @doc_ready
68
- end
69
103
  end
70
104
  end
@@ -7,11 +7,15 @@ module Spec
7
7
  self
8
8
  end
9
9
 
10
+ alias_method :text, :with_text
11
+
10
12
  def with_attrs(options={})
11
13
  @args ||= []
12
14
  @args << proc { |set| set.with_attrs(options) }
13
15
  self
14
16
  end
17
+
18
+ alias_method :attrs, :with_attrs
15
19
  end
16
20
  end
17
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elementor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Nakajima
@@ -37,6 +37,7 @@ files:
37
37
  - lib/elementor/element_set.rb
38
38
  - lib/elementor/result.rb
39
39
  - lib/core_ext
40
+ - lib/core_ext/array.rb
40
41
  - lib/core_ext/object.rb
41
42
  - lib/core_ext/kernel.rb
42
43
  - lib/core_ext/symbol.rb
@@ -62,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
63
  requirements: []
63
64
 
64
65
  rubyforge_project:
65
- rubygems_version: 1.3.0
66
+ rubygems_version: 1.3.1
66
67
  signing_key:
67
68
  specification_version: 2
68
69
  summary: Prettier element traversal with Nokogiri