ffi-gphoto2 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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +90 -0
  7. data/Rakefile +6 -0
  8. data/examples/intervalometer.rb +12 -0
  9. data/examples/live_view.rb +21 -0
  10. data/ffi-gphoto2.gemspec +30 -0
  11. data/lib/ffi/gphoto2.rb +85 -0
  12. data/lib/ffi/gphoto2/camera.rb +12 -0
  13. data/lib/ffi/gphoto2/camera_abilities.rb +37 -0
  14. data/lib/ffi/gphoto2/camera_abilities_list.rb +15 -0
  15. data/lib/ffi/gphoto2/camera_capture_type.rb +8 -0
  16. data/lib/ffi/gphoto2/camera_driver_status.rb +9 -0
  17. data/lib/ffi/gphoto2/camera_event_type.rb +10 -0
  18. data/lib/ffi/gphoto2/camera_file.rb +28 -0
  19. data/lib/ffi/gphoto2/camera_file_access_type.rb +8 -0
  20. data/lib/ffi/gphoto2/camera_file_operation.rb +11 -0
  21. data/lib/ffi/gphoto2/camera_file_path.rb +9 -0
  22. data/lib/ffi/gphoto2/camera_file_type.rb +11 -0
  23. data/lib/ffi/gphoto2/camera_folder_operation.rb +10 -0
  24. data/lib/ffi/gphoto2/camera_list.rb +15 -0
  25. data/lib/ffi/gphoto2/camera_operation.rb +12 -0
  26. data/lib/ffi/gphoto2/camera_widget.rb +33 -0
  27. data/lib/ffi/gphoto2/camera_widget_type.rb +14 -0
  28. data/lib/ffi/gphoto2/entry.rb +9 -0
  29. data/lib/ffi/gphoto2/gp_context.rb +31 -0
  30. data/lib/ffi/gphoto2/gphoto_device_type.rb +7 -0
  31. data/lib/ffi/gphoto2_port.rb +31 -0
  32. data/lib/ffi/gphoto2_port/gp_port_info.rb +11 -0
  33. data/lib/ffi/gphoto2_port/gp_port_info_list.rb +14 -0
  34. data/lib/ffi/gphoto2_port/gp_port_result.rb +35 -0
  35. data/lib/ffi/gphoto2_port/gp_port_type.rb +12 -0
  36. data/lib/gphoto2.rb +41 -0
  37. data/lib/gphoto2/camera.rb +219 -0
  38. data/lib/gphoto2/camera_abilities.rb +44 -0
  39. data/lib/gphoto2/camera_abilities_list.rb +64 -0
  40. data/lib/gphoto2/camera_file.rb +69 -0
  41. data/lib/gphoto2/camera_file_path.rb +21 -0
  42. data/lib/gphoto2/camera_folder.rb +82 -0
  43. data/lib/gphoto2/camera_list.rb +43 -0
  44. data/lib/gphoto2/camera_widgets/camera_widget.rb +112 -0
  45. data/lib/gphoto2/camera_widgets/date_camera_widget.rb +19 -0
  46. data/lib/gphoto2/camera_widgets/menu_camera_widget.rb +4 -0
  47. data/lib/gphoto2/camera_widgets/radio_camera_widget.rb +41 -0
  48. data/lib/gphoto2/camera_widgets/range_camera_widget.rb +37 -0
  49. data/lib/gphoto2/camera_widgets/section_camera_widget.rb +4 -0
  50. data/lib/gphoto2/camera_widgets/text_camera_widget.rb +14 -0
  51. data/lib/gphoto2/camera_widgets/toggle_camera_widget.rb +18 -0
  52. data/lib/gphoto2/camera_widgets/window_camera_widget.rb +4 -0
  53. data/lib/gphoto2/context.rb +31 -0
  54. data/lib/gphoto2/entry.rb +38 -0
  55. data/lib/gphoto2/port_info.rb +70 -0
  56. data/lib/gphoto2/port_info_list.rb +46 -0
  57. data/lib/gphoto2/port_result.rb +7 -0
  58. data/lib/gphoto2/version.rb +3 -0
  59. data/spec/gphoto2/camera_abilities_list_spec.rb +45 -0
  60. data/spec/gphoto2/camera_abilities_spec.rb +38 -0
  61. data/spec/gphoto2/camera_file_path_spec.rb +27 -0
  62. data/spec/gphoto2/camera_file_spec.rb +71 -0
  63. data/spec/gphoto2/camera_folder_spec.rb +110 -0
  64. data/spec/gphoto2/camera_list_spec.rb +35 -0
  65. data/spec/gphoto2/camera_spec.rb +292 -0
  66. data/spec/gphoto2/camera_widgets/date_camera_widget_spec.rb +15 -0
  67. data/spec/gphoto2/camera_widgets/radio_camera_widget_spec.rb +31 -0
  68. data/spec/gphoto2/camera_widgets/range_camera_widget_spec.rb +27 -0
  69. data/spec/gphoto2/camera_widgets/text_camera_widget_spec.rb +15 -0
  70. data/spec/gphoto2/camera_widgets/toggle_camera_widget_spec.rb +21 -0
  71. data/spec/gphoto2/context_spec.rb +17 -0
  72. data/spec/gphoto2/entry_spec.rb +27 -0
  73. data/spec/gphoto2/port_info_list_spec.rb +33 -0
  74. data/spec/gphoto2/port_info_spec.rb +53 -0
  75. data/spec/spec_helper.rb +12 -0
  76. data/spec/support/shared_examples/camera_widget_examples.rb +89 -0
  77. metadata +194 -0
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ module GPhoto2
4
+ describe CameraFile do
5
+ let(:camera) { double('camera') }
6
+ let(:folder) { '/store_00010001' }
7
+ let(:name) { 'capt0100.jpg' }
8
+ let(:data_and_size) { ['data', 384] }
9
+
10
+ before do
11
+ CameraFile.any_instance.stub(:new)
12
+ CameraFile.any_instance.stub(:data_and_size).and_return(data_and_size)
13
+ end
14
+
15
+ describe '#preview' do
16
+ context 'when a folder and file are set' do
17
+ it 'returns false' do
18
+ file = CameraFile.new(camera, folder, name)
19
+ expect(file.preview?).to be_false
20
+ end
21
+ end
22
+
23
+ context 'when no folder or file is set' do
24
+ it 'returns true' do
25
+ file = CameraFile.new(camera)
26
+ expect(file.preview?).to be_true
27
+ end
28
+ end
29
+ end
30
+
31
+ describe '#save' do
32
+ let(:file) { CameraFile.new(camera, folder, name) }
33
+ let(:data) { data_and_size.first }
34
+
35
+ before do
36
+ File.stub(:binwrite)
37
+ end
38
+
39
+ context 'when a pathname is passed' do
40
+ it 'saves the data to the passed pathname' do
41
+ pathname = '/tmp/capt0100.jpg'
42
+ expect(File).to receive(:binwrite).with(pathname, data)
43
+ file.save(pathname)
44
+ end
45
+ end
46
+
47
+ context 'when no arguments are passed' do
48
+ it 'saves the data to the working directory using file path name' do
49
+ expect(File).to receive(:binwrite).with(name, data)
50
+ file.save
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#data' do
56
+ it 'returns the data of the camera file' do
57
+ file = CameraFile.new(camera, folder, name)
58
+ file.stub(:data_and_size).and_return(data_and_size)
59
+ expect(file.data).to eq(data_and_size.first)
60
+ end
61
+ end
62
+
63
+ describe '#size' do
64
+ it 'returns the size of the camera file' do
65
+ file = CameraFile.new(camera, folder, name)
66
+ file.stub(:data_and_size).and_return(data_and_size)
67
+ expect(file.size).to eq(data_and_size.last)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ module GPhoto2
4
+ describe CameraFolder do
5
+ let(:camera) { double('camera') }
6
+
7
+ describe '#root?' do
8
+ it 'returns true if the folder is the root' do
9
+ folder = CameraFolder.new(camera, '/')
10
+ expect(folder).to be_root
11
+ end
12
+ end
13
+
14
+ describe '#name' do
15
+ context 'when the folder is the root' do
16
+ it 'returns /' do
17
+ folder = CameraFolder.new(camera, '/')
18
+ expect(folder.name).to eq('/')
19
+ end
20
+ end
21
+
22
+ context 'when the folder is not the root' do
23
+ it 'returns the current folder name' do
24
+ folder = CameraFolder.new(camera, '/store_00010001/DCIM')
25
+ expect(folder.name).to eq('DCIM')
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#folders' do
31
+ it 'returns a list of subfolders' do
32
+ folder = CameraFolder.new(camera)
33
+
34
+ folders = 2.times.map { folder }
35
+ folder.stub(:folder_list_folders).and_return(folders)
36
+
37
+ expect(folder.folders).to eq(folders)
38
+ end
39
+ end
40
+
41
+ describe '#files' do
42
+ it 'returns a list of files in the folder' do
43
+ folder = CameraFolder.new(camera)
44
+
45
+ file = double('camera_file')
46
+ files = 2.times.map { file }
47
+ folder.stub(:folder_list_files).and_return(files)
48
+
49
+ expect(folder.files).to eq(files)
50
+ end
51
+ end
52
+
53
+ describe '#cd' do
54
+ let(:folder) { CameraFolder.new(camera, '/store_00010001') }
55
+
56
+ context 'when passed "."' do
57
+ it 'returns self' do
58
+ expect(folder.cd('.')).to be(folder)
59
+ end
60
+ end
61
+
62
+ context 'when passed ".."' do
63
+ it 'returns the parent folder' do
64
+ parent = folder.cd('..')
65
+ expect(parent.path).to eq('/')
66
+ end
67
+ end
68
+
69
+ context 'when passed a normal folder name' do
70
+ it 'returns a new folder changed to the new path' do
71
+ child = folder.cd('DCIM')
72
+ expect(child.path).to eq('/store_00010001/DCIM')
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#open' do
78
+ it 'returns a new CameraFile of a file in the folder' do
79
+ file = double('camera_file')
80
+ CameraFile.stub(:new).and_return(file)
81
+
82
+ folder = CameraFolder.new(camera)
83
+ expect(folder.open('capt0001.jpg')).to eq(file)
84
+ end
85
+ end
86
+
87
+ describe '#up' do
88
+ context 'when the folder is root' do
89
+ it 'returns self' do
90
+ folder = CameraFolder.new(camera, '/')
91
+ expect(folder.up).to be(folder)
92
+ end
93
+ end
94
+
95
+ context 'when the folder is not root' do
96
+ it 'returns the parent folder' do
97
+ folder = CameraFolder.new(camera, '/store_00010001')
98
+ expect(folder.up.path).to eq('/')
99
+ end
100
+ end
101
+ end
102
+
103
+ describe '#to_s' do
104
+ it 'returns the name of the folder' do
105
+ folder = CameraFolder.new(camera, '/store_00010001')
106
+ expect(folder.to_s).to eq('store_00010001')
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module GPhoto2
4
+ describe CameraList do
5
+ before do
6
+ CameraList.any_instance.stub(:new)
7
+ end
8
+
9
+ describe '#size' do
10
+ it 'returns the number of camera entries in the list' do
11
+ size = 2
12
+
13
+ list = CameraList.new
14
+ list.stub(:count).and_return(size)
15
+
16
+ expect(list.size).to eq(size)
17
+ end
18
+ end
19
+
20
+ describe '#to_a' do
21
+ it 'returns an array of camera entries' do
22
+ size = 2
23
+
24
+ list = CameraList.new
25
+ list.stub(:size).and_return(size)
26
+
27
+ ary = list.to_a
28
+
29
+ expect(ary.size).to eq(size)
30
+
31
+ ary.each { |e| expect(e).to be_kind_of(Entry) }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,292 @@
1
+ require 'spec_helper'
2
+
3
+ module GPhoto2
4
+ describe Camera do
5
+ let(:port) { double('entry', name: 'model', value: 'usb:250,006') }
6
+
7
+ describe '.all' do
8
+ let(:abilities_list) { double('camera_abilities_list') }
9
+ let(:camera_list) { double('camera_list') }
10
+ let(:camera) { Camera.new(port) }
11
+
12
+ before do
13
+ Context.stub(:stub)
14
+ CameraAbilitiesList.stub(:new).and_return(abilities_list)
15
+ abilities_list.stub(:detect).and_return(camera_list)
16
+ camera_list.stub(:to_a).and_return([port])
17
+ end
18
+
19
+ it 'returns a list of device entries' do
20
+ list = Camera.all
21
+ expect(list).to be_kind_of(Array)
22
+ expect(list.first).to be_kind_of(Camera)
23
+ end
24
+ end
25
+
26
+ describe '.first' do
27
+ context 'when devices are automatically detected' do
28
+ it 'returns a new Camera using the first entry' do
29
+ camera = Camera.new(port)
30
+ Camera.stub(:all).and_return([camera])
31
+ expect(Camera.first).to be_kind_of(Camera)
32
+ end
33
+ end
34
+
35
+ context 'when no devices are detected' do
36
+ it 'raises a RuntimeError' do
37
+ Camera.stub(:all).and_return([])
38
+ expect { Camera.first }.to raise_error(RuntimeError)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '.open' do
44
+ let(:camera) { double('camera') }
45
+ before { Camera.stub(:new).and_return(camera) }
46
+
47
+ context 'when a block is given' do
48
+ it 'yeilds a new camera instance' do
49
+ expect(Camera).to receive(:open).and_yield(camera)
50
+ Camera.open(port) { |c| }
51
+ end
52
+
53
+ it 'finalizes the camera when the block terminates' do
54
+ expect(camera).to receive(:finalize)
55
+ Camera.open(port) { |c| }
56
+ end
57
+ end
58
+
59
+ context 'when no block is given' do
60
+ it 'returns a new camera instance' do
61
+ expect(Camera.open(port)).to eq(camera)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '.where' do
67
+ it 'filters all detected cameras by model' do
68
+ cameras = %w[cheese toast wine].map { |model| double('camera', model: model) }
69
+ Camera.stub(:all).and_return(cameras)
70
+ expect(Camera.where(/e/)).to match_array([cameras[0], cameras[2]])
71
+ end
72
+ end
73
+
74
+ describe '#exit' do
75
+ it 'closes the camera connection' do
76
+ camera = Camera.new(port)
77
+ camera.stub(:_exit)
78
+ expect(camera).to receive(:_exit)
79
+ camera.exit
80
+ end
81
+ end
82
+
83
+ describe '#capture' do
84
+ let(:camera) { Camera.new(port) }
85
+ let(:path) { double('camera_file_path', folder: 'folder', name: 'name') }
86
+
87
+ before do
88
+ camera.stub(:_capture).and_return(path)
89
+ end
90
+
91
+ it 'saves the camera configuration' do
92
+ expect(camera).to receive(:save)
93
+ camera.capture
94
+ end
95
+
96
+ it 'returns a new CameraFile' do
97
+ expect(camera).to receive(:_capture)
98
+ expect(camera.capture).to be_kind_of(CameraFile)
99
+ end
100
+ end
101
+
102
+ describe '#preview' do
103
+ let(:camera) { Camera.new(port) }
104
+ let(:file) { double('camera_file') }
105
+
106
+ before do
107
+ camera.stub(:save)
108
+ camera.stub(:capture_preview).and_return(file)
109
+ end
110
+
111
+ it 'saves the camera configuration' do
112
+ expect(camera).to receive(:save)
113
+ camera.preview
114
+ end
115
+
116
+ it 'returns a new CameraFile' do
117
+ expect(camera.preview).to eq(file)
118
+ end
119
+ end
120
+
121
+ describe '#wait' do
122
+ let(:camera) { Camera.new(port) }
123
+ let(:event) { :capture_complete }
124
+
125
+ before do
126
+ camera.stub(:wait_for_event).and_return(event)
127
+ end
128
+
129
+ it 'waits for a camera event' do
130
+ expect(camera).to receive(:wait_for_event)
131
+ camera.wait
132
+ end
133
+
134
+ it 'returns an event symbol' do
135
+ expect(camera.wait).to eq(event)
136
+ end
137
+ end
138
+
139
+ describe '#window' do
140
+ let(:camera) { Camera.new(port) }
141
+ let(:window) { double('camera_widget') }
142
+
143
+ it 'always returns the same CameraWidget instance' do
144
+ camera.stub(:get_config).and_return(window)
145
+ expect(camera.window).to eq(window)
146
+ expect(camera.window).to eq(window)
147
+ end
148
+ end
149
+
150
+ describe '#config' do
151
+ it 'returns a map of configuration widgets' do
152
+ camera = Camera.new(port)
153
+ window = double('camera_widget')
154
+ camera.stub(:window).and_return(window)
155
+ window.stub(:flatten).and_return({ 'iso' => window })
156
+
157
+ expect(camera.config).to eq({ 'iso' => window })
158
+ end
159
+ end
160
+
161
+ describe '#filesystem' do
162
+ let(:camera) { Camera.new(port) }
163
+
164
+ context 'when a path is passed' do
165
+ let(:path) { '/store_00010001' }
166
+
167
+ it 'assumes the path is absolute' do
168
+ fs = camera.filesystem(path[1..-1])
169
+ expect(fs.path).to eq(path)
170
+ end
171
+
172
+ it 'returns a folder at the path that was passed' do
173
+ fs = camera.filesystem(path)
174
+ expect(fs.path).to eq(path)
175
+ end
176
+ end
177
+
178
+ context 'when no path is passed' do
179
+ it 'returns a folder at the root of the filesystem' do
180
+ fs = camera.filesystem
181
+ expect(fs.path).to eq('/')
182
+ end
183
+ end
184
+ end
185
+
186
+ describe '#file' do
187
+ it 'retrieves a file from the camera' do
188
+ camera = Camera.new(port)
189
+ camera.stub(:file_get)
190
+ expect(camera).to receive(:file_get)
191
+ camera.file(double('camera_file'))
192
+ end
193
+ end
194
+
195
+ describe '#[]' do
196
+ let(:window) { double('camera_widget') }
197
+
198
+ let(:camera) do
199
+ camera = Camera.new(port)
200
+ camera.stub(:config).and_return({ 'iso' => window })
201
+ camera
202
+ end
203
+
204
+ context 'when the specified key exists' do
205
+ it 'returns a CameraWidget' do
206
+ expect(camera['iso']).to eq(window)
207
+ end
208
+ end
209
+
210
+ context 'when the specified key does not exist' do
211
+ it 'returns nil' do
212
+ expect(camera['shutterspeed2']).to be_nil
213
+ end
214
+ end
215
+ end
216
+
217
+ describe '#[]=' do
218
+ let(:window) do
219
+ window = double('camera_widget')
220
+ expect(window).to receive(:value=).with(400)
221
+ window
222
+ end
223
+
224
+ let(:camera) do
225
+ camera = Camera.new(port)
226
+ camera.stub(:[]).and_return(window)
227
+ camera
228
+ end
229
+
230
+ it "set a widget's value" do
231
+ camera['iso'] = 400
232
+ end
233
+
234
+ it 'marks the camera as dirty' do
235
+ camera['iso'] = 400
236
+ expect(camera).to be_dirty
237
+ end
238
+
239
+ it 'returns the passed value' do
240
+ expect(camera['iso'] = 400).to eq(400)
241
+ end
242
+ end
243
+
244
+ describe '#dirty?' do
245
+ context 'when the configuration changed' do
246
+ it 'returns true' do
247
+ camera = Camera.new(port)
248
+ camera.stub_chain(:[], :value=)
249
+
250
+ camera['iso'] = 400
251
+
252
+ expect(camera).to be_dirty
253
+ end
254
+ end
255
+
256
+ context 'when the configuration has not changed' do
257
+ it 'returns false' do
258
+ camera = Camera.new(port)
259
+ expect(camera).not_to be_dirty
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#save' do
265
+ let(:camera) do
266
+ camera = Camera.new(port)
267
+ camera.stub_chain(:[], :value=)
268
+ camera.stub(:set_config)
269
+ camera
270
+ end
271
+
272
+ context 'when the camera is marked as dirty' do
273
+ it 'returns true' do
274
+ camera['iso'] = 400
275
+ expect(camera.save).to be_true
276
+ end
277
+
278
+ it 'marks the camera as not dirty' do
279
+ camera['iso'] = 400
280
+ camera.save
281
+ expect(camera).not_to be_dirty
282
+ end
283
+ end
284
+
285
+ context 'when the camera is not marked as dirty' do
286
+ it 'returns false' do
287
+ expect(camera.save).to be_false
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end