lucid_works 0.6.29 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.rvmrc +1 -1
  2. data/Rakefile +25 -0
  3. data/config/locales/en.yml +171 -83
  4. data/lib/lucid_works/associations/has_many.rb +2 -2
  5. data/lib/lucid_works/associations/has_one.rb +1 -1
  6. data/lib/lucid_works/associations/proxy.rb +3 -3
  7. data/lib/lucid_works/associations.rb +2 -2
  8. data/lib/lucid_works/base.rb +21 -48
  9. data/lib/lucid_works/collection/click.rb +17 -0
  10. data/lib/lucid_works/collection/settings.rb +0 -1
  11. data/lib/lucid_works/collection.rb +22 -3
  12. data/lib/lucid_works/crawler.rb +13 -0
  13. data/lib/lucid_works/datasource/history.rb +5 -9
  14. data/lib/lucid_works/datasource/status.rb +8 -11
  15. data/lib/lucid_works/datasource.rb +67 -32
  16. data/lib/lucid_works/datasource_property.rb +18 -0
  17. data/lib/lucid_works/datasource_type.rb +23 -0
  18. data/lib/lucid_works/exceptions.rb +1 -0
  19. data/lib/lucid_works/field.rb +43 -2
  20. data/lib/lucid_works/fieldtype.rb +28 -0
  21. data/lib/lucid_works/gem_version.rb +1 -1
  22. data/lib/lucid_works/jdbcdriver.rb +30 -0
  23. data/lib/lucid_works/role.rb +59 -0
  24. data/lib/lucid_works/schema/attribute.rb +86 -0
  25. data/lib/lucid_works/schema/boolean_attribute.rb +34 -0
  26. data/lib/lucid_works/schema/custom_attribute.rb +15 -0
  27. data/lib/lucid_works/schema/integer_attribute.rb +32 -0
  28. data/lib/lucid_works/schema/iso8601_attribute.rb +31 -0
  29. data/lib/lucid_works/schema/string_attribute.rb +22 -0
  30. data/lib/lucid_works/schema.rb +66 -97
  31. data/lib/lucid_works/server.rb +14 -0
  32. data/lib/lucid_works.rb +12 -0
  33. data/spec/fixtures/fake_file_ds_to_be_deleted/.gitkeep +0 -0
  34. data/spec/fixtures/fake_file_ds_to_be_updated/.gitkeep +0 -0
  35. data/spec/fixtures/fake_file_ds_to_get_index_of/.gitkeep +0 -0
  36. data/spec/fixtures/fake_file_ds_to_get_schedule_of/.gitkeep +0 -0
  37. data/spec/fixtures/fake_file_ds_to_get_status_of/.gitkeep +0 -0
  38. data/spec/fixtures/fake_file_ds_to_mess_with_job_of/.gitkeep +0 -0
  39. data/spec/fixtures/fake_file_ds_to_test_progress/.gitkeep +0 -0
  40. data/spec/lib/lucid_works/associations/has_many_spec.rb +4 -3
  41. data/spec/lib/lucid_works/associations/has_one_spec.rb +4 -3
  42. data/spec/lib/lucid_works/base_spec.rb +110 -62
  43. data/spec/lib/lucid_works/collection/activity/history_spec.rb +1 -1
  44. data/spec/lib/lucid_works/collection_spec.rb +17 -17
  45. data/spec/lib/lucid_works/datasource/history_spec.rb +4 -4
  46. data/spec/lib/lucid_works/datasource/status_spec.rb +7 -7
  47. data/spec/lib/lucid_works/datasource_spec.rb +9 -8
  48. data/spec/lib/lucid_works/field_spec.rb +101 -2
  49. data/spec/lib/lucid_works/fieldtype_spec.rb +156 -0
  50. data/spec/lib/lucid_works/schema/attribute_spec.rb +136 -0
  51. data/spec/lib/lucid_works/schema_spec.rb +53 -27
  52. data/spec/spec_helper.rb +3 -50
  53. data/spec/support/active_model_lint.rb +21 -0
  54. data/spec/support/lucid_works.rb +52 -0
  55. metadata +36 -2
