rbpod 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
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 */