tire 0.1.16 → 0.2.0
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.
- data/README.markdown +131 -62
- data/examples/rails-application-template.rb +1 -1
- data/examples/tire-dsl.rb +6 -6
- data/lib/tire/index.rb +7 -27
- data/lib/tire/model/callbacks.rb +1 -1
- data/lib/tire/model/search.rb +63 -27
- data/lib/tire/results/collection.rb +33 -15
- data/lib/tire/results/item.rb +46 -8
- data/lib/tire/search.rb +2 -5
- data/lib/tire/search/sort.rb +0 -7
- data/lib/tire/version.rb +6 -6
- data/test/integration/active_model_searchable_test.rb +26 -5
- data/test/integration/active_record_searchable_test.rb +94 -9
- data/test/integration/filters_test.rb +1 -1
- data/test/integration/highlight_test.rb +0 -1
- data/test/integration/index_mapping_test.rb +3 -4
- data/test/integration/percolator_test.rb +6 -6
- data/test/integration/persistent_model_test.rb +19 -1
- data/test/integration/query_string_test.rb +9 -0
- data/test/integration/results_test.rb +11 -0
- data/test/models/active_record_models.rb +49 -0
- data/test/test_helper.rb +2 -0
- data/test/unit/index_test.rb +16 -5
- data/test/unit/model_search_test.rb +45 -6
- data/test/unit/results_collection_test.rb +51 -2
- data/test/unit/results_item_test.rb +64 -1
- data/test/unit/search_sort_test.rb +26 -59
- data/test/unit/search_test.rb +17 -1
- metadata +16 -49
- data/test/models/active_record_article.rb +0 -12
@@ -6,7 +6,7 @@ module Tire
|
|
6
6
|
include Test::Integration
|
7
7
|
|
8
8
|
context "Default mapping" do
|
9
|
-
teardown { Tire.index('mapped-index').delete }
|
9
|
+
teardown { Tire.index('mapped-index').delete; sleep 0.1 }
|
10
10
|
|
11
11
|
should "create and return the default mapping" do
|
12
12
|
|
@@ -14,8 +14,8 @@ module Tire
|
|
14
14
|
create
|
15
15
|
store :type => :article, :title => 'One'
|
16
16
|
refresh
|
17
|
+
sleep 1
|
17
18
|
end
|
18
|
-
sleep 1.5
|
19
19
|
|
20
20
|
assert_equal 'string', index.mapping['article']['properties']['title']['type'], index.mapping.inspect
|
21
21
|
assert_nil index.mapping['article']['properties']['title']['boost'], index.mapping.inspect
|
@@ -23,14 +23,13 @@ module Tire
|
|
23
23
|
end
|
24
24
|
|
25
25
|
context "Creating index with mapping" do
|
26
|
-
teardown { Tire.index('mapped-index').delete; sleep 1 }
|
26
|
+
teardown { Tire.index('mapped-index').delete; sleep 0.1 }
|
27
27
|
|
28
28
|
should "create the specified mapping" do
|
29
29
|
|
30
30
|
index = Tire.index 'mapped-index' do
|
31
31
|
create :mappings => { :article => { :properties => { :title => { :type => 'string', :boost => 2.0, :store => 'yes' } } } }
|
32
32
|
end
|
33
|
-
sleep 1
|
34
33
|
|
35
34
|
# p index.mapping
|
36
35
|
assert_equal 2.0, index.mapping['article']['properties']['title']['boost'], index.mapping.inspect
|
@@ -20,7 +20,7 @@ module Tire
|
|
20
20
|
should "register query as a Hash" do
|
21
21
|
query = { :query => { :query_string => { :query => 'warning' } } }
|
22
22
|
assert @index.register_percolator_query('alert', query)
|
23
|
-
Tire.index('_percolator').refresh
|
23
|
+
Tire.index('_percolator').refresh
|
24
24
|
|
25
25
|
percolator = Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
|
26
26
|
assert percolator
|
@@ -28,7 +28,7 @@ module Tire
|
|
28
28
|
|
29
29
|
should "register query as block" do
|
30
30
|
assert @index.register_percolator_query('alert') { string 'warning' }
|
31
|
-
Tire.index('_percolator').refresh
|
31
|
+
Tire.index('_percolator').refresh
|
32
32
|
|
33
33
|
percolator = Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
|
34
34
|
assert percolator
|
@@ -37,11 +37,11 @@ module Tire
|
|
37
37
|
should "unregister a query" do
|
38
38
|
query = { :query => { :query_string => { :query => 'warning' } } }
|
39
39
|
assert @index.register_percolator_query('alert', query)
|
40
|
-
Tire.index('_percolator').refresh
|
40
|
+
Tire.index('_percolator').refresh
|
41
41
|
assert Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
|
42
42
|
|
43
43
|
assert @index.unregister_percolator_query('alert')
|
44
|
-
Tire.index('_percolator').refresh
|
44
|
+
Tire.index('_percolator').refresh
|
45
45
|
|
46
46
|
assert_raise(RestClient::ResourceNotFound) do
|
47
47
|
Configuration.client.get("#{Configuration.url}/_percolator/percolator-test/alert")
|
@@ -55,7 +55,7 @@ module Tire
|
|
55
55
|
@index.register_percolator_query('alert') { string 'warning' }
|
56
56
|
@index.register_percolator_query('gantz') { string '"y u no match"' }
|
57
57
|
@index.register_percolator_query('weather', :tags => ['weather']) { string 'severe' }
|
58
|
-
Tire.index('_percolator').refresh
|
58
|
+
Tire.index('_percolator').refresh
|
59
59
|
end
|
60
60
|
|
61
61
|
should "return an empty array when no query matches" do
|
@@ -79,7 +79,7 @@ module Tire
|
|
79
79
|
@index.register_percolator_query('alert') { string 'warning' }
|
80
80
|
@index.register_percolator_query('gantz') { string '"y u no match"' }
|
81
81
|
@index.register_percolator_query('weather', :tags => ['weather']) { string 'severe' }
|
82
|
-
Tire.index('_percolator').refresh
|
82
|
+
Tire.index('_percolator').refresh
|
83
83
|
end
|
84
84
|
|
85
85
|
should "return an empty array when no query matches" do
|
@@ -22,13 +22,31 @@ module Tire
|
|
22
22
|
two = PersistentArticle.create :id => 2, :title => 'Two'
|
23
23
|
|
24
24
|
PersistentArticle.index.refresh
|
25
|
-
sleep(1.5)
|
26
25
|
|
27
26
|
results = PersistentArticle.find [1, 2]
|
28
27
|
|
29
28
|
assert_equal 2, results.size
|
30
29
|
|
31
30
|
end
|
31
|
+
|
32
|
+
context "with pagination" do
|
33
|
+
|
34
|
+
setup do
|
35
|
+
1.upto(9) { |number| PersistentArticle.create :title => "Test#{number}" }
|
36
|
+
PersistentArticle.elasticsearch_index.refresh
|
37
|
+
end
|
38
|
+
|
39
|
+
should "find first page with five results" do
|
40
|
+
results = PersistentArticle.search( :per_page => 5, :page => 1 ) { query { all } }
|
41
|
+
assert_equal 5, results.size
|
42
|
+
|
43
|
+
assert_equal 2, results.total_pages
|
44
|
+
assert_equal 1, results.current_page
|
45
|
+
assert_equal nil, results.previous_page
|
46
|
+
assert_equal 2, results.next_page
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
32
50
|
end
|
33
51
|
|
34
52
|
end
|
@@ -30,6 +30,15 @@ module Tire
|
|
30
30
|
assert_equal 4, search(q).results.count
|
31
31
|
end
|
32
32
|
|
33
|
+
should "pass options to query definition" do
|
34
|
+
s = Tire.search 'articles-test' do
|
35
|
+
query do
|
36
|
+
string 'ruby python', :default_operator => 'AND'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
assert_equal 1, s.results.count
|
40
|
+
end
|
41
|
+
|
33
42
|
end
|
34
43
|
|
35
44
|
private
|
@@ -21,6 +21,17 @@ module Tire
|
|
21
21
|
assert_nil s.results.first.tags
|
22
22
|
end
|
23
23
|
|
24
|
+
should "allow to retrieve multiple fields" do
|
25
|
+
q = 'title:one'
|
26
|
+
s = Tire.search('articles-test') do
|
27
|
+
query { string q }
|
28
|
+
fields 'title', 'tags'
|
29
|
+
end
|
30
|
+
assert_equal 'One', s.results.first.title
|
31
|
+
assert_equal 'ruby', s.results.first.tags[0]
|
32
|
+
assert_nil s.results.first.published_on
|
33
|
+
end
|
34
|
+
|
24
35
|
end
|
25
36
|
|
26
37
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'active_record'
|
3
|
+
|
4
|
+
class ActiveRecordArticle < ActiveRecord::Base
|
5
|
+
has_many :comments, :class_name => "ActiveRecordComment", :foreign_key => "article_id"
|
6
|
+
has_many :stats, :class_name => "ActiveRecordStat", :foreign_key => "article_id"
|
7
|
+
|
8
|
+
include Tire::Model::Search
|
9
|
+
include Tire::Model::Callbacks
|
10
|
+
|
11
|
+
mapping do
|
12
|
+
indexes :title, :type => 'string', :boost => 10, :analyzer => 'snowball'
|
13
|
+
indexes :created_at, :type => 'date'
|
14
|
+
|
15
|
+
indexes :comments do
|
16
|
+
indexes :author
|
17
|
+
indexes :body
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_indexed_json
|
22
|
+
{
|
23
|
+
:title => title,
|
24
|
+
:length => length,
|
25
|
+
|
26
|
+
:comments => comments.map { |c| { :_type => 'active_record_comment',
|
27
|
+
:_id => c.id,
|
28
|
+
:author => c.author,
|
29
|
+
:body => c.body } },
|
30
|
+
:stats => stats.map { |s| { :pageviews => s.pageviews } }
|
31
|
+
}.to_json
|
32
|
+
end
|
33
|
+
|
34
|
+
def length
|
35
|
+
title.length
|
36
|
+
end
|
37
|
+
|
38
|
+
def comment_authors
|
39
|
+
comments.map(&:author).to_sentence
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class ActiveRecordComment < ActiveRecord::Base
|
44
|
+
belongs_to :article, :class_name => "ActiveRecordArticle", :foreign_key => "article_id"
|
45
|
+
end
|
46
|
+
|
47
|
+
class ActiveRecordStat < ActiveRecord::Base
|
48
|
+
belongs_to :article, :class_name => "ActiveRecordArticle", :foreign_key => "article_id"
|
49
|
+
end
|
data/test/test_helper.rb
CHANGED
data/test/unit/index_test.rb
CHANGED
@@ -181,7 +181,10 @@ module Tire
|
|
181
181
|
setup do
|
182
182
|
Configuration.reset :wrapper
|
183
183
|
|
184
|
-
Configuration.client.stubs(:post).with
|
184
|
+
Configuration.client.stubs(:post).with do |url, payload|
|
185
|
+
url == "#{Configuration.url}/dummy/article/" &&
|
186
|
+
payload =~ /"title":"Test"/
|
187
|
+
end.
|
185
188
|
returns(mock_response('{"ok":true,"_id":"id-1"}'))
|
186
189
|
@index.store :type => 'article', :title => 'Test'
|
187
190
|
end
|
@@ -319,7 +322,7 @@ module Tire
|
|
319
322
|
end
|
320
323
|
end
|
321
324
|
|
322
|
-
should "
|
325
|
+
should "display error message when collection item does not have ID" do
|
323
326
|
Configuration.client.expects(:post).with { |url, json| url == "#{Configuration.url}/_bulk" }
|
324
327
|
STDERR.expects(:puts).once
|
325
328
|
|
@@ -487,7 +490,7 @@ module Tire
|
|
487
490
|
Configuration.client.expects(:put).with do |url, payload|
|
488
491
|
payload = MultiJson.decode(payload)
|
489
492
|
url == "#{Configuration.url}/_percolator/dummy/my-query" &&
|
490
|
-
payload['query']['query_string']['query'] == 'foo'
|
493
|
+
payload['query']['query_string']['query'] == 'foo' &&
|
491
494
|
payload['tags'] == ['alert']
|
492
495
|
end.
|
493
496
|
returns(mock_response('{
|
@@ -548,13 +551,21 @@ module Tire
|
|
548
551
|
context "while storing document" do
|
549
552
|
|
550
553
|
should "percolate document against all registered queries" do
|
551
|
-
Configuration.client.expects(:post).
|
554
|
+
Configuration.client.expects(:post).
|
555
|
+
with do |url, payload|
|
556
|
+
url == "#{Configuration.url}/dummy/article/?percolate=*" &&
|
557
|
+
payload =~ /"title":"Test"/
|
558
|
+
end.
|
552
559
|
returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
|
553
560
|
@index.store( {:type => 'article', :title => 'Test'}, {:percolate => true} )
|
554
561
|
end
|
555
562
|
|
556
563
|
should "percolate document against specific queries" do
|
557
|
-
Configuration.client.expects(:post).
|
564
|
+
Configuration.client.expects(:post).
|
565
|
+
with do |url, payload|
|
566
|
+
url == "#{Configuration.url}/dummy/article/?percolate=tag:alerts" &&
|
567
|
+
payload =~ /"title":"Test"/
|
568
|
+
end.
|
558
569
|
returns(mock_response('{"ok":true,"_id":"test","matches":["alerts"]}'))
|
559
570
|
response = @index.store( {:type => 'article', :title => 'Test'}, {:percolate => 'tag:alerts'} )
|
560
571
|
assert_equal response['matches'], ['alerts']
|
@@ -90,20 +90,18 @@ module Tire
|
|
90
90
|
ActiveModelArticle.elasticsearch_index.refresh
|
91
91
|
end
|
92
92
|
|
93
|
-
should "wrap results in
|
93
|
+
should "wrap results in instances of the wrapper class" do
|
94
94
|
response = { 'hits' => { 'hits' => [{'_id' => 1, '_score' => 0.8, '_source' => { 'title' => 'Article' }}] } }
|
95
95
|
Configuration.client.expects(:get).returns(mock_response(response.to_json))
|
96
96
|
|
97
97
|
collection = ActiveModelArticle.search 'foo'
|
98
98
|
assert_instance_of Results::Collection, collection
|
99
99
|
|
100
|
-
assert_equal Results::Item, Tire::Configuration.wrapper
|
101
|
-
|
102
100
|
document = collection.first
|
103
101
|
|
104
|
-
assert_instance_of
|
105
|
-
assert_not_nil
|
106
|
-
assert_equal 1,
|
102
|
+
assert_instance_of Results::Item, document
|
103
|
+
assert_not_nil document._score
|
104
|
+
assert_equal 1, document.id
|
107
105
|
assert_equal 'Article', document.title
|
108
106
|
end
|
109
107
|
|
@@ -129,6 +127,15 @@ module Tire
|
|
129
127
|
end
|
130
128
|
end
|
131
129
|
|
130
|
+
should "allow to pass :page and :per_page options" do
|
131
|
+
Tire::Search::Search.any_instance.expects(:size).with(10)
|
132
|
+
Tire::Search::Search.any_instance.expects(:from).with(20)
|
133
|
+
|
134
|
+
ActiveModelArticle.search :per_page => 10, :page => 3 do
|
135
|
+
query { string 'foo' }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
132
139
|
end
|
133
140
|
|
134
141
|
context "searching with query string" do
|
@@ -551,6 +558,38 @@ module Tire
|
|
551
558
|
|
552
559
|
end
|
553
560
|
|
561
|
+
context "Results::Item" do
|
562
|
+
|
563
|
+
setup do
|
564
|
+
module ::Rails
|
565
|
+
end
|
566
|
+
|
567
|
+
class ::FakeRailsModel
|
568
|
+
extend ActiveModel::Naming
|
569
|
+
include ActiveModel::Conversion
|
570
|
+
def self.find(*args); new; end
|
571
|
+
end
|
572
|
+
|
573
|
+
@document = Results::Item.new :id => 1, :_type => 'fake_rails_model', :title => 'Test'
|
574
|
+
end
|
575
|
+
|
576
|
+
should "load the 'real' instance from the corresponding model" do
|
577
|
+
assert_respond_to @document, :load
|
578
|
+
assert_instance_of FakeRailsModel, @document.load
|
579
|
+
end
|
580
|
+
|
581
|
+
should "pass the ID to the corresponding model's find method" do
|
582
|
+
FakeRailsModel.expects(:find).with(1).returns(FakeRailsModel.new)
|
583
|
+
@document.load
|
584
|
+
end
|
585
|
+
|
586
|
+
should "pass the options to the corresponding model's find method" do
|
587
|
+
FakeRailsModel.expects(:find).with(1, {:include => 'everything'}).returns(FakeRailsModel.new)
|
588
|
+
@document.load :include => 'everything'
|
589
|
+
end
|
590
|
+
|
591
|
+
end
|
592
|
+
|
554
593
|
end
|
555
594
|
|
556
595
|
end
|
@@ -6,7 +6,8 @@ module Tire
|
|
6
6
|
|
7
7
|
context "Collection" do
|
8
8
|
setup do
|
9
|
-
|
9
|
+
begin; Object.send(:remove_const, :Rails); rescue; end
|
10
|
+
Configuration.reset
|
10
11
|
@default_response = { 'hits' => { 'hits' => [{'_id' => 1, '_score' => 1, '_source' => {:title => 'Test'}},
|
11
12
|
{'_id' => 2},
|
12
13
|
{'_id' => 3}] } }
|
@@ -110,7 +111,7 @@ module Tire
|
|
110
111
|
# Underlying issue: https://github.com/karmi/tire/pull/31#issuecomment-1340967
|
111
112
|
#
|
112
113
|
setup do
|
113
|
-
Configuration.reset
|
114
|
+
Configuration.reset
|
114
115
|
@default_response = { 'hits' => { 'hits' =>
|
115
116
|
[ { '_id' => 1, '_score' => 0.5, '_index' => 'testing', '_type' => 'article',
|
116
117
|
'fields' => {
|
@@ -187,6 +188,54 @@ module Tire
|
|
187
188
|
|
188
189
|
end
|
189
190
|
|
191
|
+
context "with eager loading" do
|
192
|
+
setup do
|
193
|
+
@response = { 'hits' => { 'hits' => [ {'_id' => 1, '_type' => 'active_record_article'},
|
194
|
+
{'_id' => 2, '_type' => 'active_record_article'},
|
195
|
+
{'_id' => 3, '_type' => 'active_record_article'}] } }
|
196
|
+
ActiveRecordArticle.stubs(:inspect).returns("<ActiveRecordArticle>")
|
197
|
+
end
|
198
|
+
|
199
|
+
should "load the records via model find method from database" do
|
200
|
+
ActiveRecordArticle.expects(:find).with([1,2,3]).
|
201
|
+
returns([ Results::Item.new(:id => 3),
|
202
|
+
Results::Item.new(:id => 1),
|
203
|
+
Results::Item.new(:id => 2) ])
|
204
|
+
Results::Collection.new(@response, :load => true).results
|
205
|
+
end
|
206
|
+
|
207
|
+
should "pass the :load option Hash to model find metod" do
|
208
|
+
ActiveRecordArticle.expects(:find).with([1,2,3], :include => 'comments').
|
209
|
+
returns([ Results::Item.new(:id => 3),
|
210
|
+
Results::Item.new(:id => 1),
|
211
|
+
Results::Item.new(:id => 2) ])
|
212
|
+
Results::Collection.new(@response, :load => { :include => 'comments' }).results
|
213
|
+
end
|
214
|
+
|
215
|
+
should "preserve the order of records returned from search" do
|
216
|
+
ActiveRecordArticle.expects(:find).with([1,2,3]).
|
217
|
+
returns([ Results::Item.new(:id => 3),
|
218
|
+
Results::Item.new(:id => 1),
|
219
|
+
Results::Item.new(:id => 2) ])
|
220
|
+
assert_equal [1,2,3], Results::Collection.new(@response, :load => true).results.map(&:id)
|
221
|
+
end
|
222
|
+
|
223
|
+
should "raise error when model class cannot be inferred from _type" do
|
224
|
+
assert_raise(NameError) do
|
225
|
+
response = { 'hits' => { 'hits' => [ {'_id' => 1, '_type' => 'hic_sunt_leones'}] } }
|
226
|
+
Results::Collection.new(response, :load => true).results
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
should "raise error when _type is missing" do
|
231
|
+
assert_raise(NoMethodError) do
|
232
|
+
response = { 'hits' => { 'hits' => [ {'_id' => 1}] } }
|
233
|
+
Results::Collection.new(response, :load => true).results
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
190
239
|
end
|
191
240
|
|
192
241
|
end
|
@@ -1,9 +1,17 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
module Tire
|
4
|
-
|
5
4
|
class ResultsItemTest < Test::Unit::TestCase
|
6
5
|
|
6
|
+
# ActiveModel compatibility tests
|
7
|
+
#
|
8
|
+
def setup
|
9
|
+
super
|
10
|
+
begin; Object.send(:remove_const, :Rails); rescue; end
|
11
|
+
@model = Results::Item.new :title => 'Test'
|
12
|
+
end
|
13
|
+
include ActiveModel::Lint::Tests
|
14
|
+
|
7
15
|
context "Item" do
|
8
16
|
|
9
17
|
setup do
|
@@ -57,6 +65,61 @@ module Tire
|
|
57
65
|
assert_equal 'Kafka', @document.author.name
|
58
66
|
end
|
59
67
|
|
68
|
+
should "wrap arrays" do
|
69
|
+
@document = Results::Item.new :stats => [1, 2, 3]
|
70
|
+
assert_equal [1, 2, 3], @document.stats
|
71
|
+
end
|
72
|
+
|
73
|
+
should "wrap hashes in arrays" do
|
74
|
+
@document = Results::Item.new :comments => [{:title => 'one'}, {:title => 'two'}]
|
75
|
+
assert_equal 2, @document.comments.size
|
76
|
+
assert_instance_of Results::Item, @document.comments.first
|
77
|
+
assert_equal 'one', @document.comments.first.title
|
78
|
+
assert_equal 'two', @document.comments.last.title
|
79
|
+
end
|
80
|
+
|
81
|
+
should "be an Item instance" do
|
82
|
+
assert_instance_of Tire::Results::Item, @document
|
83
|
+
end
|
84
|
+
|
85
|
+
should "be convertible to hash" do
|
86
|
+
assert_instance_of Hash, @document.to_hash
|
87
|
+
end
|
88
|
+
|
89
|
+
should "be inspectable" do
|
90
|
+
assert_match /<Item title|Item author/, @document.inspect
|
91
|
+
end
|
92
|
+
|
93
|
+
context "within Rails" do
|
94
|
+
|
95
|
+
setup do
|
96
|
+
module ::Rails
|
97
|
+
end
|
98
|
+
|
99
|
+
class ::FakeRailsModel
|
100
|
+
extend ActiveModel::Naming
|
101
|
+
include ActiveModel::Conversion
|
102
|
+
def self.find(id, options); new; end
|
103
|
+
end
|
104
|
+
|
105
|
+
@document = Results::Item.new :id => 1, :_type => 'fake_rails_model', :title => 'Test'
|
106
|
+
end
|
107
|
+
|
108
|
+
should "be an instance of model, based on _type" do
|
109
|
+
assert_equal FakeRailsModel, @document.class
|
110
|
+
end
|
111
|
+
|
112
|
+
should "be inspectable with masquerade" do
|
113
|
+
assert_match /<Item \(FakeRailsModel\)/, @document.inspect
|
114
|
+
end
|
115
|
+
|
116
|
+
should "return proper singular and plural forms" do
|
117
|
+
assert_equal 'fake_rails_model', ActiveModel::Naming.singular(@document)
|
118
|
+
assert_equal 'fake_rails_models', ActiveModel::Naming.plural(@document)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
60
123
|
end
|
61
124
|
|
62
125
|
end
|