lucid_works 0.1.1

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.
@@ -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