rbpod 0.0.5 → 0.0.6

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.
data/README.md CHANGED
@@ -104,10 +104,21 @@ playlist.podcast? # => false
104
104
 
105
105
  playlist.tracks # => #<RbPod::TrackCollection:0xdeadbeef>
106
106
  ```
107
+ ### RbPod::TrackCollection
107
108
 
108
- ### RbPod::Track
109
+ All playlists contain a `tracks` method which returns an `RbPod::TrackCollection`:
110
+
111
+ ```ruby
112
+ tracks = database.playlists.master.tracks
113
+
114
+ tracks.playlist # => #<RbPod::Playlist:0xdeadbeef>
115
+ tracks.length # => 400
109
116
 
110
- Tracks also can do a lot, but not complete:
117
+ tracks.first # => #<RbPod::Track:0xdeadbeef>
118
+ tracks[0] # => #<RbPod::Track:0xdeadbeef>
119
+ ```
120
+
121
+ ### RbPod::Track
111
122
 
112
123
  ```ruby
113
124
  track = database.playlists.master.tracks.first
@@ -1,7 +1,6 @@
1
1
  /* collection.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "collection.h"
5
4
 
6
5
  /*
7
6
  * call-seq:
data/ext/rbpod/database.c CHANGED
@@ -1,12 +1,6 @@
1
1
  /* database.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "error.h"
5
- #include "device.h"
6
- #include "database.h"
7
- #include "playlist.h"
8
- #include "track_collection.h"
9
- #include "playlist_collection.h"
10
4
 
11
5
  /*
12
6
  * call-seq:
@@ -71,7 +65,9 @@ static VALUE rbpod_database_tracks_get(VALUE self)
71
65
  */
72
66
  static VALUE rbpod_database_device_get(VALUE self)
73
67
  {
74
- return rb_class_new_instance(1, &self, cRbPodDevice);
68
+ Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
69
+ VALUE mount_point = rb_str_new2(itdb_get_mountpoint(database));
70
+ return rb_class_new_instance(1, &mount_point, cRbPodDevice);
75
71
  }
76
72
 
