telvue-rsolr 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RSolr::Document do
4
+ describe RSolr::Field do
5
+ describe ".instance" do
6
+ subject { RSolr::Field }
7
+
8
+ it "uses the class name of the field value" do
9
+ expect(subject.instance({}, Time.new)).to be_a_kind_of(RSolr::TimeField)
10
+ end
11
+
12
+ it "uses the provided type option when it is a class" do
13
+ expect(subject.instance({:type => RSolr::TimeField}, nil)).to be_a_kind_of(RSolr::TimeField)
14
+ end
15
+
16
+ it "uses the provided type option when it is a string" do
17
+ expect(subject.instance({:type => 'Time'}, nil)).to be_a_kind_of(RSolr::TimeField)
18
+ end
19
+
20
+ it "falls back to the base Field class" do
21
+ expect(subject.instance({:type => 'UndefinedType'}, nil)).to be_a_kind_of(RSolr::Field)
22
+ end
23
+
24
+ it "defaults to the base Field class" do
25
+ expect(subject.instance({}, nil)).to be_a_kind_of(RSolr::Field)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe RSolr::TimeField do
31
+ it "convert value to string" do
32
+ time_value = Time.utc(2013, 9, 11, 18, 10, 0)
33
+ expect(RSolr::Field.instance({}, time_value).value).to eq '2013-09-11T18:10:00Z'
34
+ end
35
+
36
+ it "convert time to UTC" do
37
+ time_value = Time.new(2013, 9, 11, 18, 10, 0, '+02:00')
38
+ expect(RSolr::Field.instance({}, time_value).value).to eq '2013-09-11T16:10:00Z'
39
+ end
40
+ end
41
+
42
+ describe RSolr::DateField do
43
+ it "convert value to string" do
44
+ date_value = Date.new(2013, 9, 11)
45
+ expect(RSolr::Field.instance({}, date_value).value).to eq '2013-09-11T00:00:00Z'
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RSolr::Error do
4
+ def generate_error_with_backtrace(request, response)
5
+ raise RSolr::Error::Http.new request, response
6
+ rescue RSolr::Error::Http => exception
7
+ exception
8
+ end
9
+ let (:response_lines) { (1..15).to_a.map { |i| "line #{i}" } }
10
+ let(:request) { double :[] => "mocked" }
11
+ let(:response_body) { response_lines.join("\n") }
12
+ let(:response) {{
13
+ :body => response_body,
14
+ :status => 400
15
+ }}
16
+ subject { generate_error_with_backtrace(request, response).to_s }
17
+
18
+ context "when the response body is wrapped in a <pre> element" do
19
+ let(:response_body) { "<pre>" + response_lines.join("\n") + "</pre>" }
20
+
21
+ it "only shows the first eleven lines of the response" do
22
+ expect(subject).to match(/line 1\n.+line 11\n\n/m)
23
+ end
24
+
25
+ context "when the response is one line long" do
26
+ let(:response_body) { "<pre>failed</pre>" }
27
+ it { should match(/Error: failed/) }
28
+ end
29
+ end
30
+
31
+ context "when the response body is not wrapped in a <pre> element" do
32
+
33
+ it "only shows the first eleven lines of the response" do
34
+ expect(subject).to match(/line 1\n.+line 11\n\n/m)
35
+ end
36
+
37
+ context "when the response is one line long" do
38
+ let(:response_body) { 'failed' }
39
+ it { should match(/Error: failed/) }
40
+ end
41
+ context "when the response body contains a msg key" do
42
+ let(:msg) { "'org.apache.solr.search.SyntaxError: Cannot parse \\':false\\': Encountered \" \":\" \": \"\" at line 1, column 0.'" }
43
+ let(:response_body) { (response_lines << "'error'=>{'msg'=> #{msg}").join("\n") }
44
+ it { should include msg }
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,198 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RSolr::JSON do
4
+
5
+ let(:generator){ RSolr::JSON::Generator.new }
6
+
7
+ context :add do
8
+ # add a single hash ("doc")
9
+ it 'should create an add from a hash' do
10
+ data = {
11
+ :id=>"1",
12
+ :name=>'matt'
13
+ }
14
+ message = JSON.parse(generator.add(data), symbolize_names: true)
15
+ expect(message.length).to eq 1
16
+ expect(message.first).to eq data
17
+ end
18
+
19
+ # add an array of hashes
20
+ it 'should create many adds from an array of hashes' do
21
+ data = [
22
+ {
23
+ :id=>"1",
24
+ :name=>'matt'
25
+ },
26
+ {
27
+ :id=>"2",
28
+ :name=>'sam'
29
+ }
30
+ ]
31
+ message = JSON.parse(generator.add(data), symbolize_names: true)
32
+ expect(message).to eq data
33
+ end
34
+
35
+ it 'should yield a Document object when #add is called with a block' do
36
+ documents = [{:id=>1, :name=>'sam', :cat=>['cat 1', 'cat 2']}]
37
+ result = generator.add(documents) do |doc|
38
+ doc.field_by_name(:name).attrs[:boost] = 10
39
+ end
40
+
41
+ message = JSON.parse(result, symbolize_names: true)
42
+
43
+ expect(message.length).to eq 1
44
+ expect(message.first).to include name: { boost: 10, value: 'sam' }
45
+ end
46
+
47
+ context 'with add_attr' do
48
+ it 'should create an add command with the attributes from a hash' do
49
+ data = {
50
+ :id=>"1",
51
+ :name=>'matt'
52
+ }
53
+ message = JSON.parse(generator.add(data, boost: 1), symbolize_names: true)
54
+ expect(message).to include :add
55
+ expect(message[:add][:doc]).to eq data
56
+ expect(message[:add][:boost]).to eq 1
57
+ end
58
+
59
+ it 'should create multiple add command with the attributes from a hash' do
60
+ data = [
61
+ {
62
+ :id=>"1",
63
+ :name=>'matt'
64
+ },
65
+ {
66
+ :id=>"2",
67
+ :name=>'sam'
68
+ },
69
+ ]
70
+
71
+ # custom JSON object class to handle Solr's non-standard JSON command format
72
+ tmp = Class.new do
73
+ def initialize
74
+ @source ||= {}
75
+ end
76
+
77
+ def []=(k, v)
78
+ if k == :add
79
+ @source[k] ||= []
80
+ @source[k] << v.to_h
81
+ elsif v.class == self.class
82
+ @source[k] = v.to_h
83
+ else
84
+ @source[k] = v
85
+ end
86
+ end
87
+
88
+ def to_h
89
+ @source
90
+ end
91
+ end
92
+
93
+ request = generator.add(data, boost: 1)
94
+ message = JSON.parse(request, object_class: tmp, symbolize_names: true).to_h
95
+ expect(message[:add].length).to eq 2
96
+ expect(message[:add].map { |x| x[:doc] }).to eq data
97
+ end
98
+ end
99
+
100
+ it 'allows for atomic updates' do
101
+ data = {
102
+ foo: { set: 'Bar' }
103
+ }
104
+
105
+ message = JSON.parse(generator.add(data), symbolize_names: true)
106
+ expect(message.length).to eq 1
107
+ expect(message.first).to eq data
108
+ end
109
+
110
+ it 'supports nested child documents' do
111
+ data = {
112
+ _childDocuments_: [
113
+ {
114
+ id: 1
115
+ },
116
+ {
117
+ id: 2
118
+ }
119
+ ]
120
+ }
121
+
122
+ message = JSON.parse(generator.add(data), symbolize_names: true)
123
+ expect(message.length).to eq 1
124
+ expect(message.first).to eq data
125
+ end
126
+
127
+ it 'supports nested child documents with only a single document' do
128
+ data = {
129
+ _childDocuments_: [
130
+ {
131
+ id: 1
132
+ }
133
+ ]
134
+ }
135
+
136
+ message = JSON.parse(generator.add(data), symbolize_names: true)
137
+ expect(message.length).to eq 1
138
+ expect(message.first).to eq data
139
+ end
140
+ end
141
+
142
+ it 'should create multiple fields from array values' do
143
+ data = {
144
+ :id => "1",
145
+ :name => ['matt1', 'matt2']
146
+ }
147
+ message = JSON.parse(generator.add(data), symbolize_names: true)
148
+ expect(message.length).to eq 1
149
+ expect(message.first).to eq data
150
+ end
151
+
152
+ it 'should create multiple fields from array values with options' do
153
+ test_values = [nil, 'matt1', 'matt2']
154
+ message = JSON.parse(
155
+ generator.add(id: '1') { |doc| doc.add_field(:name, test_values, boost: 3) },
156
+ symbolize_names: true
157
+ )
158
+ expect(message).to eq [{ id: '1', name: { boost: 3, value: test_values } }]
159
+ end
160
+
161
+ describe '#commit' do
162
+ it 'generates a commit command' do
163
+ expect(JSON.parse(generator.commit, symbolize_names: true)).to eq(commit: {})
164
+ end
165
+ end
166
+
167
+ describe '#optimize' do
168
+ it 'generates a optimize command' do
169
+ expect(JSON.parse(generator.optimize, symbolize_names: true)).to eq(optimize: {})
170
+ end
171
+ end
172
+
173
+ describe '#rollback' do
174
+ it 'generates a rollback command' do
175
+ expect(JSON.parse(generator.rollback, symbolize_names: true)).to eq(rollback: {})
176
+ end
177
+ end
178
+
179
+ describe '#delete_by_id' do
180
+ it 'generates a delete_by_id command for single documents' do
181
+ expect(JSON.parse(generator.delete_by_id('x'), symbolize_names: true)).to eq(delete: 'x')
182
+ end
183
+
184
+ it 'generates a delete_by_id command for an array of documents' do
185
+ expect(JSON.parse(generator.delete_by_id(%w(a b c)), symbolize_names: true)).to eq(delete: %w(a b c))
186
+ end
187
+ end
188
+
189
+ describe '#delete_by_query' do
190
+ it 'generates a delete_by_id command for single documents' do
191
+ expect(JSON.parse(generator.delete_by_query('id:x'), symbolize_names: true)).to eq(delete: { query: 'id:x'})
192
+ end
193
+
194
+ it 'generates a delete_by_id command for an array of documents' do
195
+ expect(JSON.parse(generator.delete_by_id(%w(a b c)), symbolize_names: true)).to eq(delete: %w(a b c))
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RSolr::Client do
4
+ context "build_paginated_request" do
5
+ it "should create the proper solr params and query string" do
6
+ c = RSolr::Client.new(nil, {})#.extend(RSolr::Pagination::Client)
7
+ r = c.build_paginated_request 3, 25, "select", {:params => {:q => "test"}}
8
+ #r[:page].should == 3
9
+ #r[:per_page].should == 25
10
+ expect(r[:params]["start"]).to eq(50)
11
+ expect(r[:params]["rows"]).to eq(25)
12
+ expect(r[:uri].query).to match(/rows=25/)
13
+ expect(r[:uri].query).to match(/start=50/)
14
+ end
15
+ end
16
+ context "paginate" do
17
+ it "should build a paginated request context and call execute" do
18
+ c = RSolr::Client.new(nil, {})#.extend(RSolr::Pagination::Client)
19
+ expect(c).to receive(:execute).with(hash_including({
20
+ #:page => 1,
21
+ #:per_page => 10,
22
+ :params => {
23
+ "rows" => 10,
24
+ "start" => 0,
25
+ :wt => :json
26
+ }
27
+ }))
28
+ c.paginate 1, 10, "select"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RSolr do
4
+
5
+ it "has a version that can be read via #version or VERSION" do
6
+ expect(RSolr.version).to eq(RSolr::VERSION)
7
+ end
8
+
9
+ context "connect" do
10
+ it "should return a RSolr::Client instance" do
11
+ expect(RSolr.connect).to be_a(RSolr::Client)
12
+ end
13
+ end
14
+
15
+ context '.solr_escape' do
16
+ it "adds backslash to Solr query syntax chars" do
17
+ # per http://lucene.apache.org/core/4_0_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Escaping_Special_Characters
18
+ special_chars = [ "+", "-", "&", "|", "!", "(", ")", "{", "}", "[", "]", "^", '"', "~", "*", "?", ":", "\\", "/" ]
19
+ escaped_str = RSolr.solr_escape("aa#{special_chars.join('aa')}aa")
20
+ special_chars.each { |c|
21
+ # note that the ruby code sending the query to Solr will un-escape the backslashes
22
+ # so the result sent to Solr is ultimately a single backslash in front of the particular character
23
+ expect(escaped_str).to match "\\#{c}"
24
+ }
25
+ end
26
+ it "leaves other chars alone" do
27
+ str = "nothing to see here; let's move along people."
28
+ expect(RSolr.solr_escape(str)).to eq str
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe RSolr::Uri do
4
+
5
+ let(:uri) { RSolr::Uri }
6
+
7
+ context '.params_to_solr' do
8
+ it "converts Hash to Solr query string w/o a starting ?" do
9
+ hash = {:q => "gold", :fq => ["mode:one", "level:2"]}
10
+ query = uri.params_to_solr hash
11
+ expect(query[0]).not_to eq(??)
12
+ [/q=gold/, /fq=mode%3Aone/, /fq=level%3A2/].each do |p|
13
+ expect(query).to match p
14
+ end
15
+ expect(query.split('&').size).to eq(3)
16
+ end
17
+ it 'should URL escape &' do
18
+ expect(uri.params_to_solr(:fq => "&")).to eq('fq=%26')
19
+ end
20
+
21
+ it 'should convert spaces to +' do
22
+ expect(uri.params_to_solr(:fq => "me and you")).to eq('fq=me+and+you')
23
+ end
24
+
25
+ it 'should URL escape complex queries, part 1' do
26
+ my_params = {'fq' => '{!raw f=field_name}crazy+\"field+value'}
27
+ expected = 'fq=%7B%21raw+f%3Dfield_name%7Dcrazy%2B%5C%22field%2Bvalue'
28
+ expect(uri.params_to_solr(my_params)).to eq(expected)
29
+ end
30
+
31
+ it 'should URL escape complex queries, part 2' do
32
+ my_params = {'q' => '+popularity:[10 TO *] +section:0'}
33
+ expected = 'q=%2Bpopularity%3A%5B10+TO+*%5D+%2Bsection%3A0'
34
+ expect(uri.params_to_solr(my_params)).to eq(expected)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,255 @@
1
+ require 'spec_helper'
2
+ require 'builder'
3
+ require 'nokogiri'
4
+
5
+ RSpec.describe RSolr::Xml do
6
+ let(:generator){ RSolr::Xml::Generator.new }
7
+
8
+ builder_engines = {
9
+ :builder => { :val => false, :class => Builder::XmlMarkup, :engine => Builder::XmlMarkup.new(:indent => 0, :margin => 0, :encoding => 'UTF-8') },
10
+ :nokogiri => { :val => true, :class => Nokogiri::XML::Builder, :engine => Nokogiri::XML::Builder.new }
11
+ }
12
+
13
+ [:builder,:nokogiri].each do |engine_name|
14
+ describe engine_name do
15
+ before :all do
16
+ @engine = builder_engines[engine_name]
17
+ @old_ng_setting = RSolr::Xml::Generator.use_nokogiri
18
+ RSolr::Xml::Generator.use_nokogiri = @engine[:val]
19
+ end
20
+
21
+ after :all do
22
+ RSolr::Xml::Generator.use_nokogiri = @old_ng_setting
23
+ end
24
+
25
+ before :each do
26
+ builder_engines.each_pair do |name,spec|
27
+ expect(spec[:class]).not_to receive(:new) unless name == engine_name
28
+ end
29
+ end
30
+
31
+ context :xml_engine do
32
+ it "should use #{engine_name}" do
33
+ expect(@engine[:class]).to receive(:new).and_return(@engine[:engine])
34
+ generator.send(:commit)
35
+ end
36
+ end
37
+
38
+ # call all of the simple methods...
39
+ # make sure the xml string is valid
40
+ # ensure the class is actually Solr::XML
41
+ [:optimize, :rollback, :commit].each do |meth|
42
+ it "#{meth} should generator xml" do
43
+ result = generator.send(meth)
44
+ expect(result).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><#{meth}/>")
45
+ end
46
+ end
47
+
48
+ context :add do
49
+
50
+ it 'should yield a Message::Document object when #add is called with a block' do
51
+ documents = [{:id=>1, :name=>'sam', :cat=>['cat 1', 'cat 2']}]
52
+ add_attrs = {:boost=>200.00}
53
+ result = generator.add(documents, add_attrs) do |doc|
54
+ doc.field_by_name(:name).attrs[:boost] = 10
55
+ end
56
+ expect(result).to match(%r(name="cat">cat 1</field>))
57
+ expect(result).to match(%r(name="cat">cat 2</field>))
58
+ expect(result).to match(%r(<add boost="200.0">))
59
+ expect(result).to match(%r(boost="10"))
60
+ expect(result).to match(%r(<field name="id">1</field>))
61
+ end
62
+
63
+ it 'should work for values that yield enumerators' do
64
+ documents = [{id: 1, cat: ['cat 1', 'cat 2'].to_enum}]
65
+ result = generator.add(documents)
66
+
67
+ expect(result).to match(%r(name="cat">cat 1</field>))
68
+ expect(result).to match(%r(name="cat">cat 2</field>))
69
+ end
70
+
71
+ # add a single hash ("doc")
72
+ it 'should create an add from a hash' do
73
+ data = {
74
+ :id=>1,
75
+ :name=>'matt'
76
+ }
77
+ result = generator.add(data)
78
+ expect(result).to match(/<field name="name">matt<\/field>/)
79
+ expect(result).to match(/<field name="id">1<\/field>/)
80
+ end
81
+
82
+ # add a single hash ("doc")
83
+ it 'should create an add from a hash formatted for atomic updates' do
84
+ data = {
85
+ :id=>1,
86
+ :name=> { set: 'matt' }
87
+ }
88
+ result = generator.add(data)
89
+ expect(result).to match(/<field name="name" update="set">matt<\/field>/)
90
+ expect(result).to match(/<field name="id">1<\/field>/)
91
+ end
92
+ it 'should remove a field from a hash formatted for atomic updates' do
93
+ data = {
94
+ :id => 1,
95
+ :name => nil
96
+ }
97
+ result = generator.add(data)
98
+ expect(result).to match(%r{<field name="name" null="true"})
99
+ expect(result).to match(/<field name="id">1<\/field>/)
100
+ end
101
+
102
+ # add an array of hashes
103
+ it 'should create many adds from an array of hashes' do
104
+ data = [
105
+ {
106
+ :id=>1,
107
+ :name=>'matt'
108
+ },
109
+ {
110
+ :id=>2,
111
+ :name=>'sam'
112
+ }
113
+ ]
114
+ message = generator.add(data)
115
+ expect(message).to match %r{<field name="name">matt</field>}
116
+ expect(message).to match %r{<field name="name">sam</field>}
117
+ end
118
+
119
+ # multiValue field support test, thanks to Fouad Mardini!
120
+ it 'should create multiple fields from array values' do
121
+ data = {
122
+ :id => 1,
123
+ :name => ['matt1', 'matt2']
124
+ }
125
+ result = generator.add(data)
126
+ expect(result).to match(/<field name="name">matt1<\/field>/)
127
+ expect(result).to match(/<field name="name">matt2<\/field>/)
128
+ end
129
+
130
+ it 'should allow for objects which can be casted to an array' do
131
+ name = double("name", to_ary: ['matt1', 'matt2'])
132
+ data = {
133
+ :id => 1,
134
+ :name => name
135
+ }
136
+ result = generator.add(data)
137
+ expect(result).to match(/<field name="name">matt1<\/field>/)
138
+ expect(result).to match(/<field name="name">matt2<\/field>/)
139
+ end
140
+
141
+ it 'should create an add from a single Message::Document' do
142
+ document = RSolr::Document.new
143
+ document.add_field('id', 1)
144
+ document.add_field('name', 'matt', :boost => 2.0)
145
+ result = generator.add(document)
146
+ expect(result).to match(Regexp.escape('<?xml version="1.0" encoding="UTF-8"?>'))
147
+ expect(result).to match(/<field name="id">1<\/field>/)
148
+ expect(result).to match Regexp.escape('boost="2.0"')
149
+ expect(result).to match Regexp.escape('name="name"')
150
+ expect(result).to match Regexp.escape('matt</field>')
151
+ end
152
+
153
+ it 'should create adds from multiple Message::Documents' do
154
+ documents = (1..2).map do |i|
155
+ doc = RSolr::Document.new
156
+ doc.add_field('id', i)
157
+ doc.add_field('name', "matt#{i}")
158
+ doc
159
+ end
160
+ result = generator.add(documents)
161
+ expect(result).to match(/<field name="name">matt1<\/field>/)
162
+ expect(result).to match(/<field name="name">matt2<\/field>/)
163
+ end
164
+
165
+ it 'supports nested child documents' do
166
+ data = {
167
+ :_childDocuments_ => [
168
+ {
169
+ :id => 1
170
+ },
171
+ {
172
+ :id => 2
173
+ }
174
+ ]
175
+ }
176
+
177
+ result = generator.add(data)
178
+ expect(result).to match(%r{<add><doc><doc>})
179
+ expect(result).to match(%r{<doc><field name="id">1</field></doc>})
180
+ expect(result).to match(%r{<doc><field name="id">2</field></doc>})
181
+ end
182
+ end
183
+
184
+ context :delete_by_id do
185
+
186
+ it 'should create a doc id delete' do
187
+ expect(generator.delete_by_id(10)).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>10</id></delete>")
188
+ end
189
+
190
+ it 'should create many doc id deletes' do
191
+ expect(generator.delete_by_id([1, 2, 3])).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><id>1</id><id>2</id><id>3</id></delete>")
192
+ end
193
+
194
+ end
195
+
196
+ context :delete_by_query do
197
+ it 'should create a query delete' do
198
+ expect(generator.delete_by_query('status:"LOST"')).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query>status:\"LOST\"</query></delete>")
199
+ end
200
+
201
+ it 'should create many query deletes' do
202
+ expect(generator.delete_by_query(['status:"LOST"', 'quantity:0'])).to eq("<?xml version=\"1.0\" encoding=\"UTF-8\"?><delete><query>status:\"LOST\"</query><query>quantity:0</query></delete>")
203
+ end
204
+ end
205
+
206
+ end
207
+ end
208
+
209
+ context :formatting do
210
+ it 'should format date objects into ISO 8601' do
211
+ data = {
212
+ dt: Date.new(1992, 03, 15)
213
+ }
214
+ result = generator.add(data)
215
+ expect(result).to match(/<field name="dt">1992-03-15T00:00:00Z<\/field>/)
216
+ end
217
+
218
+ it 'should format time objects into ISO 8601' do
219
+ data = {
220
+ dt: Time.new(1992, 03, 15, 16, 23, 55, 3600)
221
+ }
222
+ result = generator.add(data)
223
+ expect(result).to match(/<field name="dt">1992-03-15T15:23:55Z<\/field>/)
224
+ end
225
+
226
+ it 'should format datetime objects into ISO 8601' do
227
+ data = {
228
+ dt: DateTime.new(1992, 03, 15, 16, 23, 55, '+1')
229
+ }
230
+ result = generator.add(data)
231
+ expect(result).to match(/<field name="dt">1992-03-15T15:23:55Z<\/field>/)
232
+ end
233
+
234
+ it 'passes through other values' do
235
+ data = {
236
+ whatever: 'some string'
237
+ }
238
+
239
+ result = generator.add(data)
240
+ expect(result).to match(/<field name="whatever">some string<\/field>/)
241
+ end
242
+
243
+ # rails monkey-patches String to add a #to_time casting..
244
+ context 'with rails monkey patching' do
245
+ it 'passes through string values' do
246
+ data = {
247
+ whatever: double(to_s: 'some string', to_time: nil)
248
+ }
249
+
250
+ result = generator.add(data)
251
+ expect(result).to match(/<field name="whatever">some string<\/field>/)
252
+ end
253
+ end
254
+ end
255
+ end