@@ -12,7 +12,7 @@ describe LucidWorks::Datasource::Status do
12
12
 
13
13
  STATES.each do |actual_state|
14
14
  describe "when state is #{actual_state}," do
15
- before { @status.crawlState = actual_state.upcase }
15
+ before { @status.crawl_state = actual_state.upcase }
16
16
 
17
17
  (STATES - ['stopped']).each do |testing_state|
18
18
  predicate = "#{testing_state}?"
@@ -41,7 +41,7 @@ describe LucidWorks::Datasource::Status do
41
41
  }
42
42
  truth_table.each do |state, expected_result|
43
43
  describe "when state is #{state}" do
44
- before { @status.crawlState = state }
44
+ before { @status.crawl_state = state }
45
45
  if expected_result
46
46
  it { should be_stopped }
47
47
  else
@@ -60,7 +60,7 @@ describe LucidWorks::Datasource::Status do
60
60
  }
61
61
  truth_table.each do |state, expected_result|
62
62
  describe "when state is #{state}" do
63
- before { @status.crawlState = state }
63
+ before { @status.crawl_state = state }
64
64
  if expected_result
65
65
  it { should be_post_processing }
66
66
  else
@@ -71,12 +71,12 @@ describe LucidWorks::Datasource::Status do
71
71
  end
72
72
 
73
73
  describe "#doc_count" do
74
- it "should sum numUpdated, numNew and numUnchanged" do
74
+ it "should sum num_updated, num_new and num_unchanged" do
75
75
  status = LucidWorks::Datasource::Status.new(
76
76
  :parent => @server,
77
- :numUpdated => 11,
78
- :numNew => 22,
79
- :numUnchanged => 33
77
+ :num_updated => 11,
78
+ :num_new => 22,
79
+ :num_unchanged => 33
80
80
  )
81
81
  status.doc_count.should == 66
82
82
  end
@@ -133,7 +133,7 @@ describe LucidWorks::Datasource do
133
133
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
134
134
  :type => 'file',
135
135
  :name => "datasource we are going to update",
136
- :path => "/fake_ds_to_be_updated",
136
+ :path => fixture_path("fake_file_ds_to_be_updated"),
137
137
  :crawl_depth => 2
138
138
  )
139
139
  @datasource.should be_persisted
@@ -155,7 +155,7 @@ describe LucidWorks::Datasource do
155
155
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
156
156
  :type => 'file',
157
157
  :name => "datasource we are going to delete",
158
- :path => "/fake_ds_to_be_deleted",
158
+ :path => fixture_path("fake_file_ds_to_be_deleted"),
159
159
  :crawl_depth => 2
160
160
  )
161
161
  @datasource.should be_persisted
@@ -180,7 +180,7 @@ describe LucidWorks::Datasource do
180
180
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
181
181
  :type => 'file',
182
182
  :name => "datasource we are going to get a status of",
183
- :path => "/fake_ds_to_get_status_of",
183
+ :path => fixture_path("fake_file_ds_to_get_status_of"),
184
184
  :crawl_depth => 2
185
185
  )
186
186
  @datasource.should be_persisted
@@ -192,7 +192,7 @@ describe LucidWorks::Datasource do
192
192
  @status.should be_valid
193
193
  end
194
194
 
195
- %w{ crawlState numUnchanged }.each do |attr|
195
+ %w{ crawl_state num_unchanged }.each do |attr|
196
196
  it "should have a value for #{attr}" do
197
197
  @status.send(attr.to_sym).should_not be_nil
198
198
  end
@@ -206,9 +206,10 @@ describe LucidWorks::Datasource do
206
206
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
207
207
  :type => 'file',
208
208
  :name => "datasource we are going to get a schedule of",
209
- :path => "/fake_ds_to_get_schedule_of",
209
+ :path => fixture_path("fake_file_ds_to_get_schedule_of"),
210
210
  :crawl_depth => 2
211
211
  )
212
+
212
213
  @datasource.should be_persisted
213
214
 
214
215
  schedule = @datasource.schedule
@@ -228,7 +229,7 @@ describe LucidWorks::Datasource do
228
229
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
229
230
  :type => 'file',
