rbpod 0.0.2 → 0.0.3

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/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/README.md CHANGED
@@ -18,47 +18,93 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- Functional for most read-only purposes.
21
+ Functional for most read-only purposes. To get started, add `require 'rbpod'` to your script.
22
22
 
23
- #!/usr/bin/env ruby
23
+ ### RbPod::Database
24
24
 
25
- require 'rbpod'
25
+ To load a database from the filesystem:
26
26
 
27
- # Explicitly load a database from the filesystem -- can be a mount point to an iPod.
28
- database = RbPod::Database.new('/mnt/ipod/') # => #<RbPod::Database:0x8733fa4>
27
+ database = RbPod::Database.new('/mnt/ipod/') # => #<RbPod::Database:0xdeadbeef>
29
28
 
30
29
  database.version # => 42
31
30
  database.mountpoint # => "/mnt/ipod/"
32
31
  database.filename # => "/mnt/ipod/iPod_Control/iTunes/iTunesDB"
33
32
  database.synchronized? # => true
34
33
 
35
- # Device information, including model name, number, and capacity.
36
- database.device.capacity # => 4.0
37
- database.device.generation # => "Nano Video (3rd Gen.)"
38
- database.device.model_name # => "Nano (Silver)"
39
- database.device.model_number # => "A978"
40
-
41
- # Device feature support detection.
42
- database.device.supports_photos? # => true
43
- database.device.supports_videos? # => true
44
- database.device.supports_artwork? # => true
45
- database.device.supports_podcasts? # => true
46
- database.device.supports_chapter_images? # => true
47
-
48
- # Device SysInfo read/write.
49
- database.device['ModelNumStr'] # => "xA978"
50
- database.device['PotsdamConf45'] = "Kilroy Was Here"
51
- database.device.save!
52
-
53
- # Playlists (includes enumerable)
34
+ If you'd like to create a blank database, you can do that too:
35
+
36
+ database = RbPod::Database.create!('/tmp/ipod-blank') # => #<RbPod::Database:0xdeadbeef>
37
+
38
+ ### RbPod::Device
39
+
40
+ The device (if any) that backs the database can be interrogated:
41
+
42
+ device = database.device # => #<RbPod::Device:0xdeadbeef>
43
+
44
+ # Model name, number, and capacity.
45
+ device.capacity # => 4.0
46
+ device.generation # => "Nano Video (3rd Gen.)"
47
+ device.model_name # => "Nano (Silver)"
48
+ device.model_number # => "A978"
49
+
50
+ # Feature support.
51
+ device.supports_photos? # => true
52
+ device.supports_videos? # => true
53
+ device.supports_artwork? # => true
54
+ device.supports_podcasts? # => true
55
+ device.supports_chapter_images? # => true
56
+
57
+ # Reading/writing SysInfo. Set a key to nil to erase it.
58
+ device['ModelNumStr'] # => "xA978"
59
+ device['PotsdamConf45'] = "Kilroy Was Here"
60
+ device.save!
61
+
62
+ ### RbPod::Collection
63
+
64
+ All methods that return lists return a `Collection` enumerator decorated depending on it's contents:
65
+
66
+ database.playlists # => #<RbPod::Collection:0xdeadbeef>
67
+
68
+ # For a list of all the names of playlists on the iPod:
54
69
  database.playlists.map(&:name) # => ["iPod", "Podcasts", "Recently Added"]
55
70
 
