formless 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/README.md +74 -0
- data/config.ru +8 -0
- data/formless.gemspec +18 -0
- data/lib/formless.rb +125 -0
- data/spec/form.html +45 -0
- data/spec/formless_spec.rb +135 -0
- data/spec/helper.rb +15 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 81893b813f0da1305af226192c0715a5f63f883e
|
4
|
+
data.tar.gz: 9e2b6cc25e49ad246db6e6519862378d8a4fbd15
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2161405866eb39ee0dc998818e8ea848b5d48b511a48a279627ddaa53bd21e763f51f2709b0dc2ca9a5db1c1ed150e3153c67c4a7e162cf991d42ae197808c4e
|
7
|
+
data.tar.gz: 3a6230c67266f323827608a1ff9baa2f2cacba1fa18cdf36fda32e7b8785eb11f8e76fdf7f6e2ed5e55e7d373570a0b34de695cfc13eb0d9e200782b1ed59906
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
Formless
|
2
|
+
========
|
3
|
+
|
4
|
+
Formless provides a means to populate forms without the need for anything other than plain-old HTML. It removes the requirement for form population logic within your views, serving as a complete replacement for form builders.
|
5
|
+
|
6
|
+
Formless can be used with any existing libraries or frameworks. Its only dependancy is Nokogiri.
|
7
|
+
|
8
|
+
|
9
|
+
In Action
|
10
|
+
---------
|
11
|
+
Install it...
|
12
|
+
|
13
|
+
gem install formless
|
14
|
+
|
15
|
+
Take some HTML...
|
16
|
+
|
17
|
+
<!DOCTYPE html>
|
18
|
+
<html>
|
19
|
+
<body>
|
20
|
+
<h1>Edit Person</h1>
|
21
|
+
<form id="edit_form" method="POST" action="./">
|
22
|
+
<input type="text" name="full_name" />
|
23
|
+
<input type="number" min="0" name="age" />
|
24
|
+
<label><input type="radio" name="gender" value="m"> Male</label>
|
25
|
+
<label><input type="radio" name="gender" value="f"> Female</label>
|
26
|
+
<select name="region">
|
27
|
+
<option>America</option>
|
28
|
+
<option>Europe</option>
|
29
|
+
<option>Oceania</option>
|
30
|
+
</select>
|
31
|
+
<input type="submit" value="Submit" />
|
32
|
+
</form>
|
33
|
+
</body>
|
34
|
+
</html>
|
35
|
+
|
36
|
+
And populate it...
|
37
|
+
|
38
|
+
selector = '#edit_form'
|
39
|
+
values = {name: 'Jeffrey', age: 29, gender: 'm', region: 'Europe'}
|
40
|
+
FormPopulator.new('<html>...</html>', selector).populate!(values).to_s #=> <!DOCTYPE html><html> ... </html>
|
41
|
+
|
42
|
+
|
43
|
+
How It Works
|
44
|
+
------------
|
45
|
+
Nokogiri is used to parse the given HTML into a data structure that can be easily and reliably operated on. The keys in the provided hash are mapped to the `name` attribute of HTML elements. A collection of _field setters_, defaulting to `Formless::FieldSetters` are responsible for correctly setting the various field types, whilst _formatters_, defaulting to `Formless::Formatters` provide an opportunity to process the value before setting it, such as formatting dates.
|
46
|
+
|
47
|
+
|
48
|
+
Performance
|
49
|
+
-----------
|
50
|
+
Convenience is prioritised over performance; there are many less convenient but better performing solutions if that's your priority. With that said, there are ways to optimise your use of Formless. The most obvious optimisation is to re-use your Formless or Nokogiri NodeSet instances, to save re-parsing your HTML:
|
51
|
+
|
52
|
+
@form ||= Formless.new('...')
|
53
|
+
@form.populate({name: 'Bill', age: 31}).to_s #=> <!DOCTYPE html><html> ... </html>
|
54
|
+
|
55
|
+
Formless also provides two complementary `populate` methods. `populate!` modifies the nodeset associated with the Formless instance, whilst `populate` works on a copy of that nodeset. Where performance is important, `populate!` should be used. It's important to note however that you must explicitly set a field to a value for that field to be reset, so extra care must be taken:
|
56
|
+
|
57
|
+
@form ||= Formless.new('...')
|
58
|
+
@form.populate!({name: 'Bill', age: 31})
|
59
|
+
@form.populate!({name: 'John'}) #=> Age is still set to 31
|
60
|
+
@form.populate!({name: 'John', age: nil}) #=> Age is now set to empty
|
61
|
+
|
62
|
+
|
63
|
+
Comprehensive
|
64
|
+
-------------
|
65
|
+
Formless is intended to provide comprehensive support for HTML5 forms. Any contradiction to this is considered a bug. To summarise:
|
66
|
+
|
67
|
+
* All HTML5 input fields, including:
|
68
|
+
* Checkboxes
|
69
|
+
* Radio buttons
|
70
|
+
* Password's which are not populated by default
|
71
|
+
* Date and time fields: date, datetime, datetime-local, week, month
|
72
|
+
* Textarea fields
|
73
|
+
* Select fields, including multi-select fields
|
74
|
+
* Common-name fields, such as for use with the array idiom, e.g. name="favourites[]"
|
data/config.ru
ADDED
data/formless.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
require 'formless'
|
3
|
+
|
4
|
+
Gem::Specification.new 'formless', Formless::VERSION do |s|
|
5
|
+
s.summary = "Unobtrusive form populator for web applications."
|
6
|
+
s.description = "Completely transparent, unobtrusive form populator for web applications and content scrapers."
|
7
|
+
s.authors = ["Tom Wardrop"]
|
8
|
+
s.email = "tom@tomwardrop.com"
|
9
|
+
s.homepage = "http://github.com/wardrop/Formless"
|
10
|
+
s.files = Dir.glob(`git ls-files`.split("\n") - %w[.gitignore])
|
11
|
+
s.test_files = Dir.glob('spec/**/*_spec.rb')
|
12
|
+
s.rdoc_options = %w[--line-numbers --inline-source --title Scorched --encoding=UTF-8]
|
13
|
+
|
14
|
+
s.required_ruby_version = '>= 1.9.3'
|
15
|
+
|
16
|
+
s.add_dependency 'nokogiri', '~> 1.5'
|
17
|
+
s.add_development_dependency 'rspec', '~> 2.9'
|
18
|
+
end
|
data/lib/formless.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
class Formless
|
5
|
+
VERSION = '0.1'
|
6
|
+
DATE_FORMAT = '%d/%m/%Y'
|
7
|
+
DATETIME_FORMAT = '%d/%m/%Y %l:%M%P'
|
8
|
+
|
9
|
+
FieldSetters = {
|
10
|
+
textarea: proc { |node, values|
|
11
|
+
node.content = values.shift
|
12
|
+
},
|
13
|
+
radio: proc { |node, values|
|
14
|
+
node.delete('checked')
|
15
|
+
node['checked'] = 'checked' if values.include? node['value']
|
16
|
+
},
|
17
|
+
checkbox: proc { |node, values|
|
18
|
+
node.delete('checked')
|
19
|
+
node['checked'] = 'checked' if values.include? node['value']
|
20
|
+
},
|
21
|
+
select: proc { |node, values|
|
22
|
+
matches = node.css('option').to_a.each { |n| n.delete('selected') }.find_all do |n|
|
23
|
+
n.has_attribute?('value') ? values.include?(n['value']) : values.include?(n.content)
|
24
|
+
end
|
25
|
+
if !matches.empty?
|
26
|
+
if node.has_attribute? 'multiple'
|
27
|
+
matches.each { |n| n['selected'] = 'selected' }
|
28
|
+
else
|
29
|
+
matches.first['selected'] = 'selected'
|
30
|
+
end
|
31
|
+
elsif !node.has_attribute?('multiple') && value = values.compact.first
|
32
|
+
node << node.document.create_element('option', value, selected: 'selected')
|
33
|
+
end
|
34
|
+
},
|
35
|
+
password: proc { |node, values|
|
36
|
+
node['value'] = values.shift if options[:populate_passwords]
|
37
|
+
},
|
38
|
+
default: proc { |node, values|
|
39
|
+
node['value'] = values.shift
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
Formatters = {
|
44
|
+
[Date, Time] => proc { |node, value|
|
45
|
+
case node['type']
|
46
|
+
when 'date'
|
47
|
+
value.strftime('%F')
|
48
|
+
when 'datetime'
|
49
|
+
value.strftime('%FT%T%z')
|
50
|
+
when 'datetime-local'
|
51
|
+
value.strftime('%FT%T')
|
52
|
+
when 'week'
|
53
|
+
value.strftime('%G-W%V')
|
54
|
+
when 'month'
|
55
|
+
value.strftime('%G-%m')
|
56
|
+
else
|
57
|
+
(value.respond_to? :hour) ? value.strftime(DATETIME_FORMAT) : value.strftime(DATE_FORMAT)
|
58
|
+
end
|
59
|
+
},
|
60
|
+
nil => proc { '' }
|
61
|
+
}
|
62
|
+
|
63
|
+
attr_accessor :selector
|
64
|
+
attr_reader :options
|
65
|
+
attr_reader :nodeset
|
66
|
+
|
67
|
+
def nodeset=(html)
|
68
|
+
@nodeset = case html
|
69
|
+
when Nokogiri::XML::Node
|
70
|
+
Nokogiri::XML::NodeSet.new(html, [html])
|
71
|
+
when Nokogiri::XML::NodeSet
|
72
|
+
html
|
73
|
+
else
|
74
|
+
doc = Nokogiri.parse(html)
|
75
|
+
Nokogiri::XML::NodeSet.new(doc, [doc])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize(html, selector = nil, **options)
|
80
|
+
self.nodeset = html
|
81
|
+
self.selector = selector
|
82
|
+
@options = {
|
83
|
+
field_setters: FieldSetters,
|
84
|
+
formatters: Formatters,
|
85
|
+
populate_passwords: false
|
86
|
+
}.merge!(options)
|
87
|
+
end
|
88
|
+
|
89
|
+
def field_setters
|
90
|
+
self.options[:field_setters]
|
91
|
+
end
|
92
|
+
|
93
|
+
def formatters
|
94
|
+
self.options[:formatters]
|
95
|
+
end
|
96
|
+
|
97
|
+
def populate(values, selector = nil, nodeset = self.nodeset)
|
98
|
+
populate!(values, selector, Nokogiri::XML::NodeSet.new(nodeset.document, nodeset.to_a.map! { |n| n.dup }))
|
99
|
+
end
|
100
|
+
|
101
|
+
def populate!(values, selector = nil, nodeset = self.nodeset)
|
102
|
+
nodeset = selector ? nodeset.css(selector) : nodeset
|
103
|
+
values.each do |field, value|
|
104
|
+
nodes = nodeset.css(%{[name="#{field}"]})
|
105
|
+
nodes = nodeset.css(%{[name="#{field}[]"]}) if nodes.empty?
|
106
|
+
nodes.each { |n| set_field(n, value) }
|
107
|
+
end
|
108
|
+
nodeset
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def set_field(node, value)
|
114
|
+
setter = field_setters[(node['type'] || node.name).to_sym] || FieldSetters[:default]
|
115
|
+
instance_exec(node, [*format_value(node, value)], &setter)
|
116
|
+
end
|
117
|
+
|
118
|
+
def format_value(node, value)
|
119
|
+
condition, formatter = formatters.find do |conditions, block|
|
120
|
+
[*conditions].find { |c| c === value }
|
121
|
+
end
|
122
|
+
formatter ? instance_exec(node, value, &formatter) : value
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
data/spec/form.html
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
5
|
+
<style>
|
6
|
+
label { display: block; }
|
7
|
+
</style>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<h1>Edit Person</h1>
|
11
|
+
<form id="edit_form" method="POST" action="./">
|
12
|
+
<label>Password: <input type="password" name="password"></label>
|
13
|
+
<label>Unknown: <input type="unknown" name="unknown"></label>
|
14
|
+
<label>Full Name: <input type="text" name="full_name"></label>
|
15
|
+
<label>Age: <input type="number" min="0" name="age"></label>
|
16
|
+
<label><input type="radio" name="gender" value="m"> Male</label>
|
17
|
+
<label><input type="radio" name="gender" value="f"> Female</label>
|
18
|
+
<label>Region:
|
19
|
+
<select name="region">
|
20
|
+
<option>America</option>
|
21
|
+
<option>Europe</option>
|
22
|
+
<option value="Australia">Oceania</option>
|
23
|
+
</select>
|
24
|
+
</label>
|
25
|
+
<label>Foods:
|
26
|
+
<select name="foods[]" multiple>
|
27
|
+
<option>Pizza</option>
|
28
|
+
<option>Burger</option>
|
29
|
+
<option>Chips</option>
|
30
|
+
</select>
|
31
|
+
</label>
|
32
|
+
<textarea name="hobbies"></textarea>
|
33
|
+
<label>Birthday: <input type="date" name="birthday"></label>
|
34
|
+
<label>Breakfast: <input type="datetime" name="breakfast"></label>
|
35
|
+
<label>Dinner: <input type="datetime-local" name="dinner"></label>
|
36
|
+
<label>Favourite Week: <input type="week" name="favourite_week"></label>
|
37
|
+
<label>Favourite Month: <input type="month" name="favourite_month"></label>
|
38
|
+
<label><input type="checkbox" name="colours[]" value="red">Red</label>
|
39
|
+
<label><input type="checkbox" name="colours[]" value="green">Green</label>
|
40
|
+
<label><input type="checkbox" name="colours[]" value="blue">Blue</label>
|
41
|
+
<label><input type="checkbox" name="subscribe" value="yes"> Subscribe</label>
|
42
|
+
<div><input type="submit" value="Submit"></div>
|
43
|
+
</form>
|
44
|
+
</body>
|
45
|
+
</html>
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require_relative './helper.rb'
|
2
|
+
|
3
|
+
describe Formless do
|
4
|
+
it "can take a html string" do
|
5
|
+
form = Formless.new(html)
|
6
|
+
form.nodeset.should be_a(Nokogiri::XML::NodeSet)
|
7
|
+
form.nodeset.to_s.strip.should == html.strip
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can take a nokogiri node" do
|
11
|
+
doc = Nokogiri.parse(html)
|
12
|
+
form = Formless.new(doc)
|
13
|
+
form.nodeset.should be_a(Nokogiri::XML::NodeSet)
|
14
|
+
form.nodeset.document.should == doc
|
15
|
+
form.nodeset[0].should == doc
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can take a nokogiri nodeset" do
|
19
|
+
nodeset = Nokogiri.parse(html).css('> *')
|
20
|
+
form = Formless.new(nodeset)
|
21
|
+
form.nodeset.should == nodeset
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:form) do
|
25
|
+
Formless.new(html)
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "field populating" do
|
29
|
+
example "default/unknown" do
|
30
|
+
form.populate(unknown: 'yay').css('[name=unknown]')[0]['value'].should == 'yay'
|
31
|
+
end
|
32
|
+
|
33
|
+
example "text" do
|
34
|
+
form.populate(full_name: 'William Fisher').css('[name=full_name]')[0]['value'].should == 'William Fisher'
|
35
|
+
end
|
36
|
+
|
37
|
+
example "textarea" do
|
38
|
+
form.populate(hobbies: 'fishing, camping').css('[name=hobbies]')[0].content.should == 'fishing, camping'
|
39
|
+
end
|
40
|
+
|
41
|
+
example "radio" do
|
42
|
+
form.populate(gender: 'f').css('[name=gender][value=f]')[0].has_attribute?('checked').should == true
|
43
|
+
end
|
44
|
+
|
45
|
+
example "checkbox" do
|
46
|
+
form.populate(subscribe: 'yes').css('[name=subscribe]')[0].has_attribute?('checked').should == true
|
47
|
+
end
|
48
|
+
|
49
|
+
example "select" do
|
50
|
+
form.populate(region: 'Europe').
|
51
|
+
css('[name=region] > option').find { |n| n.has_attribute?('selected') }.text.should == 'Europe'
|
52
|
+
form.populate(region: 'Australia').
|
53
|
+
css('[name=region] > option').find { |n| n.has_attribute?('selected') }.text.should == 'Oceania'
|
54
|
+
# Will add non-existant values
|
55
|
+
form.populate(region: 'Middle East').
|
56
|
+
css('[name=region] > option').find { |n| n.has_attribute?('selected') }.text.should == 'Middle East'
|
57
|
+
end
|
58
|
+
|
59
|
+
example "multi-select" do
|
60
|
+
# Also tests order independance
|
61
|
+
form.populate('foods[]' => ['Chips', 'Pizza']).css('[name="foods[]"] > option').select { |n|
|
62
|
+
n.has_attribute?('selected')
|
63
|
+
}.map { |v| v.text }.should == ['Pizza', 'Chips']
|
64
|
+
|
65
|
+
# Will automatically append square brackets if not found.
|
66
|
+
form.populate('foods' => ['Pizza', 'Chips']).css('[name="foods[]"] > option').select { |n|
|
67
|
+
n.has_attribute?('selected')
|
68
|
+
}.map { |v| v.text }.should == ['Pizza', 'Chips']
|
69
|
+
end
|
70
|
+
|
71
|
+
example "date" do
|
72
|
+
date = Date.today
|
73
|
+
form.populate(birthday: date).css('[name=birthday]')[0]['value'].should == date.strftime('%F')
|
74
|
+
end
|
75
|
+
|
76
|
+
example "datetime" do
|
77
|
+
time = DateTime.now
|
78
|
+
form.populate(breakfast: time).css('[name=breakfast]')[0]['value'].should == time.strftime('%FT%T%z')
|
79
|
+
end
|
80
|
+
|
81
|
+
example "datetime-local" do
|
82
|
+
time = DateTime.now
|
83
|
+
form.populate(dinner: time).css('[name=dinner]')[0]['value'].should == time.strftime('%FT%T')
|
84
|
+
end
|
85
|
+
|
86
|
+
example "month" do
|
87
|
+
time = DateTime.new(2013, 9, 2, 11, 30, 15)
|
88
|
+
form.populate(favourite_month: time).css('[name=favourite_month]')[0]['value'].should == '2013-09'
|
89
|
+
end
|
90
|
+
|
91
|
+
example "week" do
|
92
|
+
time = DateTime.new(2013, 9, 2, 11, 30, 15)
|
93
|
+
form.populate(favourite_week: time).css('[name=favourite_week]')[0]['value'].should == '2013-W36'
|
94
|
+
end
|
95
|
+
|
96
|
+
example "array-like field names" do
|
97
|
+
form.populate('colours[]' => %w{blue red}).css('[name="colours[]"][checked=checked]').map { |n| n['value'] }.should == ['red', 'blue']
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "outputs a Nokogiri nodeset" do
|
102
|
+
form.populate({}).should be_a(Nokogiri::XML::NodeSet)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can modify the original nodeset, or a copy" do
|
106
|
+
form.populate!({full_name: 'Bob Bobinski'}).css('[name=full_name]')[0]['value'].should == 'Bob Bobinski'
|
107
|
+
form.populate({full_name: 'Harold Haroldo'}).css('[name=full_name]')[0]['value'].should == 'Harold Haroldo'
|
108
|
+
form.populate!({}).css('[name=full_name]')[0]['value'].should == 'Bob Bobinski'
|
109
|
+
end
|
110
|
+
|
111
|
+
it "can take a CSS selector to narrow the nodeset" do
|
112
|
+
form.populate!({full_name: 'Tony Jones', hobbies: 'stuff'}, 'input')
|
113
|
+
form.nodeset.css('[name=full_name]')[0]['value'].should == 'Tony Jones'
|
114
|
+
form.nodeset.css('[name=hobbies]')[0].content.should_not == 'stuff'
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "configuration" do
|
118
|
+
it "can toggle whether password fields are populated" do
|
119
|
+
form.populate(password: 'ilovemonkeys').css('[name=password]')[0]['value'].should == nil
|
120
|
+
form.options[:populate_passwords] = true
|
121
|
+
form.populate(password: 'ilovemonkeys').css('[name=password]')[0]['value'].should == 'ilovemonkeys'
|
122
|
+
end
|
123
|
+
|
124
|
+
it "allows field setters to be overridden" do
|
125
|
+
form.options[:field_setters] = Formless::FieldSetters.merge(password: proc { |node| node['value'] = 'meow' })
|
126
|
+
form.populate(password: 'ilovemonkeys').css('[name=password]')[0]['value'].should == 'meow'
|
127
|
+
end
|
128
|
+
|
129
|
+
it "allows formatters to be overridden" do
|
130
|
+
form.options[:formatters] = Formless::Formatters.merge('yes' => proc { |n,v| v == 'yes' ? 1 : 0 })
|
131
|
+
form.populate({full_name: 'yes'}).css('[name=full_name]')[0]['value'].should == '1'
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rack/test'
|
2
|
+
require_relative '../lib/formless.rb'
|
3
|
+
|
4
|
+
module GlobalConfig
|
5
|
+
extend RSpec::SharedContext
|
6
|
+
let(:html) do
|
7
|
+
open(File.join __dir__, 'form.html').read
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
RSpec.configure do |c|
|
12
|
+
c.alias_example_to :they
|
13
|
+
# c.backtrace_clean_patterns = []
|
14
|
+
c.include GlobalConfig
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: formless
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Wardrop
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.9'
|
41
|
+
description: Completely transparent, unobtrusive form populator for web applications
|
42
|
+
and content scrapers.
|
43
|
+
email: tom@tomwardrop.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- Gemfile
|
49
|
+
- README.md
|
50
|
+
- config.ru
|
51
|
+
- formless.gemspec
|
52
|
+
- lib/formless.rb
|
53
|
+
- spec/form.html
|
54
|
+
- spec/formless_spec.rb
|
55
|
+
- spec/helper.rb
|
56
|
+
homepage: http://github.com/wardrop/Formless
|
57
|
+
licenses: []
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --line-numbers
|
62
|
+
- --inline-source
|
63
|
+
- --title
|
64
|
+
- Scorched
|
65
|
+
- --encoding=UTF-8
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 1.9.3
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.0.0
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Unobtrusive form populator for web applications.
|
84
|
+
test_files:
|
85
|
+
- spec/formless_spec.rb
|