230
231
  :name => "datasource we are going to get a index of",
231
- :path => "/fake_ds_to_get_index_of",
232
+ :path => fixture_path("fake_file_ds_to_get_index_of"),
232
233
  :crawl_depth => 2
233
234
  )
234
235
  @datasource.should be_valid
@@ -246,7 +247,7 @@ describe LucidWorks::Datasource do
246
247
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
247
248
  :type => 'file',
248
249
  :name => "datasource we are going to mess with job of",
249
- :path => "/fake_ds_to_mess_with_job_of",
250
+ :path => fixture_path("fake_file_ds_to_mess_with_job_of"),
250
251
  :crawl_depth => 2
251
252
  )
252
253
  @datasource.should be_valid
@@ -302,7 +303,7 @@ describe LucidWorks::Datasource do
302
303
  :crawler => LucidWorks::Datasource::CRAWLERS['file'],
303
304
  :type => 'file',
304
305
  :name => "datasource we are going to get progress of",
305
- :path => "/fake_ds_to_test_progress",
306
+ :path => fixture_path("fake_file_ds_to_test_progress"),
306
307
  :crawl_depth => 2
307
308
  )
308
309
  @datasource.should be_valid
@@ -124,7 +124,7 @@ describe LucidWorks::Field do
124
124
  end
125
125
  end
126
126
  end
127
-
127
+
128
128
  context "for an existing field" do
129
129
  it "doesn't validate the name's uniqueness" do
130
130
  field = LucidWorks::Field.find('body', :parent => @collection)
@@ -132,7 +132,106 @@ describe LucidWorks::Field do
132
132
  end
133
133
  end
134
134
  end
135
-
135
+
136
+ describe "#index_term_freq_and_pos" do
137
+
138
+ context "when indexed is false" do
139
+ before do
140
+ @field = LucidWorks::Field.new(:parent => @collection, :name => 'my_field', :indexed => false)
141
+ end
142
+
143
+ [
144
+ { :omit_tf => false, :omit_positions => false },
145
+ { :omit_tf => false, :omit_positions => true },
146
+ { :omit_tf => true, :omit_positions => false },
147
+ { :omit_tf => true, :omit_positions => true }
148
+ ].each do |settings|
149
+ it "should reuturn :document if omit_tf=#{settings[:omit_tf]} and omit_positions=#{settings[:omit_positions]}" do
150
+ settings.each { |k,v| @field.send("#{k}=", v) }
151
+ @field.index_term_freq_and_pos.should == :document_only
152
+ end
153
+ end
154
+ end
155
+
156
+ context "when indexed is true" do
157
+ before do
158
+ @field = LucidWorks::Field.new(:parent => @collection, :name => 'my_field', :indexed => true)
159
+ end
160
+
161
+ [
162
+ { :omit_tf => false, :omit_positions => false, :expected_result => :document_termfreq_termpos },
163
+ { :omit_tf => false, :omit_positions => true , :expected_result => :document_termfreq },
164
+ { :omit_tf => true, :omit_positions => false, :expected_result => :document_only },
165
+ { :omit_tf => true, :omit_positions => true , :expected_result => :document_only }
166
+ ].each do |data|
167
+ it "should return #{data[:expected_result]} if omit_tf=#{data[:omit_tf]} and omit_positions=#{data[:omit_positions]}" do
168
+ @field.omit_tf = data[:omit_tf]
169
+ @field.omit_positions = data[:omit_positions]
170
+ @field.index_term_freq_and_pos.should == data[:expected_result]
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "#index_term_freq_and_pos=" do
177
+
178
+ INDEX_TERM_FREQ_AND_POS_TRUTH_TABLE = [
179
+ { :omit_tf => false, :omit_positions => false, :index_term_freq_and_pos => :document_termfreq_termpos },
180
+ { :omit_tf => false, :omit_positions => true , :index_term_freq_and_pos => :document_termfreq },
181
+ { :omit_tf => true, :omit_positions => true , :index_term_freq_and_pos => :document_only }
182
+ ]
183
+
184
+ context "when indexed is true" do
185
+ before do
186
+ @field = LucidWorks::Field.new(:parent => @collection, :name => 'my_field', :indexed => true)
187
+ end
188
+
189
+ INDEX_TERM_FREQ_AND_POS_TRUTH_TABLE.each do |data|
190
+ it "should set omit_tf=#{data[:omit_tf]} and omit_positions=#{data[:omit_positions]} when given #{data[:index_term_freq_and_pos]}" do
191
+ # Set to opposite so we can see change
192
+ @field.omit_tf = !data[:omit_tf]
193
+ @field.omit_positions = !data[:omit_positions]
194
+
195
+ @field.index_term_freq_and_pos = data[:index_term_freq_and_pos]
196
+
197
+ @field.omit_positions.should == data[:omit_positions]
198
+ @field.omit_tf.should == data[:omit_tf]
199
+ end
200
+ end
201
+
202
+ context "given anything else" do
203
+ it "should raise an error" do
204
+ lambda {
205
+ @field.index_term_freq_and_pos = :documents_and_bananas
206
+ }.should raise_error
207
+ end
208
+ end
209
+ end
210
+
211
+ context "when indexed is false" do
212
+ before do
213
+ @field = LucidWorks::Field.new(:parent => @collection, :name => 'my_field', :indexed => false)
214
+ end
215
+
216
+ INDEX_TERM_FREQ_AND_POS_TRUTH_TABLE.each do |data|
217
+ it "should set omit_tf=true and omit_positions=true when given #{data[:index_term_freq_and_pos]}" do
218
+ # Set to opposite so we can see change
219
+ @field.omit_tf = false
220
+ @field.omit_positions = false
221
+
222
+ @field.index_term_freq_and_pos = data[:index_term_freq_and_pos]
223
+
224
+ @field.omit_positions.should == true
225
+ @field.omit_tf.should == true
226
+ end
227
+ end
228
+
229
+ it "should set omit_tf=true and omit_positions=true" do
230
+
231
+ end
232
+ end
233
+ end
234
+
136
235
  describe "CRUD" do