56
- # Track Listing (also enumerable)
57
- database.tracks.length # => 400
58
- database.tracks.first.artist # => "Steppenwolf"
59
- database.tracks.first.title # => "Born To Be Wild"
71
+ # For direct access to the master playlist:
72
+ database.playlists.master # => #<RbPod::Playlist:0xdeadbeef>
73
+
74
+ # For direct access to the podcasts playlist:
75
+ database.playlists.podcasts # => #<RbPod::Playlist:0xdeadbeef>
76
+
77
+ ### RbPod::Playlist
78
+
79
+ All playlists support a variety of methods:
80
+
81
+ playlist = database.playlists.master
82
+
83
+ playlist.name # => "iPod"
84
+ playlist.length # => 400
85
+ playlist.created_on # => 2008-04-05 08:47:46 -0700
86
+
87
+ playlist.master_playlist? # => true
88
+ playlist.smart_playlist? # => false
89
+ playlist.podcast_playlist? # => false
90
+
91
+ playlist.tracks # => #<RbPod::Collection:0xdeadbeef>
92
+
93
+ ### RbPod::Track
94
+
95
+ Tracks also can do a lot, but not complete:
96
+
97
+ track = database.playlists.master.tracks.first
98
+
99
+ track.artist # => "Steppenwolf"
100
+ track.title # => "Born To Be Wild"
101
+ track.album # => "All Time Greatest Hits Remastered"
102
+ track.file_type # => "MPEG audio file"
103
+ track.transferred? # => true
104
+
105
+ ### RbPod::Error
60
106
 
61
- It's alpha quality -- many pieces still need to be implemented. Patches welcome.
107
+ If anything goes belly up at run time, an `RbPod::Error` exception should be thrown with a detailed message.
62
108
 
63
109
  ## Contributing
64
110
 
data/Rakefile CHANGED
@@ -9,6 +9,9 @@ require 'bundler/gem_tasks'
9
9
  # Bring in RSpec's built-in rake task.
10
10
  require 'rspec/core/rake_task'
11
11
 
12
+ # Bring in RDoc's built-in rake task.
13
+ require 'rdoc/task'
14
+
12
15
  # By default, clean, compile and then test.
13
16
  task :default => [ :compile, :test ]
14
17
 
@@ -30,6 +33,15 @@ RSpec::Core::RakeTask.new(:test) do |task|
30
33
  task.ruby_opts = '-w'
31
34
  end
32
35
 
36
+ desc "Build all RDoc documentation."
37
+ RDoc::Task.new(:rdoc) do |task|
38
+ task.main = 'README.md'
39
+ task.markup = 'markdown'
40
+ task.rdoc_files.include("README.md", 'lib/**/*.rb', 'ext/**/*.[ch]')
41
+ task.options << "--all"
42
+ task.rdoc_dir = 'doc'
43
+ end
44
+
33
45
  desc "Open a console with rbpod preloaded."
34
46
  task :console => [ :compile ] do
35
47
  sh 'bundle console'
@@ -3,53 +3,72 @@
3
3
  #include "rbpod.h"
4
4
  #include "collection.h"
5
5
 
6
- VALUE cRbPodCollection;
6
+ struct collection {
7
+ VALUE klass;
8
+ GList *list;
9
+ };
7
10
 