77
73
  /*
@@ -203,34 +199,35 @@ static VALUE rbpod_database_create(int argc, VALUE *argv, VALUE self)
203
199
  return Qnil;
204
200
  }
205
201
 
206
- /* If we didn't specify a device name, default to 'iPod'. */
207
- if (RTEST(device_name) == FALSE) {
208
- device_name = rb_str_new2("iPod");
209
- }
202
+ /* If a device name was given, ensure it's a string. */
203
+ if (RTEST(device_name)) {
204
+ Check_Type(device_name, T_STRING);
210
205
 
211
- /* Ensure it's a valid device name, if it was given. */
212
- if (TYPE(device_name) == T_STRING && RSTRING_LEN(device_name) == 0) {
213
- rb_raise(eRbPodError, "Device name must not be an empty string.");
214
- return Qnil;
206
+ if (RSTRING_LEN(device_name) < 4) {
207
+ rb_raise(eRbPodError, "Device name must be a string of at least four characters.");
208
+ return Qnil;
209
+ }
215
210
  }
216
211
 
217
212
  /* If a model number was given, ensure it's a string. */
218
213
  if (RTEST(model_number)) {
219
- Check_Type(device_name, T_STRING);
214
+ Check_Type(model_number, T_STRING);
220
215
 
221
- model_matcher = rb_str_new2("/x?[A-Z][0-9]{3}/");
216
+ model_matcher = rb_str_new2("/[xM]?[A-Z][0-9]{3}/");
222
217
  ignorecase = rb_const_get(rb_cRegexp, rb_intern("IGNORECASE"));
223
218
  model_regexp = rb_reg_new_str(model_matcher, ignorecase);
224
219
 
225
220
  if (RTEST(rb_reg_match(model_regexp, model_number)) == FALSE) {
226
- rb_raise(eRbPodError, "Model number must be a string matching: /x?[A-Z][0-9]{3}/i");
221
+ rb_raise(eRbPodError, "Model number must be a string matching: /[xM]?[A-Z][0-9]{3}/i");
227
222
  return Qnil;
228
223
  }
229
224
  }
230
225
 
231
226
  /* Extract pointers for glib use. */
232
227
  _mount_point = StringValueCStr(mount_point);
233
- _device_name = StringValueCStr(device_name);
228
+
229
+ /* GPod will use 'iPod' as the device name if it wasn't specified. */
230
+ _device_name = !NIL_P(device_name) ? StringValueCStr(device_name) : NULL;
234
231
 
235
232
  /* GPod can function with a NULL model number, however, artwork may not function properly. */
236
233
  _model_number = !NIL_P(model_number) ? StringValueCStr(model_number) : NULL;
data/ext/rbpod/device.c CHANGED
@@ -1,9 +1,6 @@
1
1
  /* device.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "error.h"
5
- #include "device.h"
6
- #include "database.h"
7
4
 
8
5
  /*
9
6
  * call-seq:
@@ -189,32 +186,38 @@ static VALUE rbpod_device_sysinfo_save(VALUE self)
189
186
 
190
187
  /*
191
188
  * call-seq:
192
- * initialize(database) -> RbPod::Device
189
+ * initialize(mount_point) -> RbPod::Device
193
190
  *
194
- * Creates an RbPod::Device for a given RbPod::Database. You shouldn't have to call this.
191
+ * Creates an RbPod::Device mapped to a given device mount point.
195
192
  */
196
- static VALUE rbpod_device_initialize(VALUE self, VALUE database)
193
+ static VALUE rbpod_device_initialize(VALUE self, VALUE mount_point)
197
194
  {
198
- Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
195
+ Itdb_Device *device = TYPED_DATA_PTR(self, Itdb_Device);
199
196
 
200
- /* Make sure we were given an instance of a RbPod::Database. */
201
- if (rb_obj_is_instance_of(database, cRbPodDatabase) == FALSE) {
202
- rb_raise(eRbPodError, "Given database must be an instance of RbPod::Database: %s", StringValueCStr(database));
197
+ /* Check if the mount point is a directory. */
198
+ if (rb_file_directory_p(rb_cFile, mount_point) != Qtrue) {
199
+ rb_raise(eRbPodError, "The mount point must be a directory!");
203
200
  return Qnil;
204
201
  }
205
202
 
206
- /* The database pointer and it's device should both be non-NULL. */
207
- if (_database == NULL || _database->device == NULL) {
208
- rb_raise(eRbPodError, "Given database and/or device was NULL.");
209
- return Qnil;
210
- }
203
+ itdb_device_set_mountpoint(device, StringValueCStr(mount_point));
211
204
 
212
- /* Attach our data pointer to this database's backing device. */
213
- DATA_PTR(self) = _database->device;
205
+ DATA_PTR(self) = device;
214
206
 
215
207
  return self;
216
208
  }
217
209
 
210
+ static void rbpod_device_deallocate(void *handle)
211
+ {
212
+ itdb_device_free((Itdb_Device *) handle);
213
+ }
214
+
215
+ static VALUE rbpod_device_allocate(VALUE self)
216
+ {
217
+ Itdb_Device *device = itdb_device_new();
218
+ return Data_Wrap_Struct(cRbPodDevice, NULL, rbpod_device_deallocate, (void *) device);
219
+ }
220
+
218
221
  void Init_rbpod_device(void)
219
222
  {
220
223
  #if RDOC_CAN_PARSE_DOCUMENTATION
@@ -222,6 +225,8 @@ void Init_rbpod_device(void)
222
225
  #endif
223
226
  cRbPodDevice = rb_define_class_under(mRbPod, "Device", rb_cObject);
224
227
 
228
+ rb_define_alloc_func(cRbPodDevice, rbpod_device_allocate);
229
+
225
230
  rb_define_method(cRbPodDevice, "initialize", rbpod_device_initialize, 1);
226
231
 
227
232
  rb_define_method(cRbPodDevice, "[]", rbpod_device_sysinfo_get, 1);
data/ext/rbpod/error.c CHANGED
@@ -1,7 +1,6 @@
1
1
  /* error.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "error.h"
5
4
 
6
5
  inline VALUE rbpod_raise_error(GError *error)
7
6
  {
data/ext/rbpod/extconf.rb CHANGED
@@ -1,10 +1,15 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  require 'mkmf'
4
2
 
3
+ # Fail immediately if we don't have libgpod-1.0 installed here.
5
4
  abort "Please install libgpod-1.0" unless pkg_config('libgpod-1.0')
6
5
 
7
6
  # Unfortunately, rdoc isn't capable of a lot of things.
8
7
  $defs.push("-DRDOC_CAN_PARSE_DOCUMENTATION=0")
9
8
 
9
+ # Provide HAVE_STDDEF_H to the pre-processor.
10
+ have_header('stddef.h')
11
+
12
+ # Provide HAVE_GPOD_ITDB_H to the pre-processor.
13
+ have_header('gpod/itdb.h')
14
+
10
15
  create_makefile('rbpod/rbpod')
@@ -0,0 +1,99 @@
1
+ /* macros.h */
2
+
3
+ #ifndef RBPOD_MACROS_H
4
+ #define RBPOD_MACROS_H
5
+
6
+ #define BooleanValue(value) (value) ? Qtrue : Qfalse
7
+
8
+ #define TYPED_DATA_PTR(self, type) ((type *) DATA_PTR(self))
9
+
10
+ #define DEF_ATTR_READER_STRING(c_type, prefix, field) \
11
+ static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
12
+ { \
13
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
14
+ return (ptr->field == NULL) ? Qnil : rb_str_new2(ptr->field); \
15
+ }
16
+
17
+ #define DEF_ATTR_WRITER_STRING(c_type, prefix, field) \
18
+ static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
19
+ { \
20
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
21
+ gchar *previous = (gchar *) ptr->field; \
22
+ ptr->field = StringValueCStr(value); \
23
+ if (previous != NULL) { \
24
+ g_free(previous); \
25
+ } \
26
+ return Qnil; \
27
+ }
28
+
29
+ #define DEF_ATTR_READER_FIXNUM(c_type, prefix, field) \
30
+ static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
31
+ { \
32
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
33
+ return INT2NUM(ptr->field); \
34
+ }
35
+
36
+ #define DEF_ATTR_WRITER_FIXNUM(c_type, prefix, field) \
37
+ static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
38
+ { \
39
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
40
+ ptr->field = NUM2INT(value); \
41
+ return Qnil; \
42
+ }
43
+
44
+ #define DEF_ATTR_READER_BOOLEAN(c_type, prefix, field) \
45
+ static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
46
+ { \
47
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
48
+ return (ptr->field == 0) ? Qfalse : Qtrue; \
49
+ }
50
+
51
+ #define DEF_ATTR_WRITER_BOOLEAN(c_type, prefix, field) \
52
+ static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
53
+ { \
54
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
55
+ ptr->field = (RTEST(value)) ? 1 : 0; \
56
+ return Qnil; \
57
+ }
58
+
59
+ #define DEF_ATTR_READER_DATETIME(c_type, prefix, field) \
60
+ static VALUE rbpod_##prefix##_##field##_get(VALUE self) \
61
+ { \
62
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
63
+ VALUE timestamp = INT2NUM(ptr->field); \
64
+ if (ptr->field == 0) { \
65
+ return Qnil; \
66
+ } \
67
+ return rb_funcall(rb_cTime, rb_intern("at"), 1, timestamp); \
68
+ }
69
+
70
+ #define DEF_ATTR_WRITER_DATETIME(c_type, prefix, field) \
71
+ static VALUE rbpod_##prefix##_##field##_set(VALUE self, VALUE value) \
72
+ { \
73
+ c_type *ptr = TYPED_DATA_PTR(self, c_type); \
74
+ ptr->field = rb_funcall(value, rb_intern("to_i"), 0); \
75
+ return Qnil; \
76
+ }
77
+
78
+ #define DEF_ATTR_ACCESSOR(c_type, prefix, ruby_type, field) \
79
+ DEF_ATTR_READER_##ruby_type(c_type, prefix, field); \
80
+ DEF_ATTR_WRITER_##ruby_type(c_type, prefix, field)
81
+
82
+ #define DCL_ATTR_WRITER(ruby_class, prefix, name, field) \
83
+ rb_define_method(ruby_class, #name "=", rbpod_##prefix##_##field##_set, 1)
84
+
85
+ #define DCL_ATTR_PREDICATE(ruby_class, prefix, name, field) \
86
+ rb_define_method(ruby_class, #name "?", rbpod_##prefix##_##field##_get, 0)
87
+
88
+ #define DCL_ATTR_READER(ruby_class, prefix, name, field) \
89
+ rb_define_method(ruby_class, #name, rbpod_##prefix##_##field##_get, 0)
90
+
91
+ #define DCL_ATTR_P(ruby_class, prefix, name, field) \
92
+ DCL_ATTR_PREDICATE(ruby_class, prefix, name, field); \
93
+ DCL_ATTR_WRITER(ruby_class, prefix, name, field)
94
+
95
+ #define DCL_ATTR(ruby_class, prefix, name, field) \
96
+ DCL_ATTR_READER(ruby_class, prefix, name, field); \
97
+ DCL_ATTR_WRITER(ruby_class, prefix, name, field)
98
+
99
+ #endif /* RBPOD_MACROS_H */
data/ext/rbpod/playlist.c CHANGED
@@ -1,8 +1,6 @@
1
1
  /* playlist.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "playlist.h"
5
- #include "track_collection.h"
6
4
 
7
5
  /*
8
6
  * call-seq:
@@ -42,7 +40,21 @@ static VALUE rbpod_playlist_smart_p(VALUE self)
42
40
 
43
41
  /*
44
42
  * call-seq:
45
- * timestamp() -> Time
43
+ * smart=(flag) -> nil
44
+ *
45
+ * Set this playlist to a smart playlist.
46
+ */
47
+ static VALUE rbpod_playlist_smart_set(VALUE self, VALUE flag)
48
+ {
49
+ Itdb_Playlist *playlist = TYPED_DATA_PTR(self, Itdb_Playlist);
50
+
51
+ playlist->is_spl = (RTEST(flag)) ? TRUE : FALSE;
52
+
53
+ return Qnil;
54
+ }
55
+
56
+ /*
57
+ * call-seq:
46
58
  * created_on() -> Time
47
59
  *
48
60
  * Returns a Time object representing the time that this playlist was created.
@@ -89,6 +101,27 @@ static VALUE rbpod_playlist_length_get(VALUE self)
89
101
  return INT2NUM(itdb_playlist_tracks_number(playlist));
90
102
  }
91
103
 
104
+ /*
105
+ * call-seq:
106
+ * name=(string) -> nil
107
+ *
108
+ * Sets the name of this playlist.
109
+ */
110
+ static VALUE rbpod_playlist_name_set(VALUE self, VALUE name)
111
+ {
112
+ Itdb_Playlist *playlist = TYPED_DATA_PTR(self, Itdb_Playlist);
113
+ gchar *new_name = StringValueCStr(name);
114
+ gchar *old_name = playlist->name;
115
+
116
+ /* Free up the old name. */
117
+ g_free(old_name);
118
+
119
+ /* Attach the new name. */
120
+ playlist->name = new_name;
121
+
122
+ return Qnil;
123
+ }
124
+
92
125
  /*
93
126
  * call-seq:
94
127
  * name() -> String
@@ -116,24 +149,23 @@ static VALUE rbpod_playlist_compare(VALUE self, VALUE other)
116
149
  return Qnil;
117
150
  }
118
151
 
152
+ /* If this is the master playlist, it always comes first. */
153
+ if (rbpod_playlist_master_p(self) == Qtrue) {
154
+ return NUM2INT(-1);
155
+ }
156
+
157
+ /* If we're comparing against the master playlist, it comes first. */
158
+ if (rbpod_playlist_master_p(other) == Qtrue) {
159
+ return NUM2INT(1);
160
+ }
161
+
119
162
  this_playlist_name = rbpod_playlist_name_get(self);
120
163
  other_playlist_name = rbpod_playlist_name_get(other);
121
164
 
165
+ /* Otherwise, compare by playlist name. */
122
166
  return rb_str_cmp(this_playlist_name, other_playlist_name);
123
167
  }
124
168
 
125
- /*
126
- * call-seq:
127
- * id() -> Fixnum
128
- *
129
- * Returns the internal ID number for this playlist. Do not use this method.
130
- */
131
- static VALUE rbpod_playlist_id_get(VALUE self)
132
- {
133
- Itdb_Playlist *playlist = TYPED_DATA_PTR(self, Itdb_Playlist);
134
- return INT2NUM(playlist->id);
135
- }
136
-
137
169
  /*
138
170
  * call-seq:
139
171
  * initialize() -> RbPod::Playlist
@@ -175,23 +207,22 @@ void Init_rbpod_playlist(void)
175
207
 
176
208
  rb_define_method(cRbPodPlaylist, "initialize", rbpod_playlist_initialize, 0);
177
209
 
178
- rb_define_private_method(cRbPodPlaylist, "id", rbpod_playlist_id_get, 0);
179
-
180
210
  rb_define_method(cRbPodPlaylist, "<=>", rbpod_playlist_compare, 1);
181
211
 
182
212
  rb_define_method(cRbPodPlaylist, "name", rbpod_playlist_name_get, 0);
183
213
  rb_define_method(cRbPodPlaylist, "length", rbpod_playlist_length_get, 0);
184
214
  rb_define_method(cRbPodPlaylist, "tracks", rbpod_playlist_tracks_get, 0);
185
- rb_define_method(cRbPodPlaylist, "shuffle!", rbpod_playlist_shuffle_bang, 0);
186
- rb_define_method(cRbPodPlaylist, "timestamp", rbpod_playlist_timestamp_get, 0);
215
+ rb_define_method(cRbPodPlaylist, "created_on", rbpod_playlist_timestamp_get, 0);
187
216
 
188
- /* :nodoc */
189
- rb_define_alias(cRbPodPlaylist, "created_on", "timestamp");
217
+ rb_define_method(cRbPodPlaylist, "name=", rbpod_playlist_name_set, 1);
218
+ rb_define_method(cRbPodPlaylist, "smart=", rbpod_playlist_smart_set, 1);
190
219
 
191
220
  rb_define_method(cRbPodPlaylist, "smart?", rbpod_playlist_smart_p, 0);
192
221
  rb_define_method(cRbPodPlaylist, "master?", rbpod_playlist_master_p, 0);
193
222
  rb_define_method(cRbPodPlaylist, "podcast?", rbpod_playlist_podcast_p, 0);
194
223
 
224
+ rb_define_method(cRbPodPlaylist, "shuffle!", rbpod_playlist_shuffle_bang, 0);
225
+
195
226
  return;
196
227
  }
197
228
 
@@ -1,10 +1,6 @@
1
1
  /* playlist_collection.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "database.h"
5
- #include "playlist.h"
6
- #include "collection.h"
7
- #include "playlist_collection.h"
8
4
 
9
5
  /*
10
6
  * call-seq:
@@ -19,24 +15,106 @@ static VALUE rbpod_playlist_collection_database(VALUE self)
19
15
 
20
16
  /*
21
17
  * call-seq:
22
- * include?(playlist) -> Boolean
18
+ * insert(playlist, position = -1) -> nil
19
+ *
20
+ * Adds the given RbPod::Playlist +playlist+ to the database.
21
+ */
22
+ static VALUE rbpod_playlist_collection_insert(int argc, VALUE *argv, VALUE self)
23
+ {
24
+ VALUE database = rbpod_playlist_collection_database(self);
25
+ VALUE playlist = Qnil, position = Qnil;
26
+ Itdb_iTunesDB *_database = NULL;
27
+ Itdb_Playlist *_playlist = NULL;
28
+ gint32 _position = 0;
29
+
30
+ if (rb_scan_args(argc, argv, "11", &playlist, &position) < 1) {
31
+ rb_raise(eRbPodError, "Invalid arguments: Expected >= 1 argument!");
32
+ return Qnil;
33
+ }
34
+
35
+ _database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
36
+ _playlist = TYPED_DATA_PTR(playlist, Itdb_Playlist);
37
+ _position = NIL_P(position) ? -1 : NUM2INT(position);
38
+
39
+ /* We we given a playlist? */
40
+ if (rb_obj_is_instance_of(playlist, cRbPodPlaylist) == FALSE) {
41
+ rb_raise(eRbPodError, "Invalid argument: expected RbPod::Playlist, got %s", StringValueCStr(playlist));
42
+ return Qnil;
43
+ }
44
+
45
+ /* Does this playlist already exist in the database? */
46
+ if (itdb_playlist_exists(_database, _playlist) == TRUE) {
47
+ rb_raise(eRbPodError, "Invalid argument: this playlist already exists in the database!");
48
+ return Qnil;
49
+ }
50
+
51
+ itdb_playlist_add(_database, _playlist, _position);
52
+
53
+ return Qnil;
54
+ }
55
+
56
+ /*
57
+ * call-seq:
58
+ * remove(playlist) -> nil
59
+ *
60
+ * Removes the given RbPod::Playlist +playlist+ from the database.
61
+ */
62
+ static VALUE rbpod_playlist_collection_remove(VALUE self, VALUE playlist)
63
+ {
64
+ VALUE database = rbpod_playlist_collection_database(self);
65
+ Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
66
+ Itdb_Playlist *_playlist = TYPED_DATA_PTR(playlist, Itdb_Playlist);
67
+
68
+ /* We we given a playlist? */
69
+ if (rb_obj_is_instance_of(playlist, cRbPodPlaylist) == FALSE) {
70
+ rb_raise(eRbPodError, "Invalid argument: Expected RbPod::Playlist, got %s", StringValueCStr(playlist));
71
+ return Qnil;
72
+ }
73
+
74
+ /* Does this playlist already exist in the database? */
75
+ if (itdb_playlist_exists(_database, _playlist) == FALSE) {
76
+ rb_raise(eRbPodError, "Invalid argument: This playlist does not exist in the database!");
77
+ return Qnil;
78
+ }
79
+
80
+ itdb_playlist_remove(_playlist);
81
+
82
+ return Qnil;
83
+ }
84
+
85
+ /*
86
+ * call-seq:
87
+ * include?(playlist) -> boolean
23
88
  *
24
89
  * Returns true or false if the given RbPod::Playlist +playlist+ exists within the database.
25
90
  */
26
- static VALUE rbpod_playlist_collection_include_p(VALUE self, VALUE playlist)
91
+ static VALUE rbpod_playlist_collection_include(VALUE self, VALUE playlist)
27
92
  {
28
93
  VALUE database = rbpod_playlist_collection_database(self);
29
94
  Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
30
95
  Itdb_Playlist *_playlist = TYPED_DATA_PTR(playlist, Itdb_Playlist);
31
96
 
32
97
  if (rb_obj_is_instance_of(playlist, cRbPodPlaylist) == FALSE) {
33
- rb_raise(eRbPodError, "Invalid argument: expected RbPod::Playlist, got %s", StringValueCStr(playlist));
98
+ rb_raise(eRbPodError, "Invalid argument: Expected RbPod::Playlist, got %s", StringValueCStr(playlist));
34
99
  return Qfalse;
35
100
  }
36
101
 
37
102
  return BooleanValue(itdb_playlist_exists(_database, _playlist));
38
103
  }
39
104
 
105
+ /*
106
+ * call-seq:
107
+ * size() -> integer
108
+ *
109
+ * Returns the total number of playlists in this database.
110
+ */
111
+ static VALUE rbpod_playlist_collection_size(VALUE self)
112
+ {
113
+ VALUE database = rbpod_playlist_collection_database(self);
114
+ Itdb_iTunesDB *_database = TYPED_DATA_PTR(database, Itdb_iTunesDB);
115
+ return INT2NUM(itdb_playlists_number(_database));
116
+ }
117
+
40
118
  /*
41
119
  * call-seq:
42
120
  * master() -> RbPod::Playlist
@@ -122,11 +200,19 @@ void Init_rbpod_playlist_collection(void)
122
200
 
123
201
  rb_define_method(cRbPodPlaylistCollection, "database", rbpod_playlist_collection_database, 0);
124
202
 
125
- rb_define_method(cRbPodPlaylistCollection, "include?", rbpod_playlist_collection_include_p, 1);
203
+ rb_define_method(cRbPodPlaylistCollection, "include?", rbpod_playlist_collection_include, 1);
126
204
 
127
205
  rb_define_method(cRbPodPlaylistCollection, "master", rbpod_playlist_collection_master, 0);
128
206
  rb_define_method(cRbPodPlaylistCollection, "podcast", rbpod_playlist_collection_podcast, 0);
129
207
 
208
+ rb_define_method(cRbPodPlaylistCollection, "size", rbpod_playlist_collection_size, 0);
209
+
210
+ rb_define_method(cRbPodPlaylistCollection, "insert", rbpod_playlist_collection_insert, -1);
211
+ rb_define_method(cRbPodPlaylistCollection, "remove", rbpod_playlist_collection_remove, 1);
212
+
213
+ /* Syntactic sugar for adding a playlist. */
214
+ rb_define_alias(cRbPodPlaylistCollection, "<<", "insert");
215
+
130
216
  return;
131
217
  }
132
218
 
data/ext/rbpod/rbpod.c CHANGED
@@ -1,14 +1,6 @@
1
1
  /* rbpod.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "error.h"
5
- #include "track.h"
6
- #include "device.h"
7
- #include "playlist.h"
8
- #include "database.h"
9
- #include "collection.h"
10
- #include "track_collection.h"
11
- #include "playlist_collection.h"
12
4
 
13
5
  VALUE mRbPod;
14
6
  VALUE eRbPodError;
@@ -38,32 +30,6 @@ static VALUE rbpod_load_database(VALUE self, VALUE mount_point)
38
30
  return rb_class_new_instance(1, &mount_point, cRbPodDatabase);
39
31
  }
40
32
 
41
- /*
42
- * This is a hack used to inject a pointer from the data of one class instance into another.
43
- */
44
- inline VALUE rb_class_new_instance_with_data(int argc, VALUE *argv, VALUE class, void *handle)
45
- {
46
- /* Create a new instance of this class. */
47
- VALUE instance = rb_class_new_instance(argc, argv, class);
48
-
49
- /* Assign it's DATA pointer to the given handle. */
50
- DATA_PTR(instance) = handle;
51
-
52
- return instance;
53
- }
54
-
55
- /*
56
- * This is a hack so that including a module will trigger the +included+ singleton method.
57
- */
58
- inline void rb_real_include_module(VALUE klass, VALUE module)
59
- {
60
- ID included = rb_intern("included");
61
-
62
- rb_include_module(klass, module);
63
-
64
- rb_funcall(mRbPodCollection, included, 1, klass);
65
- }
66
-
67
33
  void Init_rbpod(void)
68
34
  {
69
35
  /* This is the wrapper for all RbPod related classes. */
data/ext/rbpod/rbpod.h CHANGED
@@ -4,19 +4,45 @@
4
4
  #define RBPOD_H
5
5
 
6
6
  #include <ruby.h>
7
- #include <gpod/itdb.h>
8
-
9
- #define BooleanValue(value) (value) ? Qtrue : Qfalse
10
7
 
11
- #define TYPED_DATA_PTR(self, type) ((type *) DATA_PTR(self))
8
+ #ifdef HAVE_STDDEF_H
9
+ #include <stddef.h>
10
+ #endif
12
11
 
13
- RUBY_EXTERN VALUE mRbPod;
14
- RUBY_EXTERN VALUE eRbPodError;
12
+ #ifdef HAVE_GPOD_ITDB_H
13
+ #include <gpod/itdb.h>
14
+ #endif
15
+
16
+ /* Pre-processor macros. */
17
+ #include "macros.h"
18
+
19
+ /* Initialization function prototypes. */
20
+ RUBY_EXTERN void Init_rbpod(void); /* rbpod.c */
21
+ RUBY_EXTERN void Init_rbpod_error(void); /* error.c */
22
+ RUBY_EXTERN void Init_rbpod_database(void); /* database.c */
23
+ RUBY_EXTERN void Init_rbpod_device(void); /* device.c */
24
+ RUBY_EXTERN void Init_rbpod_track(void); /* track.c */
25
+ RUBY_EXTERN void Init_rbpod_playlist(void); /* playlist.c */
26
+ RUBY_EXTERN void Init_rbpod_collection(void); /* collection.c */
27
+ RUBY_EXTERN void Init_rbpod_track_collection(void); /* track_collection.c */
28
+ RUBY_EXTERN void Init_rbpod_playlist_collection(void); /* playlist_collection.c */
29
+
30
+ /* Helper function prototypes. */
31
+ RUBY_EXTERN inline VALUE rbpod_raise_error(GError *error);
32
+ RUBY_EXTERN inline VALUE rb_class_new_instance_with_data(int argc, VALUE *argv, VALUE class, void *handle);
33
+ RUBY_EXTERN inline void rb_real_include_module(VALUE klass, VALUE module);
34
+
35
+ /* Global variables. */
36
+ RUBY_EXTERN VALUE mRbPod; /* rbpod.c */
37
+ RUBY_EXTERN VALUE eRbPodError; /* error.c */
38
+ RUBY_EXTERN VALUE cRbPodDatabase; /* database.c */
39
+ RUBY_EXTERN VALUE cRbPodDevice; /* device.c */
40
+ RUBY_EXTERN VALUE cRbPodTrack; /* track.c */
41
+ RUBY_EXTERN VALUE cRbPodPlaylist; /* playlist.c */
42
+ RUBY_EXTERN VALUE mRbPodCollection; /* collection.c */
43
+ RUBY_EXTERN VALUE cRbPodTrackCollection; /* track_collection.c */
44
+ RUBY_EXTERN VALUE cRbPodPlaylistCollection; /* playlist_collection.c */
15
45
 
16
46
  RUBY_EXTERN VALUE rb_cPathname;
17
47
 
18
- inline VALUE rb_class_new_instance_with_data(int argc, VALUE *argv, VALUE class, void *handle);
19
-
20
- inline void rb_real_include_module(VALUE klass, VALUE module);
21
-
22
48
  #endif /* RBPOD_H */