137
236
  describe "find" do
138
237
  it "should retrieve a field" do
@@ -0,0 +1,156 @@
1
+ require 'spec_helper'
2
+
3
+ describe LucidWorks::Fieldtype do
4
+
5
+ before :all do
6
+ @server = connect_to_live_server
7
+ @server.reset_collections!
8
+ end
9
+
10
+ describe "CRUD" do
11
+ before :all do
12
+ @collection = @server.collections.create(:name => 'collection_for_testing_fieldtypes')
13
+ end
14
+
15
+ def fieldtype_count
16
+ @collection.fieldtypes!.size
17
+ end
18
+
19
+ describe ".create" do
20
+ context "with invalid parameters" do
21
+ before do
22
+ @parameters = {
23
+ :collection => @collection,
24
+ :name => "",
25
+ :_class => 'solr.StrField'
26
+ }
27
+ end
28
+
29
+ it "should not create a fieldtype, and add errors for the invalid parameters" do
30
+ t = nil
31
+ lambda {
32
+ t = LucidWorks::Fieldtype.create(@parameters)
33
+ }.should_not change(self, :fieldtype_count)
34
+ t.errors[:name].should_not be_empty
35
+ end
36
+ end
37
+
38
+ context "with valid parameters" do
39
+ before do
40
+ @parameters = {
41
+ :collection => @collection,
42
+ :name => 'foobar',
43
+ :_class => 'solr.StrField'
44
+ }
45
+ end
46
+
47
+ it "should create a fieldtype" do
48
+ lambda {
49
+ t = LucidWorks::Fieldtype.create(@parameters)
50
+ t.should be_a(LucidWorks::Fieldtype)
51
+ t.should be_persisted
52
+ }.should change(self, :fieldtype_count).by(1)
53
+ end
54
+ end
55
+
56
+ context "with a bunch of new parameters (not listed in the schema)" do
57
+ before do
58
+ @parameters = {
59
+ :collection => @collection,
60
+ :name => 'new_fieldtype_with_random_attrs',
61
+ :_class => 'solr.StrField',
62
+ :indexed => 'true',
63
+ :omitNorms => 'false',
64
+ :sortMissingLast => 'true'
65
+ }
66
+ end
67
+
68
+ it "should create a fieldtype" do
69
+ lambda {
70
+ t = LucidWorks::Fieldtype.create(@parameters)
71
+ t.should be_a(LucidWorks::Fieldtype)
72
+ t.should be_persisted
73
+ }.should change(self, :fieldtype_count).by(1)
74
+
75
+ t = LucidWorks::Fieldtype.find('new_fieldtype_with_random_attrs', :collection => @collection)
76
+ t.indexed.should == 'true'
77
+ t.omitNorms.should == 'false'
78
+ t.sortMissingLast.should == 'true'
79
+ end
80
+ end
81
+ end
82
+
83
+ describe ".find" do
84
+ context "for an existing fieldtype" do
85
+ it "should retrieve the fieldtype" do
86
+ ft = LucidWorks::Fieldtype.find('string', :collection => @collection)
87
+ ft.should be_persisted
88
+ ft.attributes.should == {
89
+ "_class" => "solr.StrField",
90
+ "name" => "string",
91
+ "omitNorms" => "true",
92
+ "sortMissingLast" => "true"
93
+ }
94
+ end
95
+ end
96
+ end
97
+
98
+ context "for an existing fieldtype" do
99
+ before do
100
+ LucidWorks::Fieldtype.schema.reset!
101
+ @fieldtype = LucidWorks::Fieldtype.create(
102
+ :collection => @collection,
103
+ :name => "fieldtype_we_are_going_to_update",
104
+ :_class => 'solr.StrField',
105
+ :indexed => 'false'
106
+ )
107
+ @fieldtype.should be_persisted
108
+ end
109
+
110
+ after do
111
+ @fieldtype.destroy
112
+ end
113
+
114
+ describe "#save" do
115
+ it "should update the fieldtype" do
116
+ @fieldtype.indexed = "true"
117
+ @fieldtype.save
118
+ reloaded_ft = LucidWorks::Fieldtype.find(@fieldtype.name, :collection => @collection)
119
+ reloaded_ft.indexed.should == "true"
120
+ end
121
+ end
122
+
123
+ describe "#update_attributes" do
124
+ it "should add any attributes it does not recognize to the schema before saving" do
125
+ LucidWorks::Fieldtype.schema.should_not have_attribute(:stored)
126
+ @fieldtype.update_attributes(:stored => true)
127
+ LucidWorks::Fieldtype.schema.should have_attribute(:stored)
128
+ reloaded_ft = LucidWorks::Fieldtype.find(@fieldtype.name, :collection => @collection)
129
+ reloaded_ft.stored.should == "true"
130
+ end
131
+ end
132
+ end
133
+
134
+ describe "#destroy" do
135
+ context "for an existing fieldtype" do
136
+ before do
137
+ @fieldtype = LucidWorks::Fieldtype.create(
138
+ :collection => @collection,
139
+ :name => "fieldtype_we_are_going_to_delete",
140
+ :_class => 'solr.StrField'
141
+ )
142
+ @fieldtype.should be_persisted
143
+ end
144
+
145
+ it "should delete the fieldtype" do
146
+ lambda {
147
+ @fieldtype.destroy
148
+ }.should change(self, :fieldtype_count).by(-1)
149
+ lambda {
150
+ LucidWorks::Fieldtype.find(@fieldtype.name, :collection => @collection)
151
+ }.should raise_error(RestClient::ResourceNotFound)
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ describe LucidWorks::Schema::Attribute do
4
+ before(:each) do
5
+ @mock_schema = double('Schema')
6
+ end
7
+
8
+ describe "#factory" do
9
+
10
+ context "when given a bad type" do
11
+ it "should raise an error" do
12
+ lambda {
13
+ Attribute.factory(@mock_schema, 'attrname', :invalid_attribute_type)
14
+ }.should raise_error
15
+ end
16
+ end
17
+
18
+ {
19
+ :string => LucidWorks::Schema::StringAttribute,
20
+ :integer => LucidWorks::Schema::IntegerAttribute,
21
+ :boolean => LucidWorks::Schema::BooleanAttribute,
22
+ :iso8601 => LucidWorks::Schema::Iso8601Attribute,
23
+ :custom => LucidWorks::Schema::CustomAttribute,
24
+ }.each do |attr_type, attr_class|
25
+ context "when given type #{attr_type}" do
26
+ it "should create an attribute of class #{attr_class.name}" do
27
+ attr = LucidWorks::Schema::Attribute.factory(@mock_schema, 'attrname', attr_type)
28
+ attr.should be_a(attr_class)
29
+ end
30
+ end
31
+ end
32
+
33
+ context "when given a name that cannot be represented as a ruby identifier" do
34
+ before do
35
+ @name = 'dash-dash' # - should get mapped to _
36
+ end
37
+
38
+ it "should map the name to something usable, and remember the old name" do
39
+ attr = LucidWorks::Schema::Attribute.factory @mock_schema, @name, :string
40
+ attr.name.should == :dash_dash
41
+ attr.type.should == :string
42
+ attr.instance_variable_get(:'@true_name').should == "dash-dash"
43
+ end
44
+ end
45
+
46
+ context "when given a name that is a reserved word" do
47
+ before do
48
+ @name = 'class'
49
+ end
50
+
51
+ it "should prefix the name with an underscore, and remember the old name" do
52
+ attr = LucidWorks::Schema::Attribute.factory @mock_schema, @name, :string
53
+ attr.name.should == :_class
54
+ attr.type.should == :string
55
+ attr.instance_variable_get(:'@true_name').should == "class"
56
+ end
57
+ end
58
+ end
59
+
60
+ describe "#encode_and_insert" do
61
+
62
+ it "should encode the attribute and insert it into the hash" do
63
+ attribute = LucidWorks::Schema::Attribute.factory(@mock_schema, 'attrname', :string)
64
+ hash = {}
65
+ attribute.encode_and_insert('foo', hash, false)
66
+ hash['attrname'].should == 'foo'
67
+ end
68
+
69
+ context "for an attribute whose real name could not be represented as a ruby identifer" do
70
+ before do
71
+ @true_name = "dash-dash"
72
+ end
73
+
74
+ it "should use it's real name when inserting it into the hash" do
75
+ attribute = LucidWorks::Schema::Attribute.factory(@mock_schema, @true_name, :string)
76
+ attribute.name.should == :dash_dash
77
+ hash = {}
78
+ attribute.encode_and_insert('foo', hash, false)
79
+ hash[@true_name].should == 'foo'
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "#human_value(value)" do
85
+ before :all do
86
+ # So we can test without actually needing ActionPack
87
+ class LucidWorks::Schema::IntegerAttribute
88
+ def number_with_delimiter(x)
89
+ x.to_s.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,")
90
+ end
91
+ end
92
+
93
+ class TrueClass
94
+ def to_yesno
95
+ self ? "yes" : "no"
96
+ end
97
+ end
98
+ end
99
+
100
+ context "for a boolean attribute" do
101
+ before do
102
+ @attribute = LucidWorks::Schema::Attribute.factory(@mock_schema, :booly, :boolean)
103
+ end
104
+
105
+ it "should to_yesno the attribute" do
106
+ @attribute.human_value(true).should == "yes"
107
+ end
108
+ end
109
+
110
+ context "for an integer attribute" do
111
+ before do
112
+ @attribute = LucidWorks::Schema::Attribute.factory(@mock_schema, :inty, :integer)
113
+ end
114
+
115
+ it "should number_with_delimeter the attribute" do
116
+ @attribute.human_value(12345678).should == "12,345,678"
117
+ end
118
+ end
119
+
120
+ context "for an attributed described as :string in the schema" do
121
+ before do
122
+ @attribute = LucidWorks::Schema::Attribute.factory(@mock_schema, :stringy, :string)
123
+ end
124
+
125
+ it "should just to_s the attribute" do
126
+ @attribute.human_value(true).should == "true"
127
+ end
128
+ end
129
+
130
+ #context "for an attribute described as :iso8601 in the schema" do
131
+ # it "should convert it to localtime to_s(:short)" do
132
+ # @widget.human_attribute_value(:when).should == "19 Mar 10:40"
133
+ # end
134
+ #end
135
+ end
136
+ end