former 0.0.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/README.rdoc +12 -15
- data/former.gemspec +0 -1
- data/lib/former/builder.rb +25 -16
- data/lib/former/element.rb +5 -11
- data/lib/former/version.rb +1 -1
- data/test/builder_test.rb +25 -1
- metadata +6 -20
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
former
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.3-p392
|
data/README.rdoc
CHANGED
@@ -32,15 +32,14 @@ In this example, we want to be able to edit the location of all links that have
|
|
32
32
|
To produce the input fields from some example input HTML, first create an instance of your parser.
|
33
33
|
|
34
34
|
parsed = Parser.new "<p>some text<a class='important' href='http://alink.com'>some link text<img src='/an/image/path' /></a>last text</p>"
|
35
|
-
puts parsed.to_form_html
|
36
35
|
|
37
|
-
|
36
|
+
Then, we can get information about the editable fields:
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
<input name=
|
38
|
+
parsed.each { |elem| puts "#{elem.node.name} is #{elem.value}" }
|
39
|
+
|
40
|
+
And, even create an html form from this:
|
41
|
+
|
42
|
+
parsed.each { |elem| puts "<input name='former_0' type='text' value='#{elem.value}' />" }
|
44
43
|
|
45
44
|
You can also convert the original html to json:
|
46
45
|
|
@@ -61,14 +60,12 @@ Will produce the original html with the new field values:
|
|
61
60
|
|
62
61
|
<p>A New Description<a class='important' href='http://anewlink.com'>some link...
|
63
62
|
|
64
|
-
|
63
|
+
If you want finer grained control over what's considered an editable element, you can pass a block in as well to the attr method. For instance, if you want only text fields that are not empty, you could use:
|
65
64
|
|
66
65
|
class Parser < Former::Builder
|
67
|
-
|
68
|
-
attr("img", :src) { |elem, name|
|
69
|
-
# If the src url is a really long bit of text, you could do something like this.
|
70
|
-
# The name is the name (which has the index) necessary to set the value on a GET/POST
|
71
|
-
"<textarea name='#{name}'></textarea>"
|
72
|
-
}
|
73
|
-
text "p", "a.important"
|
66
|
+
text("p", "a.important") { |elem| not elem.text.strip.empty? }
|
74
67
|
end
|
68
|
+
|
69
|
+
p = Parser.new("<p>\n</p><p>some text</p>").length # this is only 1
|
70
|
+
p[0] = 'other text'
|
71
|
+
p.to_html # => '<p>\n</p><p>other text</p>'
|
data/former.gemspec
CHANGED
@@ -14,7 +14,6 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
15
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
16
|
gem.require_paths = ["lib"]
|
17
|
-
gem.add_dependency("actionpack", ">= 4.0.3")
|
18
17
|
gem.add_dependency("nokogiri", ">= 1.6.0")
|
19
18
|
gem.add_development_dependency("rake")
|
20
19
|
gem.add_development_dependency("rdoc")
|
data/lib/former/builder.rb
CHANGED
@@ -9,8 +9,8 @@ module Former
|
|
9
9
|
@html = Nokogiri::HTML.parse(html)
|
10
10
|
|
11
11
|
matches = {}
|
12
|
-
self.class.queries.each
|
13
|
-
@html.search(path).each
|
12
|
+
self.class.queries.each do |path, qs|
|
13
|
+
@html.search(path).each do |node|
|
14
14
|
# if all we need is the text, only include text kids
|
15
15
|
if qs.length == 1 and qs.first[:query] == :text
|
16
16
|
node.traverse { |e| matches[e] = qs if e.text? and not matches.keys.include? e }
|
@@ -18,23 +18,29 @@ module Former
|
|
18
18
|
# otherwise, ignore just text requests
|
19
19
|
matches[node] = qs.select { |q| q[:query] != :text }
|
20
20
|
end
|
21
|
-
|
22
|
-
|
21
|
+
end
|
22
|
+
end
|
23
23
|
|
24
24
|
@editable = []
|
25
|
-
@html.traverse_prefix
|
26
|
-
(matches[e] || []).each
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
@html.traverse_prefix do |e|
|
26
|
+
(matches[e] || []).each do |query|
|
27
|
+
if query[:block].nil? or query[:block].call(e)
|
28
|
+
@editable << Element.new(e, query[:query], @editable.length)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# if we were given text only (no html), nokogiri will helpfully
|
34
|
+
# wrap it in a <p> - but our output should just be text. So remember.
|
35
|
+
@nothtml = (@editable.length == 1 and @html.text == html)
|
30
36
|
end
|
31
37
|
|
32
|
-
def
|
33
|
-
@editable.
|
38
|
+
def length
|
39
|
+
@editable.length
|
34
40
|
end
|
35
41
|
|
36
|
-
def
|
37
|
-
@editable.
|
42
|
+
def each(&block)
|
43
|
+
@editable.each { |e| block.call(e) }
|
38
44
|
end
|
39
45
|
|
40
46
|
def to_json
|
@@ -42,12 +48,15 @@ module Former
|
|
42
48
|
end
|
43
49
|
|
44
50
|
def to_html
|
45
|
-
|
46
|
-
|
51
|
+
return @html.text if @nothtml
|
52
|
+
# nokogiri pads w/ html/body elements
|
53
|
+
@html.xpath("/html/body").children.map { |c|
|
54
|
+
c.serialize(:save_with => Nokogiri::XML::Node::SaveOptions::AS_HTML).strip
|
55
|
+
}.join
|
47
56
|
end
|
48
57
|
|
49
58
|
def []=(index, value)
|
50
|
-
@editable[index].
|
59
|
+
@editable[index].value = value
|
51
60
|
end
|
52
61
|
|
53
62
|
# vals should be { :former_0 => 'value', :former_1 => 'value two', ... }
|
data/lib/former/element.rb
CHANGED
@@ -1,15 +1,12 @@
|
|
1
|
-
require 'action_view'
|
2
1
|
require 'json'
|
3
2
|
|
4
3
|
module Former
|
5
4
|
class Element
|
6
|
-
|
7
|
-
attr_reader :node, :query, :block
|
5
|
+
attr_reader :node, :query
|
8
6
|
|
9
|
-
def initialize(node, query, index
|
7
|
+
def initialize(node, query, index)
|
10
8
|
@node = node
|
11
9
|
@query = query
|
12
|
-
@block = block
|
13
10
|
@index = index
|
14
11
|
end
|
15
12
|
|
@@ -22,14 +19,11 @@ module Former
|
|
22
19
|
h.to_json
|
23
20
|
end
|
24
21
|
|
25
|
-
def
|
26
|
-
|
27
|
-
return @block.call(@node, name) unless @block.nil?
|
28
|
-
value = (@query == :text) ? @node.text : @node[@query]
|
29
|
-
tag(:input, :type => 'text', :name => name, :value => value)
|
22
|
+
def value
|
23
|
+
@query == :text ? @node.content : @node[@query]
|
30
24
|
end
|
31
25
|
|
32
|
-
def
|
26
|
+
def value=(value)
|
33
27
|
if @query == :text
|
34
28
|
@node.content = value
|
35
29
|
else
|
data/lib/former/version.rb
CHANGED
data/test/builder_test.rb
CHANGED
@@ -13,12 +13,36 @@ class AllTextParser < Former::Builder
|
|
13
13
|
text
|
14
14
|
end
|
15
15
|
|
16
|
+
class AllTextParserNotBlank < Former::Builder
|
17
|
+
attr "a.important", :href
|
18
|
+
attr("img", :src)
|
19
|
+
text { |e| not e.text.strip.empty? }
|
20
|
+
end
|
21
|
+
|
16
22
|
class BuilderTest < Test::Unit::TestCase
|
17
23
|
def setup
|
18
24
|
@html_txt = '<p>some text<a class="important" href="http://alink.com">some link text<img src="/an/image/path"></a>last text</p>'
|
19
25
|
@parser = Parser.new @html_txt
|
20
26
|
end
|
21
27
|
|
28
|
+
def test_ignore_blank_fields
|
29
|
+
p = AllTextParserNotBlank.new "<p>\n</p><h1> </h1><p> some text </p>"
|
30
|
+
assert_equal p.length, 1
|
31
|
+
p[0] = "other text"
|
32
|
+
assert_equal "<p>\n</p><h1> </h1><p>other text</p>", p.to_html
|
33
|
+
|
34
|
+
p = AllTextParserNotBlank.new "<p>\n</p><h1> </h1> some text <p></p>"
|
35
|
+
assert_equal p.length, 1
|
36
|
+
p[0] = "other text"
|
37
|
+
assert_equal "<p>\n</p><h1> </h1>other text<p></p>", p.to_html
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_nohtml
|
41
|
+
p = Parser.new "some text that is long, and contains stuff!"
|
42
|
+
p.set_values :former_0 => "some other text"
|
43
|
+
assert_equal p.to_html, "some other text"
|
44
|
+
end
|
45
|
+
|
22
46
|
def test_parsing
|
23
47
|
assert_equal @parser.editable.length, 5
|
24
48
|
|
@@ -42,6 +66,6 @@ class BuilderTest < Test::Unit::TestCase
|
|
42
66
|
|
43
67
|
# now set it back
|
44
68
|
@parser.set_values :former_0 => "some text"
|
45
|
-
@parser
|
69
|
+
assert_equal @parser.to_html, @html_txt
|
46
70
|
end
|
47
71
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: former
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-
|
12
|
+
date: 2014-03-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: actionpack
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 4.0.3
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 4.0.3
|
30
14
|
- !ruby/object:Gem::Dependency
|
31
15
|
name: nokogiri
|
32
16
|
requirement: !ruby/object:Gem::Requirement
|
@@ -83,6 +67,8 @@ extensions: []
|
|
83
67
|
extra_rdoc_files: []
|
84
68
|
files:
|
85
69
|
- .gitignore
|
70
|
+
- .ruby-gemset
|
71
|
+
- .ruby-version
|
86
72
|
- Gemfile
|
87
73
|
- LICENSE.txt
|
88
74
|
- README.rdoc
|
@@ -108,7 +94,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
94
|
version: '0'
|
109
95
|
segments:
|
110
96
|
- 0
|
111
|
-
hash:
|
97
|
+
hash: 4051511182999902248
|
112
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
99
|
none: false
|
114
100
|
requirements:
|
@@ -117,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
103
|
version: '0'
|
118
104
|
segments:
|
119
105
|
- 0
|
120
|
-
hash:
|
106
|
+
hash: 4051511182999902248
|
121
107
|
requirements: []
|
122
108
|
rubyforge_project:
|
123
109
|
rubygems_version: 1.8.25
|