8
- struct collection { VALUE klass; GList *list; };
9
-
10
- inline VALUE rbpod_collection_create(GList *list, VALUE type) {
11
+ inline VALUE rbpod_collection_create(GList *list, VALUE type)
12
+ {
11
13
  struct collection *collection = ALLOC(struct collection);
12
14
  collection->list = list;
13
15
  collection->klass = type;
14
16
  return Data_Wrap_Struct(cRbPodCollection, NULL, NULL, (void *) collection);
15
17
  }
16
18
 
17
- static VALUE rbpod_collection_get(VALUE self, VALUE key) {
19
+ static VALUE rbpod_collection_get(VALUE self, VALUE key)
20
+ {
18
21
  struct collection *collection = TYPED_DATA_PTR(self, struct collection);
19
22
  GList *current = NULL;
20
23
 
21
- if (FIXNUM_P(key) == FALSE)
24
+ if (FIXNUM_P(key) == FALSE) {
22
25
  return Qnil;
26
+ }
23
27
 
24
28
  current = g_list_nth(collection->list, FIX2INT(key));
25
29
 
26
30
  return Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);
27
31
  }
28
32
 
29
- static VALUE rbpod_collection_last(VALUE self) {
33
+ static VALUE rbpod_collection_last(VALUE self)
34
+ {
30
35
  struct collection *collection = TYPED_DATA_PTR(self, struct collection);
31
36
  GList *current = g_list_last(collection->list);
37
+
38
+ if (current == NULL) {
39
+ return Qnil;
40
+ }
41
+
32
42
  return Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);
33
43
  }
34
44
 
35
- static VALUE rbpod_collection_first(VALUE self) {
45
+ static VALUE rbpod_collection_first(VALUE self)
46
+ {
36
47
  struct collection *collection = TYPED_DATA_PTR(self, struct collection);
37
48
  GList *current = g_list_first(collection->list);
49
+
50
+ if (current == NULL) {
51
+ return Qnil;
52
+ }
53
+
38
54
  return Data_Wrap_Struct(collection->klass, NULL, NULL, (void *) current->data);
39
55
  }
40
56
 
41
- static VALUE rbpod_collection_length(VALUE self) {
57
+ static VALUE rbpod_collection_length(VALUE self)
58
+ {
42
59
  struct collection *collection = TYPED_DATA_PTR(self, struct collection);
43
60
  return INT2NUM(g_list_length(collection->list));
44
61
  }
45
62
 
46
- static VALUE rbpod_collection_each(VALUE self) {
63
+ static VALUE rbpod_collection_each(VALUE self)
64
+ {
47
65
  struct collection *collection = TYPED_DATA_PTR(self, struct collection);
48
66
  GList *current = NULL;
49
67
  VALUE item;
50
68
 
51
- if (rb_block_given_p() == FALSE)
69
+ if (rb_block_given_p() == FALSE) {
52
70
  return rb_funcall(self, rb_intern("enum_for"), 1, ID2SYM(rb_intern("each")));
71
+ }
53
72
 
54
73
  /* If we were supplied a block, enumerate the entire list. */
55
74
  for (current = collection->list; current != NULL; current = current->next) {
@@ -60,35 +79,43 @@ static VALUE rbpod_collection_each(VALUE self) {
60
79
  return Qnil;
61
80
  }
62
81
 
63
- static VALUE rbpod_collection_initialize(VALUE self) {
82
+ static VALUE rbpod_collection_initialize(VALUE self)
83
+ {
64
84
  return self;
65
85
  }
66
86
 
67
- static void rbpod_collection_deallocate(void *handle) {
87
+ static void rbpod_collection_deallocate(void *handle)
88
+ {
68
89
  struct collection *collection = (struct collection *) handle;
69
90
 
70
- if (collection->list != NULL)
91
+ if (collection->list != NULL) {
71
92
  g_list_free(collection->list);
93
+ }
72
94
 
73
95
  xfree(handle);
74
96
  return;
75
97
  }
76
98
 
77
- static VALUE rbpod_collection_allocate(VALUE self) {
99
+ static VALUE rbpod_collection_allocate(VALUE self)
100
+ {
78
101
  struct collection *collection = ALLOC(struct collection);
79
102
  collection->list = NULL;
80
103
  collection->klass = Qnil;
81
104
  return Data_Wrap_Struct(cRbPodCollection, NULL, rbpod_collection_deallocate, (void *) collection);
82
105
  }
83
106
 
84
- void Init_rbpod_collection(void) {
107
+ void Init_rbpod_collection(void)
108
+ {
109
+ #if RDOC_CAN_PARSE_DOCUMENTATION
110
+ mRbPod = rb_define_module("RbPod");
111
+ #endif
85
112
  cRbPodCollection = rb_define_class_under(mRbPod, "Collection", rb_cObject);
86
113
 
87
114
  rb_define_alloc_func(cRbPodCollection, rbpod_collection_allocate);
88
115
 
89
116
  rb_include_module(cRbPodCollection, rb_mEnumerable);
90
117
 
91
- rb_define_method(cRbPodCollection, "initialize", rbpod_collection_initialize, 0);
118
+ rb_define_private_method(cRbPodCollection, "initialize", rbpod_collection_initialize, 0);
92
119
 
93
120
  rb_define_method(cRbPodCollection, "each", rbpod_collection_each, 0);
94
121
 
@@ -3,7 +3,7 @@
3
3
  #ifndef RBPOD_COLLECTION_H
4
4
  #define RBPOD_COLLECTION_H
5
5
 
6
- extern VALUE cRbPodCollection;
6
+ RUBY_EXTERN VALUE cRbPodCollection;
7
7
 
8
8
  void Init_rbpod_collection(void);
9
9
 
data/ext/rbpod/database.c CHANGED
@@ -1,32 +1,38 @@
1
1
  /* database.c */
2
2
 
3
3
  #include "rbpod.h"
4
- #include "track.h"
5
4
  #include "device.h"
6
- #include "playlist.h"
7
5
  #include "database.h"
8
- #include "collection.h"
6
+ #include "track_collection.h"
7
+ #include "playlist_collection.h"
9
8
 
10
- VALUE cRbPodDatabase;
11
-
12
- static VALUE rbpod_database_write(VALUE self) {
9
+ static VALUE rbpod_database_save(VALUE self)
10
+ {
13
11
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
14
- gboolean success = itdb_write(database, NULL); /* TODO: Improve error handling. */
15
- return BooleanValue(success);
12
+ GError *error = NULL;
13
+
14
+ if (itdb_write(database, &error) == FALSE) {
15
+ return rbpod_raise_error(error);
16
+ }
17
+
18
+ return self;
16
19
  }
17
20
 
18
- static VALUE rbpod_database_is_synchronized(VALUE self) {
21
+ static VALUE rbpod_database_synchronized_p(VALUE self)
22
+ {
19
23
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
20
24
  guint32 nontransferred = itdb_tracks_number_nontransferred(database);
21
25
  return BooleanValue(nontransferred == 0);
22
26
  }
23
27
 
24
- static VALUE rbpod_database_playlists_get(VALUE self) {
28
+ static VALUE rbpod_database_playlists_get(VALUE self)
29
+ {
25
30
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
26
31
  return rbpod_playlist_collection_create(self, database->playlists);
27
32
  }
28
33
 
29
- static VALUE rbpod_database_tracks_get(VALUE self) {
34
+ static VALUE rbpod_database_tracks_get(VALUE self)
35
+ {
30
36
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
31
37
  Itdb_Playlist *playlist = itdb_playlist_mpl(database);
32
38
  /* Use the master playlist as the parent for the master track list. */
@@ -34,103 +40,152 @@ static VALUE rbpod_database_tracks_get(VALUE self) {
34
40
  return rbpod_track_collection_create(parent, database->tracks);
35
41
  }
36
42
 
37
- static VALUE rbpod_database_device_get(VALUE self) {
43
+ static VALUE rbpod_database_device_get(VALUE self)
44
+ {
38
45
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
39
46
  return rbpod_device_create(database->device);
40
47
  }
41
48
 
42
- static VALUE rbpod_database_filename_get(VALUE self) {
49
+ static VALUE rbpod_database_filename_get(VALUE self)
50
+ {
43
51
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
44
52
  return rb_str_new2(database->filename);
45
53
  }
46
54
 
47
- static VALUE rbpod_database_version_get(VALUE self) {
55
+ static VALUE rbpod_database_version_get(VALUE self)
56
+ {
48
57
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
49
58
  return INT2NUM(database->version);
50
59
  }
51
60
 
52
- static VALUE rbpod_database_id_get(VALUE self) {
61
+ static VALUE rbpod_database_id_get(VALUE self)
62
+ {
53
63
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
54
64
  return INT2NUM(database->id);
55
65
  }
56
66
 
57
- static VALUE rbpod_database_mountpoint_get(VALUE self) {
67
+ static VALUE rbpod_database_mountpoint_get(VALUE self)
68
+ {
58
69
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
59
70
  return rb_str_new2(itdb_get_mountpoint(database));
60
71
  }
61
72
 
62
- static VALUE rbpod_database_mountpoint_set(VALUE self, VALUE path) {
73
+ static VALUE rbpod_database_mountpoint_set(VALUE self, VALUE path)
74
+ {
63
75
  Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
64
76
  itdb_set_mountpoint(database, StringValueCStr(path));
65
77
  return rbpod_database_mountpoint_get(self);
66
78
  }
67
79
 
68
- static void rbpod_database_deallocate(void *handle) {
80
+ static void rbpod_database_deallocate(void *handle)
81
+ {
69
82
  itdb_free((Itdb_iTunesDB *) handle);
70
83
  return;
71
84
  }
72
85
 
73
- static VALUE rbpod_database_create(VALUE self, VALUE device_name, VALUE mount_point, VALUE model_number) {
74
- gchar *_mount_point, *_model_number, *_device_name;
75
- Itdb_iTunesDB *database = NULL;
76
-
77
- _device_name = StringValueCStr(device_name);
78
- _mount_point = StringValueCStr(mount_point);
86
+ static VALUE rbpod_database_initialize(VALUE self, VALUE mount_point)
87
+ {
88
+ Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
89
+ Itdb_iTunesDB *previous = database;
90
+ GError *error = NULL;
79
91
 
80
- /* GPod can function with a NULL model number, however, artwork will not function. */
81
- _model_number = !NIL_P(model_number) ? StringValueCStr(model_number) : NULL;
92
+ /* Try to parse the database from the given mount point. */
93
+ database = itdb_parse(StringValueCStr(mount_point), &error);
82
94
 
83
- /* Initialize the iPod at this mount point, with this device name and model number. */
84
- /* TODO: Improve error handling. */
85
- if (itdb_init_ipod(_mount_point, _model_number, _device_name, NULL) == FALSE) {
86
- rb_raise(eRbPodError, "Unable to format this iPod.");
87
- return Qfalse;
95
+ if (database == NULL) {
96
+ return rbpod_raise_error(error);
88
97
  }
89
98
 
90
- /* Parse the newly created database. */
91
- /* TODO: Improve error handling. */
92
- database = itdb_parse(_mount_point, NULL);
99
+ /* Overwrite our old database with the new one. */
100
+ DATA_PTR(self) = database;
93
101
 
94
- if (database == NULL) {
95
- rb_raise(eRbPodError, "Not an iPod mount point.");
96
- return Qnil;
97
- }
102
+ /* Free the old one. */
103
+ itdb_free(previous);
98
104
 
99
- /* Return an instance of this class using the newly created database. */
105
+ return self;
106
+ }
107
+
108
+ static VALUE rbpod_database_allocate(VALUE self)
109
+ {
110
+ Itdb_iTunesDB *database = itdb_new();
100
111
  return Data_Wrap_Struct(cRbPodDatabase, NULL, rbpod_database_deallocate, (void *) database);
101
112
  }
102
113
 
103
- static VALUE rbpod_database_initialize(VALUE self, VALUE mount_point) {
104
- Itdb_iTunesDB *database = TYPED_DATA_PTR(self, Itdb_iTunesDB);
114
+ static VALUE rbpod_database_create(int argc, VALUE *argv, VALUE self)
115
+ {
116
+ VALUE mount_point, device_name, model_number, instance;
117
+ gchar *_mount_point, *_device_name, *_model_number;
118
+ GDir *directory = NULL;
119
+ GError *error = NULL;
105
120
 
106
- /* Try to parse the database from the given mount point. */
107
- /* TODO: Improve error handling. */
108
- database = itdb_parse(StringValueCStr(mount_point), NULL);
121
+ if (rb_scan_args(argc, argv, "12", &mount_point, &device_name, &model_number) < 1) {
122
+ rb_raise(eRbPodError, "Invalid arguments.");
123
+ return Qnil;
124
+ }
109
125
 
110
- /* The given mount point was not an iPod mount point. */
111
- if (database == NULL) {
112
- rb_raise(eRbPodError, "This is not an iPod mount point.");
126
+ /* Reject the mount point immediately if it isn't a string. */
127
+ if (TYPE(mount_point) != T_STRING && RSTRING_LEN(mount_point) > 0) {
128
+ rb_raise(eRbPodError, "Mount point must be a non-empty string.");
113
129
  return Qnil;
114
130
  }
115
131
 
116
- DATA_PTR(self) = database;
132
+ /* If we didn't specify a device name, default to 'iPod'. */
133
+ if (NIL_P(device_name) == TRUE) {
134
+ device_name = rb_str_new2("iPod");
135
+ }
117
136
 
118
- return self;
119
- }
137
+ /* If the specified device name isn't a string of at least three characters, bail now. */
138
+ if (TYPE(device_name) != T_STRING || RSTRING_LEN(device_name) < 3) {
139
+ rb_raise(eRbPodError, "Device name should be a string of at least three characters.");
140
+ return Qnil;
141
+ }
120
142
 
121
- static VALUE rbpod_database_allocate(VALUE self) {
122
- Itdb_iTunesDB *database = itdb_new();
123
- return Data_Wrap_Struct(cRbPodDatabase, NULL, rbpod_database_deallocate, (void *) database);
143
+ /* If the specified model number is specified but isn't a string, bail now. TODO: Use a regexp, stupid. */
144
+ if (NIL_P(model_number) == FALSE && (TYPE(model_number) != T_STRING || RSTRING_LEN(model_number) < 4)) {
145
+ rb_raise(eRbPodError, "Model number should be a string of at least four characters.");
146
+ return Qnil;
147
+ }
148
+
149
+ /* Extract pointers for glib use. */
150
+ _mount_point = StringValueCStr(mount_point);
151
+ _device_name = StringValueCStr(device_name);
152
+
153
+ /* GPod can function with a NULL model number, however, artwork may not function properly. */
154
+ _model_number = !NIL_P(model_number) ? StringValueCStr(model_number) : NULL;
155
+
156
+ /* Check if the mount point is a directory. */
157
+ directory = g_dir_open(_mount_point, 0, &error);
158
+
159
+ if (directory == NULL) {
160
+ return rbpod_raise_error(error);
161
+ }
162
+
163
+ /* Glib seems to think so... */
164
+ g_dir_close(directory);
165
+
166
+ /* Initialize the iPod at this mount point, with this device name and model number. */
167
+ if (itdb_init_ipod(_mount_point, _model_number, _device_name, &error) == FALSE) {
168
+ return rbpod_raise_error(error);
169
+ }
170
+
171
+ instance = rbpod_database_allocate(cRbPodDatabase);
172
+
173
+ /* Return an instance of this class using the newly created database. */
174
+ return rbpod_database_initialize(instance, mount_point);
124
175
  }
125
176
 
126
- void Init_rbpod_database(void) {
177
+ void Init_rbpod_database(void)
178
+ {
179
+ #if RDOC_CAN_PARSE_DOCUMENTATION
180
+ mRbPod = rb_define_module("RbPod");
181
+ #endif
127
182
  cRbPodDatabase = rb_define_class_under(mRbPod, "Database", rb_cObject);
128
183
 
129
184
  rb_define_alloc_func(cRbPodDatabase, rbpod_database_allocate);
130
185
 
131
186
  rb_define_method(cRbPodDatabase, "initialize", rbpod_database_initialize, 1);
132
187
 
133
- rb_define_singleton_method(cRbPodDatabase, "create!", rbpod_database_create, 3);
188
+ rb_define_singleton_method(cRbPodDatabase, "create!", rbpod_database_create, -1);
134
189
 
135
190
  rb_define_method(cRbPodDatabase, "mountpoint", rbpod_database_mountpoint_get, 0);
136
191
  rb_define_method(cRbPodDatabase, "mountpoint=", rbpod_database_mountpoint_set, 1);
@@ -143,8 +198,8 @@ void Init_rbpod_database(void) {
143
198
  rb_define_method(cRbPodDatabase, "tracks", rbpod_database_tracks_get, 0);
144
199
  rb_define_method(cRbPodDatabase, "playlists", rbpod_database_playlists_get, 0);
145
200
 
146
- rb_define_method(cRbPodDatabase, "synchronized?", rbpod_database_is_synchronized, 0);
201
+ rb_define_method(cRbPodDatabase, "synchronized?", rbpod_database_synchronized_p, 0);
147
202
 
148
- rb_define_method(cRbPodDatabase, "write!", rbpod_database_write, 0);
203
+ rb_define_method(cRbPodDatabase, "save!", rbpod_database_save, 0);
149
204
  }
150
205
 
data/ext/rbpod/database.h CHANGED
@@ -3,7 +3,7 @@
3
3
  #ifndef RBPOD_DATABASE_H
4
4
  #define RBPOD_DATABASE_H
5
5
 
6
- extern VALUE cRbPodDatabase;
6
+ RUBY_EXTERN VALUE cRbPodDatabase;
7
7
 
8
8
  void Init_rbpod_database(void);
9
9