lucid_works 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ module LucidWorks
2
+
3
+ class Collection < Base
4
+
5
+ self.primary_key = :name
6
+
7
+ has_many :datasources
8
+ has_one :info, :settings, :index
9
+
10
+ def empty!
11
+ build_index.destroy(:params => {:key => 'iaccepttherisk'})
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Collection
3
+
4
+ class Index < Base
5
+ self.singleton = true
6
+ belongs_to :collection
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Collection
3
+
4
+ class Info < Base
5
+ self.singleton = true
6
+ belongs_to :collection
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Collection
3
+
4
+ class Settings < Base
5
+ self.singleton = true
6
+ belongs_to :collection
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ module LucidWorks
2
+
3
+ class Datasource < Base
4
+ belongs_to :collection
5
+ has_many :histories, :class_name => :history
6
+ has_one :status, :history, :schedule, :index
7
+
8
+ TYPES = {
9
+ :FileSystemDataSource => {
10
+ :name_l10n_key => 'data_source.short_type.filesystem',
11
+ :crawler => 'lucid.aperture',
12
+ :type => 'file'
13
+ },
14
+ :WebDataSource => {
15
+ :name_l10n_key => 'data_source.short_type.web_site',
16
+ :crawler => 'lucid.aperture',
17
+ :type => 'web'
18
+ },
19
+ :SolrXmlDataSource => {
20
+ :name_l10n_key => 'data_source.short_type.solr',
21
+ :crawler => 'lucid.solrxml',
22
+ :type => 'solrxml'
23
+ },
24
+ :JDBCDataSource => {
25
+ :name_l10n_key => 'data_source.short_type.database',
26
+ :crawler => 'lucid.jdbc',
27
+ :type => 'jdbc'
28
+ },
29
+ :SharePointDataSource => {
30
+ :name_l10n_key => 'data_source.short_type.sharepoint',
31
+ :crawler => 'lucid.gcm',
32
+ :type => 'sharepoint'
33
+ }
34
+ }.with_indifferent_access
35
+
36
+ def empty!
37
+ build_index.destroy
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ module LucidWorks
2
+ class Datasource
3
+
4
+ class History < Base
5
+ belongs_to :datasource
6
+ self.collection_name = 'history' # i.e. not the plural 'histories'
7
+
8
+ def doc_count
9
+ numUpdated + numNew + numUnchanged
10
+ end
11
+
12
+ def elapsed_time
13
+ crawlStopped.to_datetime - crawlStarted.to_datetime
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Datasource
3
+
4
+ class Index < Base
5
+ self.singleton = true
6
+ belongs_to :datasource
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Datasource
3
+
4
+ class Schedule < Base
5
+ self.singleton = true
6
+ belongs_to :datasource
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module LucidWorks
2
+ class Datasource
3
+
4
+ class Status < Base
5
+ self.singleton = true
6
+ belongs_to :datasource
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module LucidWorks
2
+ class Exception < ::StandardError
3
+ end
4
+
5
+ class ResourceNotFound < Exception ; end
6
+ end
@@ -0,0 +1,29 @@
1
+ module RestClient
2
+ class Request
3
+
4
+ # Extract the query parameters for get request and append them to the url
5
+ def process_get_params url, headers
6
+ # LucidWorks: we also need params on a :delete
7
+ if [:get, :head, :delete].include? method
8
+ # if [:get, :head].include? method
9
+ get_params = {}
10
+ headers.delete_if do |key, value|
11
+ if 'params' == key.to_s.downcase && value.is_a?(Hash)
12
+ get_params.merge! value
13
+ true
14
+ else
15
+ false
16
+ end
17
+ end
18
+ unless get_params.empty?
19
+ query_string = get_params.collect { |k, v| "#{k.to_s}=#{CGI::escape(v.to_s)}" }.join('&')
20
+ url + "?#{query_string}"
21
+ else
22
+ url
23
+ end
24
+ else
25
+ url
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ module LucidWorks
2
+ #
3
+ # The LucidWorks::Server class is the starting point for access to a LucidWorks Solr search server.
4
+ #
5
+ class Server
6
+ include Associations
7
+
8
+ has_many :collections
9
+
10
+ DEFAULT_REST_API_PATH = "/api"
11
+
12
+ attr_accessor :host, :path
13
+
14
+ def initialize(server_uri, options = {})
15
+ @host = server_uri
16
+ @path = options.delete(:path) || DEFAULT_REST_API_PATH
17
+ end
18
+
19
+ def uri
20
+ @host + @path
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module LucidWorks
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "lucid_works/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "lucid_works"
7
+ s.version = LucidWorks::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Sam Pierson"]
10
+ s.email = ["sam.pierson@lucidimagination.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Ruby wrapper for the LucidWorks REST API}
13
+ s.description = %q{Ruby wrapper for the LucidWorks REST API}
14
+
15
+ s.rubyforge_project = "lucid_works"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_runtime_dependency 'activesupport', '>= 3'
23
+ s.add_runtime_dependency 'activemodel', '>= 3'
24
+ s.add_runtime_dependency 'rest-client', '>= 1.6.1'
25
+ s.add_runtime_dependency 'json'
26
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe LucidWorks::Associations do
4
+ before :all do
5
+ @fake_server_uri = "http://fakehost.com:8888"
6
+ @server = LucidWorks::Server.new(@fake_server_uri)
7
+
8
+ class ::Blog < LucidWorks::Base
9
+ has_many :posts
10
+ has_one :homepage
11
+ end
12
+ @blog = ::Blog.new(:parent => @server)
13
+
14
+ class ::Post < LucidWorks::Base
15
+ belongs_to :blog
16
+ end
17
+
18
+ class ::Homepage < LucidWorks::Base
19
+ self.singleton = true
20
+ belongs_to :blog
21
+ end
22
+ end
23
+
24
+ describe ".has_one" do
25
+ describe "#<child>!" do
26
+ it "should call Child.find" do
27
+ Homepage.should_receive(:find).with(:parent => @blog)
28
+ @blog.homepage!
29
+ end
30
+ end
31
+
32
+ describe "#<child>" do
33
+ it "should call child! the first time then return the cached value thereafter" do
34
+ mock_homepage = double('homepage')
35
+ Homepage.should_receive(:find).once.and_return(mock_homepage)
36
+ @blog.homepage.should == mock_homepage
37
+ @blog.homepage.should == mock_homepage
38
+ end
39
+ end
40
+
41
+ describe "#build_<child>" do
42
+ it "should create a new child with persisted = true" do
43
+ homepage = @blog.build_homepage
44
+ homepage.should be_a(Homepage)
45
+ homepage.should be_persisted
46
+ end
47
+ end
48
+ end
49
+
50
+ describe ".has_many" do
51
+ describe "#<children>!" do
52
+ it "should call Child.all" do
53
+ Post.should_receive(:all).with(:parent => @blog)
54
+ @blog.posts!
55
+ end
56
+ end
57
+
58
+ describe "#<children>" do
59
+ it "should call children! the first time then return the cached value thereafter" do
60
+ mock_posts = double('some posts')
61
+ Post.should_receive(:find).once.and_return(mock_posts)
62
+ @blog.posts.should == mock_posts
63
+ @blog.posts.should == mock_posts
64
+ end
65
+ end
66
+
67
+ describe "#<child>" do
68
+ it "should call Child.find" do
69
+ Post.should_receive(:find).with('child_id', :parent => @blog)
70
+ @blog.post('child_id')
71
+ end
72
+ end
73
+
74
+ describe "create_<child>" do
75
+ it "should call Child.create" do
76
+ Post.should_receive(:create).with(:name => 'child_name', :parent => @blog)
77
+ @blog.create_post(:name => 'child_name')
78
+ end
79
+ end
80
+
81
+ describe "#build_<child>" do
82
+ it "should create a new child with persisted = true" do
83
+ post = @blog.build_post
84
+ post.should be_a(Post)
85
+ post.should_not be_persisted
86
+ end
87
+ end
88
+ end
89
+
90
+ describe ".belongs_to" do
91
+
92
+ describe ".belongs_to_association_name (private)" do
93
+ it "should return the name of the association" do
94
+ Post.send(:belongs_to_association_name).should == :blog
95
+ end
96
+ end
97
+
98
+ describe ".parent_class" do
99
+ it "should return the parent class" do
100
+ Post.parent_class.should == Blog
101
+ end
102
+ end
103
+
104
+ describe "#initialize" do
105
+ it "should allow :<association_name> instead of :parent" do
106
+ lambda {
107
+ Post.new(:blog => @blog)
108
+ }.should_not raise_error
109
+ end
110
+ end
111
+
112
+ describe "#collection_url" do
113
+ it "should generate a url using its parents url and it's own name" do
114
+ @blog.id = 'fake_blog_id'
115
+ post = Post.new(:parent => @blog)
116
+ post.id = 'fake_post_id'
117
+ post.collection_url.should == "http://fakehost.com:8888/api/blogs/fake_blog_id/posts"
118
+ end
119
+ end
120
+
121
+ describe "#member_url" do
122
+ it "should generate a url using its parents url and it's own name and id" do
123
+ @blog.id = 'fake_blog_id'
124
+ post = Post.new(:parent => @blog)
125
+ post.id = 'fake_post_id'
126
+ post.send(:member_url).should == "http://fakehost.com:8888/api/blogs/fake_blog_id/posts/fake_post_id"
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,399 @@
1
+ require 'spec_helper'
2
+
3
+ describe LucidWorks::Base do
4
+ before :all do
5
+ fake_server = "http://127.0.0.1:123456"
6
+ @server = LucidWorks::Server.new(fake_server)
7
+ @fake_server_uri = "#{fake_server}/api"
8
+
9
+ class Widget < LucidWorks::Base
10
+ has_one :singleton_widget
11
+ end
12
+ WIDGET1_JSON = '{"id":1,"name":"widget1","size":"small"}'
13
+ WIDGET2_JSON = '{"id":2,"name":"widget2","size":"medium"}'
14
+ WIDGETS_JSON = "[#{WIDGET1_JSON},#{WIDGET2_JSON}]"
15
+
16
+ class SingletonWidget < LucidWorks::Base
17
+ self.singleton = true
18
+ end
19
+ end
20
+
21
+ describe "class methods" do
22
+ describe ".new" do
23
+ it "should require a hash argument" do
24
+ lambda {
25
+ Widget.new(@server)
26
+ }.should raise_error(ArgumentError)
27
+ end
28
+
29
+ it "should raise if not supplied with a server or parent" do
30
+ lambda {
31
+ Widget.new({})
32
+ }.should raise_error(ArgumentError)
33
+ end
34
+
35
+ it "should raise if the provided parent is not a LucidWorks::Server or LucidWorks::Base" do
36
+ lambda {
37
+ Widget.new(:parent => "bogoserver")
38
+ }.should raise_error(ArgumentError)
39
+
40
+ lambda {
41
+ @parent = Widget.new(:parent => @server)
42
+ }.should_not raise_error
43
+
44
+ lambda {
45
+ Widget.new(:parent => @parent)
46
+ }.should_not raise_error
47
+ end
48
+
49
+ it "should raise if the provided parent is not a LucidWorks::Base" do
50
+ lambda {
51
+ Widget.new(:parent => "bogoparent")
52
+ }.should raise_error(ArgumentError)
53
+ end
54
+
55
+ it "should save the parent" do
56
+ widget = Widget.new(:parent => @server)
57
+ widget.parent.should == @server
58
+ end
59
+
60
+ it "should save all its attributes" do
61
+ widget = Widget.new(:name => 'fake_name', :size => 'fake_size', :parent => @server)
62
+ widget.name.should == 'fake_name'
63
+ widget.size.should == 'fake_size'
64
+ end
65
+
66
+ it "should default persisted to false" do
67
+ widget = Widget.new(:parent => @server)
68
+ widget.should_not be_persisted
69
+ end
70
+
71
+ it "should save persisted if provided" do
72
+ widget = Widget.new(:persisted => true, :parent => @server)
73
+ widget.should be_persisted
74
+ end
75
+
76
+ context "for a singleton" do
77
+ it "should always set singleton" do
78
+ widget = SingletonWidget.new(:parent => @server)
79
+ widget.should be_persisted
80
+ end
81
+ end
82
+ end
83
+
84
+ describe ".create" do
85
+ it "should call new and save" do
86
+ mock_widget = double('a mock widget')
87
+ Widget.should_receive(:new).with('fake_args').and_return(mock_widget)
88
+ mock_widget.should_receive(:save)
89
+ Widget.create('fake_args')
90
+ end
91
+ end
92
+
93
+ describe ".find" do
94
+ context ":all" do
95
+ it "should call RestClient.get with the appropriate URL and parse the response into models" do
96
+ correct_url = "#{@fake_server_uri}/widgets"
97
+ RestClient.should_receive(:get).with(correct_url).and_return(WIDGETS_JSON)
98
+
99
+ widgets = Widget.find(:all, :parent => @server)
100
+ widgets.size.should == 2
101
+
102
+ widget1 = widgets.find { |c| c.id == 1 }
103
+ widget1.should be_a(Widget)
104
+ widget1.name.should == 'widget1'
105
+ widget1.size.should == 'small'
106
+
107
+ widget2 = widgets.find { |c| c.id == 2 }
108
+ widget2.should be_a(Widget)
109
+ widget2.name.should == 'widget2'
110
+ widget2.size.should == 'medium'
111
+ end
112
+ end
113
+
114
+ context ":one" do
115
+ it "should call RestClient.get with the appropriate URL and parse the response into a model" do
116
+ correct_url = "#{@fake_server_uri}/widgets/1"
117
+ RestClient.should_receive(:get).with(correct_url).and_return(WIDGET1_JSON)
118
+
119
+ widget = Widget.find(:one, 1, :parent => @server)
120
+ widget.should be_a(Widget)
121
+ widget.name.should == 'widget1'
122
+ widget.size.should == 'small'
123
+ end
124
+ end
125
+
126
+ context ":singleton" do
127
+ it "should call RestClient.get with the appropriate URL and parse the response into a model" do
128
+ correct_url = "#{@fake_server_uri}/singleton_widget"
129
+ RestClient.should_receive(:get).with(correct_url).and_return(WIDGET1_JSON)
130
+
131
+ widget = SingletonWidget.find(:singleton, :parent => @server)
132
+ widget.should be_a(SingletonWidget)
133
+ widget.name.should == 'widget1'
134
+ widget.size.should == 'small'
135
+ end
136
+ end
137
+
138
+ describe ":include option" do
139
+ context "with a single argument" do
140
+ it "should retrieve the :included submodel" do
141
+ RestClient.should_receive(:get).with("#{@fake_server_uri}/widgets").and_return(WIDGETS_JSON)
142
+ mock_singleton1 = double('singleton widget1')
143
+ mock_singleton2 = double('singleton widget2')
144
+ SingletonWidget.should_receive(:find).and_return(mock_singleton1, mock_singleton2)
145
+
146
+ widgets = Widget.find(:all, :include => :singleton_widget, :parent => @server)
147
+
148
+ widgets.first.singleton_widget.should == mock_singleton1
149
+ widgets.last.singleton_widget.should == mock_singleton2
150
+ end
151
+ end
152
+
153
+ context "with a list of submodels" do
154
+ before :all do
155
+ # Give widget a second submodel
156
+ class Widget < LucidWorks::Base ; has_many :other_widgets ; end
157
+ class OtherWidget < LucidWorks::Base ; belongs_to :widget ; end
158
+ end
159
+
160
+ it "should retrieve all the :included submodels" do
161
+ RestClient.should_receive(:get).with("#{@fake_server_uri}/widgets").and_return(WIDGETS_JSON)
162
+ mock_singleton1 = double('singleton widget1')
163
+ mock_singleton2 = double('singleton widget2')
164
+ mock_other_widgets_1 = double('other widgets 1')
165
+ mock_other_widgets_2 = double('other widgets 2')
166
+ SingletonWidget.should_receive(:find).and_return(mock_singleton1, mock_singleton2)
167
+ OtherWidget.should_receive(:find).and_return(mock_other_widgets_1, mock_other_widgets_2)
168
+
169
+ widgets = Widget.find(:all, :include => [:singleton_widget, :other_widgets], :parent => @server)
170
+
171
+ widgets.first.singleton_widget.should == mock_singleton1
172
+ widgets.first.other_widgets.should == mock_other_widgets_1
173
+ widgets.last.singleton_widget.should == mock_singleton2
174
+ widgets.last.other_widgets.should == mock_other_widgets_2
175
+ end
176
+ end
177
+ end
178
+ end
179
+
180
+ describe ".all" do
181
+ it "should call find(:all)" do
182
+ Widget.should_receive(:find).with(:all, :parent => @server).and_return([])
183
+
184
+ Widget.all(:parent => @server)
185
+ end
186
+ end
187
+
188
+ describe ".first" do
189
+ it "should call find(:all) and return the first" do
190
+ mock_coll1 = double("collection 1")
191
+ mock_coll2 = double("collection 2")
192
+ Widget.should_receive(:find).with(:all, :parent => @server).and_return([mock_coll1, mock_coll2])
193
+
194
+ Widget.first(:parent => @server).should == mock_coll1
195
+ end
196
+ end
197
+ end
198
+
199
+ describe "instance methods" do
200
+
201
+ describe "collection_url" do
202
+ it "should generate a url from the server and its name" do
203
+ widget = Widget.new(:parent => @server, :id => 1234)
204
+ widget.collection_url.should == "#{@fake_server_uri}/widgets"
205
+ end
206
+ end
207
+
208
+ describe "member_url" do
209
+ it "should generate a url from the server, its name and its id" do
210
+ widget = Widget.new(:parent => @server, :id => 1234)
211
+ widget.member_url.should == "#{@fake_server_uri}/widgets/1234"
212
+ end
213
+ end
214
+
215
+ describe "attributes" do
216
+ before { @widget = Widget.new(:known_attr => 'known_value', :nil_attr => nil, :parent => @server) }
217
+
218
+ describe '#attr=' do
219
+ context "for an unknown attr" do
220
+ it "should set an attribute" do
221
+ @widget.new_attr = "fake_value"
222
+ @widget.new_attr.should == 'fake_value'
223
+ end
224
+ end
225
+ context "for a known attr" do
226
+ it "should set an attribute" do
227
+ @widget.known_attr = "new_value"
228
+ @widget.known_attr.should == 'new_value'
229
+ end
230
+ end
231
+ end
232
+
233
+ describe "#attr" do
234
+ context "for an unknown attr" do
235
+ it "should raise an error" do
236
+ lambda {
237
+ @widget.bogus_attr
238
+ }.should raise_error("Unknown attribute: 'bogus_attr'")
239
+ end
240
+ end
241
+ context "for a known attr" do
242
+ it "should return the attr" do
243
+ @widget.known_attr.should == 'known_value'
244
+ end
245
+ end
246
+ end
247
+
248
+ describe "#attr?" do
249
+ context "for an unknown attr" do
250
+ it "should raise an error" do
251
+ lambda {
252
+ @widget.bogus_attr?
253
+ }.should raise_error("Unknown attribute: 'bogus_attr'")
254
+ end
255
+ end
256
+ context "for a known attr" do
257
+ it "should return true or false" do
258
+ @widget.known_attr?.should be_true
259
+ @widget.nil_attr?.should be_false
260
+ end
261
+ end
262
+ end
263
+ end
264
+
265
+ describe "#save" do
266
+ context "for a new model" do
267
+ context "with valid attributes" do
268
+ before do
269
+ @widget_attrs = { :name => 'widget3', :size => 'large' }
270
+ @widget = Widget.new @widget_attrs.merge(:parent => @server)
271
+ end
272
+
273
+ it "should post to the correct address" do
274
+ correct_url = "#{@fake_server_uri}/widgets"
275
+ expected_json = @widget_attrs.to_json
276
+ RestClient.should_receive(:post).with(correct_url, expected_json, :content_type => :json)
277
+ @widget.save
278
+ end
279
+
280
+ it "should set persisted" do
281
+ RestClient.should_receive(:post)
282
+ @widget.save
283
+ @widget.should be_persisted
284
+ end
285
+ end
286
+
287
+ context "with invalid attributes" do
288
+ before :all do
289
+ @widget_attrs = { :name => '', :size => ''}
290
+ ERROR_422_RESPONSE = '{"errors":[{"message":"name is a required key","key":"name"},{"message":"name must consist of only A-Z a-z 0-9 - _","key":"name"}],"http_status_name":"Unprocessable Entity","http_status_code":422}'
291
+ # ERROR_500_RESPONSE = '{"errors":[{"message":"The server encountered an unexpected condition which prevented it from fulfilling the request","key":""}],"http_status_name":"Internal Server Error","http_status_code":500}'
292
+ RestClient.stub(:post) {
293
+ e = RestClient::UnprocessableEntity.new
294
+ e.response = ERROR_422_RESPONSE
295
+ raise e
296
+ }
297
+
298
+ @widget = Widget.new @widget_attrs.merge(:parent => @server)
299
+ @returncode = @widget.save
300
+ end
301
+
302
+ it "should set errors on the model" do
303
+ @widget.errors.should_not be_empty
304
+ @widget.errors['name'].should_not be_empty
305
+ end
306
+
307
+ it "should not set persisted" do
308
+ @widget.should_not be_persisted
309
+ end
310
+
311
+ it "should return false" do
312
+ @returncode.should be_false
313
+ end
314
+ end
315
+
316
+ describe "local validation" do
317
+ before do
318
+ class WidgetRequiringName < LucidWorks::Base
319
+ validates_presence_of :name
320
+ end
321
+ end
322
+
323
+ it "should validate presence of" do
324
+ widget = WidgetRequiringName.new(:parent => @server)
325
+ RestClient.should_not_receive(:post)
326
+ widget.should_not be_valid
327
+ widget.errors[:name].should == ["can't be blank"]
328
+ widget.save
329
+ end
330
+ end
331
+ end
332
+
333
+ context "for an existing model" do
334
+ it "should PUT to the correct address" do
335
+ @widget_attrs = { :name => 'widget3', :size => 'large' }
336
+ @widget = Widget.new @widget_attrs.merge(:parent => @server)
337
+ @widget.persisted = true
338
+ @widget.id = 1234
339
+ correct_url = "#{@fake_server_uri}/widgets/#{@widget.id}"
340
+ expected_json = @widget_attrs.to_json
341
+
342
+ RestClient.should_receive(:put).with(correct_url, expected_json, :content_type => :json)
343
+ @widget.save
344
+ end
345
+ end
346
+
347
+ context "for a singleton" do
348
+ before do
349
+ @widget_attrs = { :name => 'widget3', :size => 'large' }
350
+ @widget = SingletonWidget.new(@widget_attrs.merge :parent => @server, :persisted => true)
351
+ end
352
+
353
+ it "should put to the correct address" do
354
+ expected_json = @widget_attrs.to_json
355
+ correct_url = "#{@fake_server_uri}/singleton_widget"
356
+ RestClient.should_receive(:put).with(correct_url, expected_json, :content_type => :json)
357
+
358
+ @widget.save
359
+ end
360
+ end
361
+ end
362
+
363
+ describe "#destroy" do
364
+ it "should call RestClient.delete with the appropriate URL" do
365
+ widget = Widget.new(:parent => @server)
366
+ widget.id = 27
367
+ RestClient.should_receive(:delete).with("#{@fake_server_uri}/widgets/27", {})
368
+ widget.destroy
369
+ end
370
+ end
371
+ end
372
+
373
+ describe "for a model with primary key other than 'id'" do
374
+ before :all do
375
+ class NamedWidget < LucidWorks::Base
376
+ self.primary_key = :name
377
+ end
378
+ NAMED_WIDGET1_JSON = '{"name":"widget1","size":"small"}'
379
+ NAMED_WIDGET2_JSON = '{"name":"widget2","size":"medium"}'
380
+ NAMED_WIDGETS_JSON = "[#{WIDGET1_JSON},#{WIDGET2_JSON}]"
381
+ end
382
+
383
+ describe "#id" do
384
+ it "should return the primary key's value" do
385
+ w = NamedWidget.new(:name => 'fake_name', :parent => @server)
386
+ w.id.should == 'fake_name'
387
+ end
388
+ end
389
+
390
+ describe "#id=" do
391
+ it "should set the primary key's value" do
392
+ w = NamedWidget.new(:name => 'fake_name', :parent => @server)
393
+ w.id = 'new_name'
394
+ w.id.should == 'new_name'
395
+ w.name.should == 'new_name'
396
+ end
397
+ end
398
+ end
399
+ end