cups 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/cups.c ADDED
@@ -0,0 +1,457 @@
1
+ #include <ruby_cups.h>
2
+
3
+ static int num_options;
4
+ static cups_option_t *options;
5
+ cups_dest_t *dests, *dest;
6
+ VALUE rubyCups, printJobs;
7
+
8
+
9
+ /*
10
+ * call-seq:
11
+ * PrintJob.new(filename, printer=nil)
12
+ *
13
+ * Initializes a new PrintJob object. If no target printer/class is specified, the default is chosen.
14
+ * Note the specified file does not have to exist until print is called.
15
+ */
16
+ static VALUE job_init(int argc, VALUE* argv, VALUE self)
17
+ {
18
+ VALUE filename, printer;
19
+
20
+ rb_scan_args(argc, argv, "11", &filename, &printer);
21
+
22
+ rb_iv_set(self, "@filename", filename);
23
+
24
+ if (NIL_P(printer)) {
25
+
26
+ // Fall back to default printer
27
+ VALUE def_p = rb_funcall(rubyCups, rb_intern("default_printer"), 0);
28
+ rb_iv_set(self, "@printer", def_p);
29
+
30
+ } else {
31
+ // First call Cups#show_destinations
32
+ VALUE dest_list = rb_funcall(rubyCups, rb_intern("show_destinations"), 0);
33
+ // Then check the printer arg is included in the returned array...
34
+ if (rb_ary_includes(dest_list, printer)) {
35
+ rb_iv_set(self, "@printer", printer);
36
+ } else {
37
+ rb_raise(rb_eRuntimeError, "The printer or destination doesn't exist!");
38
+ }
39
+ }
40
+ return self;
41
+ }
42
+
43
+ /*
44
+ * call-seq:
45
+ * print_job.print -> Fixnum
46
+ *
47
+ * Submit a print job to the selected printer or class. Returns true on success.
48
+ */
49
+ static VALUE cups_print(VALUE self, VALUE file, VALUE printer)
50
+ {
51
+ int job_id;
52
+ file = rb_iv_get(self, "@filename");
53
+ printer = rb_iv_get(self, "@printer");
54
+
55
+ char *fname = RSTRING_PTR(file); // Filename
56
+ char *target = RSTRING_PTR(printer); // Target printer string
57
+
58
+ FILE *fp = fopen(fname,"r");
59
+ // Check @filename actually exists...
60
+ if( fp ) {
61
+ fclose(fp);
62
+ job_id = cupsPrintFile(target, fname, "rCUPS", num_options, options); // Do it.
63
+ rb_iv_set(self, "@job_id", INT2NUM(job_id));
64
+ return Qtrue;
65
+ } else {
66
+ // and if it doesn't...
67
+ rb_raise(rb_eRuntimeError, "Couldn't find file");
68
+ return self;
69
+ }
70
+ }
71
+
72
+ /*
73
+ * call-seq:
74
+ * Cups.show_destinations -> Array
75
+ *
76
+ * Show all destinations on the default server
77
+ */
78
+ static VALUE cups_show_dests(VALUE self)
79
+ {
80
+ VALUE dest_list;
81
+ int i;
82
+ int num_dests = cupsGetDests(&dests); // Size of dest_list array
83
+ dest_list = rb_ary_new2(num_dests);
84
+
85
+ for (i = num_dests, dest = dests; i > 0; i --, dest ++) {
86
+ VALUE destination = rb_str_new2(dest->name);
87
+ rb_ary_push(dest_list, destination); // Add this testination name to dest_list string
88
+ }
89
+ return dest_list;
90
+ }
91
+
92
+ /*
93
+ * call-seq:
94
+ * Cups.default_printer -> String or nil
95
+ *
96
+ * Get default printer or class. Returns a string or false if there is no default
97
+ */
98
+ static VALUE cups_get_default(VALUE self)
99
+ {
100
+ const char *default_printer;
101
+ default_printer = cupsGetDefault();
102
+
103
+ if (default_printer != NULL) {
104
+ VALUE def_p = rb_str_new2(default_printer);
105
+ return def_p;
106
+ }
107
+ }
108
+
109
+ /*
110
+ * call-seq:
111
+ * print_job.cancel -> true or false
112
+ *
113
+ * Cancel the current job. Returns true if successful, false otherwise.
114
+ */
115
+ static VALUE cups_cancel(VALUE self)
116
+ {
117
+ VALUE printer, job_id;
118
+ printer = rb_iv_get(self, "@printer");
119
+ job_id = rb_iv_get(self, "@job_id");
120
+
121
+ if (NIL_P(job_id)) {
122
+ return Qfalse; // If @job_id is nil
123
+ } else { // Otherwise attempt to cancel
124
+ int job = NUM2INT(job_id);
125
+ char *target = RSTRING_PTR(printer); // Target printer string
126
+ int cancellation;
127
+ cancellation = cupsCancelJob(target, job);
128
+ return Qtrue;
129
+ }
130
+ }
131
+
132
+ /*
133
+ * call-seq:
134
+ * print_job.failed? -> true or false
135
+ *
136
+ * Did this job fail?
137
+ */
138
+ static VALUE cups_job_failed(VALUE self)
139
+ {
140
+ VALUE job_id = rb_iv_get(self, "@job_id");
141
+
142
+ if (NIL_P(job_id) || !NUM2INT(job_id) == 0) {
143
+ return Qfalse;
144
+ } else {
145
+ return Qtrue;
146
+ }
147
+ }
148
+
149
+ /*
150
+ * call-seq:
151
+ * print_job.error_reason -> String
152
+ *
153
+ * Get the last human-readable error string
154
+ */
155
+ static VALUE cups_get_error_reason(VALUE self)
156
+ {
157
+ VALUE job_id = rb_iv_get(self, "@job_id");
158
+
159
+ if (NIL_P(job_id) || !NUM2INT(job_id) == 0) {
160
+ return Qnil;
161
+ } else {
162
+ VALUE error_exp = rb_str_new2(cupsLastErrorString());
163
+ return error_exp;
164
+ }
165
+ }
166
+
167
+ /*
168
+ * call-seq:
169
+ * print_job.error_code -> Fixnum
170
+ *
171
+ * Get the last IPP error code.
172
+ */
173
+ static VALUE cups_get_error_code(VALUE self)
174
+ {
175
+ VALUE job_id = rb_iv_get(self, "@job_id");
176
+
177
+ if (NIL_P(job_id) || !NUM2INT(job_id) == 0) {
178
+ return Qnil;
179
+ } else {
180
+ VALUE ipp_error_code = INT2NUM(cupsLastError());
181
+ return ipp_error_code;
182
+ }
183
+ }
184
+
185
+ /*
186
+ * call-seq:
187
+ * print_job.state -> String
188
+ *
189
+ * Get human-readable state of current job.
190
+ */
191
+ static VALUE cups_get_job_state(VALUE self)
192
+ {
193
+ VALUE job_id = rb_iv_get(self, "@job_id");
194
+ VALUE printer = rb_iv_get(self, "@printer");
195
+ VALUE jstate;
196
+
197
+ int num_jobs;
198
+ cups_job_t *jobs;
199
+ ipp_jstate_t job_state = IPP_JOB_PENDING;
200
+ int i;
201
+ char *printer_arg = RSTRING_PTR(printer);
202
+
203
+ if (NIL_P(job_id)) {
204
+ return Qnil;
205
+ } else {
206
+ num_jobs = cupsGetJobs(&jobs, printer_arg, 1, -1); // Get jobs
207
+
208
+ for (i = 0; i < num_jobs; i ++) {
209
+ if (jobs[i].id == NUM2INT(job_id)) {
210
+ job_state = jobs[i].state;
211
+ break;
212
+ }
213
+ }
214
+
215
+ // Free job array
216
+ cupsFreeJobs(num_jobs, jobs);
217
+
218
+ switch (job_state) {
219
+ case IPP_JOB_PENDING :
220
+ jstate = rb_str_new2("Pending...");
221
+ break;
222
+ case IPP_JOB_HELD :
223
+ jstate = rb_str_new2("Held");
224
+ break;
225
+ case IPP_JOB_PROCESSING :
226
+ jstate = rb_str_new2("Processing...");
227
+ break;
228
+ case IPP_JOB_STOPPED :
229
+ jstate = rb_str_new2("Stopped");
230
+ break;
231
+ case IPP_JOB_CANCELED :
232
+ jstate = rb_str_new2("Cancelled");
233
+ break;
234
+ case IPP_JOB_ABORTED :
235
+ jstate = rb_str_new2("Aborted");
236
+ break;
237
+ case IPP_JOB_COMPLETED :
238
+ jstate = rb_str_new2("Completed");
239
+ break;
240
+ default:
241
+ jstate = rb_str_new2("Unknown Job Code...");
242
+ }
243
+
244
+ return jstate;
245
+ }
246
+ }
247
+
248
+ /*
249
+ * call-seq:
250
+ * print_job.completed? -> true or false
251
+ *
252
+ * Has the job completed?
253
+ */
254
+ static VALUE cups_job_completed(VALUE self)
255
+ {
256
+ VALUE job_id = rb_iv_get(self, "@job_id");
257
+ VALUE printer = rb_iv_get(self, "@printer");
258
+ VALUE jstate;
259
+
260
+ int num_jobs;
261
+ cups_job_t *jobs;
262
+ ipp_jstate_t job_state = IPP_JOB_PENDING;
263
+ int i;
264
+ char *printer_arg = RSTRING_PTR(printer);
265
+
266
+ if (NIL_P(job_id)) {
267
+ return Qfalse;
268
+ } else {
269
+ num_jobs = cupsGetJobs(&jobs, printer_arg, 1, -1); // Get jobs
270
+ // job_state = IPP_JOB_COMPLETED;
271
+
272
+ for (i = 0; i < num_jobs; i ++) {
273
+ if (jobs[i].id == NUM2INT(job_id)) {
274
+ job_state = jobs[i].state;
275
+ break;
276
+ }
277
+
278
+ // Free job array
279
+ cupsFreeJobs(num_jobs, jobs);
280
+
281
+ if (job_state == IPP_JOB_COMPLETED) {
282
+ return Qtrue;
283
+ } else {
284
+ return Qfalse;
285
+ }
286
+
287
+ }
288
+ }
289
+ }
290
+
291
+ /*
292
+ * call-seq:
293
+ * Cups.all_jobs(printer) -> Hash
294
+ *
295
+ * Get all jobs from default CUPS server. Takes a single printer/class string argument.
296
+ * Returned hash keys are CUPS job ids, and the values are arrays of job info in the
297
+ * following order:
298
+ *
299
+ * [title,submitted_by,size,format,state]
300
+ */
301
+ static VALUE cups_get_jobs(VALUE self, VALUE printer)
302
+ {
303
+ VALUE job_list, job_info_ary, jid, jtitle, juser, jsize, jformat, jstate;
304
+ int job_id;
305
+ int num_jobs;
306
+ cups_job_t *jobs;
307
+ ipp_jstate_t state;
308
+ int i;
309
+ char *printer_arg = RSTRING_PTR(printer);
310
+
311
+ num_jobs = cupsGetJobs(&jobs, printer_arg, 1, -1); // Get jobs
312
+ job_list = rb_hash_new();
313
+
314
+ for (i = 0; i < num_jobs; i ++) { // Construct a hash of individual job info
315
+ job_info_ary = rb_ary_new();
316
+ jid = INT2NUM(jobs[i].id);
317
+ jtitle = rb_str_new2(jobs[i].title);
318
+ juser = rb_str_new2(jobs[i].user);
319
+ jsize = INT2NUM(jobs[i].size);
320
+ jformat = rb_str_new2(jobs[i].format);
321
+
322
+ // Let's elaborate on that job state...
323
+ switch (jobs[i].state) {
324
+ case IPP_JOB_PENDING :
325
+ jstate = rb_str_new2("Pending...");
326
+ break;
327
+ case IPP_JOB_HELD :
328
+ jstate = rb_str_new2("Held");
329
+ break;
330
+ case IPP_JOB_PROCESSING :
331
+ jstate = rb_str_new2("Processing...");
332
+ break;
333
+ case IPP_JOB_STOPPED :
334
+ jstate = rb_str_new2("Stopped");
335
+ break;
336
+ case IPP_JOB_CANCELED :
337
+ jstate = rb_str_new2("Cancelled");
338
+ break;
339
+ case IPP_JOB_ABORTED :
340
+ jstate = rb_str_new2("Aborted");
341
+ break;
342
+ case IPP_JOB_COMPLETED :
343
+ jstate = rb_str_new2("Completed");
344
+ }
345
+
346
+ rb_ary_push(job_info_ary, jtitle);
347
+ rb_ary_push(job_info_ary, juser);
348
+ rb_ary_push(job_info_ary, jsize);
349
+ rb_ary_push(job_info_ary, jformat);
350
+ rb_ary_push(job_info_ary, jstate);
351
+
352
+ rb_hash_aset(job_list, jid, job_info_ary); // And push it all into job_list hash
353
+ }
354
+
355
+ // Free job array
356
+ cupsFreeJobs(num_jobs, jobs);
357
+
358
+ return job_list;
359
+ }
360
+
361
+ /*
362
+ * call-seq:
363
+ * Cups.options_for(name) -> Hash or nil
364
+ *
365
+ * Get all options from CUPS server with name. Returns a hash with key/value pairs
366
+ * based on server options, or nil if no server with name.
367
+ */
368
+ static VALUE cups_get_options(VALUE self, VALUE printer)
369
+ {
370
+ VALUE options_list;
371
+ int i;
372
+ char *printer_arg = RSTRING_PTR(printer);
373
+
374
+ options_list = rb_hash_new();
375
+
376
+ cups_dest_t *dests;
377
+ int num_dests = cupsGetDests(&dests);
378
+ cups_dest_t *dest = cupsGetDest(printer_arg, NULL, num_dests, dests);
379
+
380
+ if (dest == NULL) {
381
+ cupsFreeDests(num_dests, dests);
382
+ return Qnil;
383
+ } else {
384
+ for(i =0; i< dest->num_options; i++) {
385
+ rb_hash_aset(options_list, rb_str_new2(dest->options[i].name), rb_str_new2(dest->options[i].value));
386
+ }
387
+
388
+ cupsFreeDests(num_dests, dests);
389
+ return options_list;
390
+ }
391
+
392
+ }
393
+
394
+ // TODO
395
+ // int ipp_state_to_string(int state)
396
+ // {
397
+ // // char *jstate;
398
+ // switch (state) {
399
+ // case IPP_JOB_PENDING :
400
+ // // jstate = rb_str_new2("Pending...");
401
+ // // char jstate[] = "Pending...";
402
+ // break;
403
+ // case IPP_JOB_HELD :
404
+ // // jstate = rb_str_new2("Held");
405
+ // // char jstate[] = "Held";
406
+ // break;
407
+ // case IPP_JOB_PROCESSING :
408
+ // // jstate = rb_str_new2("Processing...");
409
+ // // char jstate[] = "Processing...";
410
+ // break;
411
+ // case IPP_JOB_STOPPED :
412
+ // // jstate = rb_str_new2("Stopped");
413
+ // // char jstate[] = "Stopped";
414
+ // break;
415
+ // case IPP_JOB_CANCELED :
416
+ // // jstate = rb_str_new2("Cancelled");
417
+ // // char jstate[] = "Cancelled";
418
+ // break;
419
+ // case IPP_JOB_ABORTED :
420
+ // // jstate = rb_str_new2("Aborted");
421
+ // // char jstate[] = "Aborted";
422
+ // break;
423
+ // case IPP_JOB_COMPLETED :
424
+ // // jstate = rb_str_new2("Completed");
425
+ // break;
426
+ // }
427
+ // return 0;
428
+ // }
429
+
430
+ /*
431
+ */
432
+
433
+ void Init_cups() {
434
+ rubyCups = rb_define_module("Cups");
435
+ printJobs = rb_define_class_under(rubyCups, "PrintJob", rb_cObject);
436
+
437
+ // Cups::PrintJob Attributes
438
+ rb_define_attr(printJobs, "printer", 1, 0);
439
+ rb_define_attr(printJobs, "filename", 1, 0);
440
+ rb_define_attr(printJobs, "job_id", 1, 0);
441
+
442
+ // Cups::PrintJob Methods
443
+ rb_define_method(printJobs, "initialize", job_init, -1);
444
+ rb_define_method(printJobs, "print", cups_print, 0);
445
+ rb_define_method(printJobs, "cancel", cups_cancel, 0);
446
+ rb_define_method(printJobs, "state", cups_get_job_state, 0);
447
+ rb_define_method(printJobs, "completed?", cups_job_completed, 0);
448
+ rb_define_method(printJobs, "failed?", cups_job_failed, 0);
449
+ rb_define_method(printJobs, "error_reason", cups_get_error_reason, 0);
450
+ rb_define_method(printJobs, "error_code", cups_get_error_code, 0);
451
+
452
+ // Cups Module Methods
453
+ rb_define_singleton_method(rubyCups, "show_destinations", cups_show_dests, 0);
454
+ rb_define_singleton_method(rubyCups, "default_printer", cups_get_default, 0);
455
+ rb_define_singleton_method(rubyCups, "all_jobs", cups_get_jobs, 1);
456
+ rb_define_singleton_method(rubyCups, "options_for", cups_get_options, 1);
457
+ }
data/ext/extconf.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "mkmf"
2
+
3
+ unless have_library("cups") && find_executable("cups-config")
4
+ puts "Couldn't find CUPS libraries on your system. Check they're installed and in your path."
5
+ exit
6
+ end
7
+
8
+ cups_cflags = `cups-config --cflags`.chomp || ""
9
+ cups_libs = `cups-config --libs`.chomp || ""
10
+
11
+ with_cflags(cups_cflags) {
12
+ with_ldflags(cups_libs) {
13
+ create_makefile("cups")
14
+ }
15
+ }
data/ext/ruby_cups.h ADDED
@@ -0,0 +1,10 @@
1
+ #include <cups/cups.h>
2
+ #ifdef __APPLE__
3
+ #include <ruby/ruby.h>
4
+ #else
5
+ #include <ruby.h>
6
+ #endif
7
+
8
+ #ifndef MAXOPTS
9
+ #define MAXOPTS 100
10
+ #endif
data/test/cups_test.rb ADDED
@@ -0,0 +1,121 @@
1
+ require "cups"
2
+ require "test/unit"
3
+
4
+ # The tests which don't make use of mocking go on the operating assumption that you have
5
+ # the CUPS command line utilities installed and in your $PATH
6
+
7
+ class CupsTest < Test::Unit::TestCase
8
+ def test_same_printers_returned
9
+ lplist = `lpstat -a`.split("\n").map { |pr| pr.split(' ').first }
10
+ cups_destinations = Cups.show_destinations
11
+ assert cups_destinations.is_a?(Array)
12
+ assert_equal cups_destinations, lplist
13
+ end
14
+
15
+ def test_can_instantiate_print_job_object_with_correct_args
16
+ assert_raise(ArgumentError) do
17
+ Cups::PrintJob.new
18
+ end
19
+
20
+ assert_nothing_raised do
21
+ Cups::PrintJob.new("/path", "PDF_Printer")
22
+ Cups::PrintJob.new("/path")
23
+ end
24
+ end
25
+
26
+ def test_job_defaults_to_default
27
+ assert_nothing_raised do
28
+ pj = Cups::PrintJob.new("/non/existent/file")
29
+
30
+ assert_equal Cups.default_printer, pj.instance_variable_get(:@printer)
31
+ end
32
+ end
33
+
34
+ def test_we_cant_print_to_nonexistent_printers
35
+ assert_raise(RuntimeError) do
36
+ pj = Cups::PrintJob.new("/non/existent/file", "bollocks_printer")
37
+
38
+ assert !Cups.show_destinations.include?(pj.instance_variable_get(:@printer))
39
+ end
40
+ end
41
+
42
+ def test_we_cant_print_nonexistent_files
43
+ pj = Cups::PrintJob.new("soft_class")
44
+
45
+ assert_raise(RuntimeError) do
46
+ pj.print
47
+ end
48
+
49
+ assert_nil pj.job_id
50
+ end
51
+
52
+ def test_print_job_cancellation
53
+ pj = Cups::PrintJob.new(sample, "soft_class")
54
+ pj.print
55
+ assert_not_nil pj.job_id
56
+ assert_equal pj.cancel, true
57
+ assert pj.job_id.is_a?(Fixnum)
58
+ end
59
+
60
+ def test_all_jobs_returns_hash
61
+ assert Cups.all_jobs(Cups.default_printer).is_a?(Hash)
62
+ end
63
+
64
+ def test_dest_list_returns_array
65
+ assert Cups.show_destinations.is_a?(Array)
66
+ end
67
+
68
+ def test_dest_options_returns_hash_if_real
69
+ assert Cups.options_for("PDF_Printer").is_a?(Hash)
70
+ end
71
+
72
+ def test_dest_options_returns_nil_if_not_real
73
+ assert_nil Cups.options_for("bollocks_printer")
74
+ end
75
+
76
+ def test_job_failed_boolean
77
+ pj = Cups::PrintJob.new(sample, "soft_class")
78
+ pj.print
79
+ pj.cancel
80
+ assert !pj.failed?
81
+ end
82
+
83
+ def test_returns_failure_string_on_cancellation
84
+ pj = Cups::PrintJob.new(blank_sample, "PDF_Printer")
85
+ pj.print
86
+
87
+ assert pj.job_id == 0 # Failed jobs have an ID of zero
88
+ assert pj.failed?
89
+
90
+ assert pj.error_reason.is_a?(String)
91
+ end
92
+
93
+ def test_job_state_string
94
+ pj = Cups::PrintJob.new(sample, "soft_class")
95
+ assert_nil pj.state # A job can't have a state if it hasn't been assigned a job_id yet
96
+ assert !pj.completed?
97
+
98
+ pj.print
99
+
100
+ pj.cancel
101
+ assert pj.state == "Cancelled"
102
+ assert !pj.completed?
103
+ end
104
+
105
+ def test_print_job_attributes
106
+ pj = Cups::PrintJob.new(sample)
107
+ [:printer, :filename, :job_id].each do |attr|
108
+ assert pj.respond_to?(attr)
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def sample
115
+ "#{Dir.pwd}/sample.txt"
116
+ end
117
+
118
+ def blank_sample
119
+ "#{Dir.pwd}/sample_blank.txt"
120
+ end
121
+ end
data/test/sample.txt ADDED
@@ -0,0 +1,9 @@
1
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris rutrum, nibh ut hendrerit luctus, nisl risus ultrices lorem, sed luctus ipsum velit eget ante. Vestibulum eget est. Sed lectus odio, eleifend non, iaculis in, varius ut, sapien. Suspendisse suscipit urna in sem. Cras in mauris. Sed sapien urna, semper vitae, vulputate a, venenatis et, lacus. Praesent tristique turpis quis quam. Nullam a velit a erat imperdiet luctus. Cras vulputate dignissim leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut nulla lorem, dictum quis, cursus eu, dictum ac, nisi.
2
+
3
+ Pellentesque posuere commodo nunc. Sed eu turpis ac neque cursus aliquet. Nullam semper turpis. In sit amet metus. Nunc vel diam. Praesent sit amet ipsum varius dolor pulvinar pellentesque. Nullam hendrerit. Curabitur condimentum luctus nisi. Morbi eu erat vel eros aliquam sagittis. Sed pellentesque hendrerit augue. Pellentesque facilisis, dui at placerat vulputate, odio metus pulvinar metus, ac porta metus nibh a tortor. Cras lorem dolor, ornare quis, tempor quis, faucibus ut, metus. Donec ac ipsum. Sed pretium.
4
+
5
+ Praesent elementum dapibus sapien. Nam eleifend faucibus felis. Aliquam erat volutpat. In egestas, ipsum non rutrum cursus, ante libero hendrerit enim, ut lobortis nibh ligula sit amet enim. Nullam dui nulla, ullamcorper et, posuere ac, hendrerit vel, ligula. Suspendisse vel urna non nibh ultrices congue. Sed neque. Sed id tellus eget turpis aliquet mollis. Suspendisse sed eros. Vivamus nulla. Maecenas pulvinar. Aliquam sodales blandit mi.
6
+
7
+ Curabitur lacinia. Mauris vehicula enim at odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque augue sapien, faucibus et, pulvinar vel, tempor eget, elit. Cras a leo vitae justo aliquam laoreet. In et orci sed leo scelerisque condimentum. Integer scelerisque condimentum nisl. Aenean sit amet ligula. In fringilla. Nam elementum diam in dui.
8
+
9
+ Maecenas aliquam volutpat lectus. Vivamus tellus purus, scelerisque ac, suscipit sed, rhoncus id, justo. Quisque dolor sapien, accumsan vitae, dignissim sed, pulvinar nec, ligula. Cras ligula quam, iaculis ut, ultrices id, varius nec, neque. Mauris ornare nisl ac nunc. Maecenas interdum nisl et quam. Etiam purus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam sodales odio. Cras eu ipsum. Donec fringilla odio. Etiam vitae felis vitae lectus porttitor molestie. Nunc consectetur enim at elit tincidunt tempor. Ut fringilla, tortor in rutrum egestas, magna ante suscipit nunc, sit amet sagittis justo quam quis est. Nulla sit amet tortor nec ligula ultricies placerat. Cras congue quam in magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce a ante. Etiam congue nunc quis ligula. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
File without changes
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cups
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Chris Mowforth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-22 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: " Ruby CUPS provides a wrapper for the Common UNIX Printing System, allowing rubyists to perform basic tasks like printer discovery, job submission & querying.\n"
17
+ email:
18
+ - chris@mowforth.com
19
+ executables: []
20
+
21
+ extensions:
22
+ - ext/extconf.rb
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - test/cups_test.rb
27
+ - test/sample.txt
28
+ - test/sample_blank.txt
29
+ - ext/cups.c
30
+ - ext/ruby_cups.h
31
+ - ext/extconf.rb
32
+ has_rdoc: true
33
+ homepage: http://cups.rubyforge.org/
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project: cups
56
+ rubygems_version: 1.3.3
57
+ signing_key:
58
+ specification_version: 3
59
+ summary: A lightweight Ruby library for printing.
60
+ test_files: []
61
+