mac-spotlight 0.0.1 → 0.0.2
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/Rakefile +1 -1
- data/ext/spotlight/spotlight.c +80 -51
- data/lib/spotlight.rb +5 -2
- data/mac-spotlight.gemspec +3 -3
- data/test/test_spotlight.rb +18 -0
- metadata +17 -31
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'rubygems'
|
|
5
5
|
require 'echoe'
|
6
6
|
require 'rake/extensiontask'
|
7
7
|
|
8
|
-
Echoe.new('mac-spotlight', '0.0.
|
8
|
+
Echoe.new('mac-spotlight', '0.0.2') do |p|
|
9
9
|
p.description = "Spotlight - Ruby interface to Mac OSX Spotlight"
|
10
10
|
p.url = "https://github.com/xli/spotlight"
|
11
11
|
p.author = "Li Xiao"
|
data/ext/spotlight/spotlight.c
CHANGED
@@ -11,36 +11,25 @@
|
|
11
11
|
|
12
12
|
#define RELEASE_IF_NOT_NULL(ref) { if (ref) { CFRelease(ref); } }
|
13
13
|
|
14
|
-
|
14
|
+
Boolean MDItemSetAttribute(MDItemRef item, CFStringRef name, CFTypeRef value);
|
15
15
|
|
16
|
-
VALUE
|
17
|
-
|
18
|
-
VALUE
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
rb_define_module_function(SpotlightIntern, "attributes", method_attributes, 1);
|
27
|
-
rb_define_module_function(SpotlightIntern, "set_attribute", method_set_attribute, 3);
|
28
|
-
rb_define_module_function(SpotlightIntern, "get_attribute", method_get_attribute, 2);
|
29
|
-
}
|
30
|
-
|
31
|
-
VALUE cfstring2rbstr(CFStringRef str) {
|
32
|
-
CFIndex len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8);
|
33
|
-
char *result = (char *)malloc(sizeof(char) * len);
|
34
|
-
CFStringGetCString(str, result, len, kCFStringEncodingUTF8);
|
35
|
-
free(result);
|
36
|
-
return rb_str_new2(result);
|
16
|
+
static VALUE cfstring2rbstr(CFStringRef str) {
|
17
|
+
const char *result;
|
18
|
+
VALUE rb_result = Qnil;
|
19
|
+
CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, str, kCFStringEncodingUTF8, 0);
|
20
|
+
if (data) {
|
21
|
+
result = (const char *)CFDataGetBytePtr(data);
|
22
|
+
rb_result = rb_str_new2(result);
|
23
|
+
}
|
24
|
+
RELEASE_IF_NOT_NULL(data)
|
25
|
+
return rb_result;
|
37
26
|
}
|
38
27
|
|
39
|
-
CFStringRef rbstr2cfstring(VALUE str) {
|
28
|
+
static CFStringRef rbstr2cfstring(VALUE str) {
|
40
29
|
return CFStringCreateWithCString(kCFAllocatorDefault, StringValuePtr(str), kCFStringEncodingUTF8);
|
41
30
|
}
|
42
31
|
|
43
|
-
CFStringRef date_string(CFDateRef date) {
|
32
|
+
static CFStringRef date_string(CFDateRef date) {
|
44
33
|
CFLocaleRef locale = CFLocaleCopyCurrent();
|
45
34
|
CFDateFormatterRef formatter = CFDateFormatterCreate(kCFAllocatorDefault, locale, kCFDateFormatterFullStyle, kCFDateFormatterFullStyle);
|
46
35
|
CFStringRef result = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault, formatter, date);
|
@@ -49,13 +38,13 @@ CFStringRef date_string(CFDateRef date) {
|
|
49
38
|
return result;
|
50
39
|
}
|
51
40
|
|
52
|
-
VALUE convert2rb_type(CFTypeRef ref) {
|
41
|
+
static VALUE convert2rb_type(CFTypeRef ref) {
|
53
42
|
VALUE result = Qnil;
|
54
43
|
double double_result;
|
55
44
|
int int_result;
|
56
45
|
long long_result;
|
57
46
|
int i;
|
58
|
-
if (ref
|
47
|
+
if (ref) {
|
59
48
|
if (CFGetTypeID(ref) == CFStringGetTypeID()) {
|
60
49
|
result = cfstring2rbstr(ref);
|
61
50
|
} else if (CFGetTypeID(ref) == CFDateGetTypeID()) {
|
@@ -82,18 +71,19 @@ VALUE convert2rb_type(CFTypeRef ref) {
|
|
82
71
|
return result;
|
83
72
|
}
|
84
73
|
|
85
|
-
CFTypeRef convert2cf_type(VALUE obj) {
|
86
|
-
CFTypeRef result =
|
74
|
+
static CFTypeRef convert2cf_type(VALUE obj) {
|
75
|
+
CFTypeRef result = NULL;
|
87
76
|
double double_result;
|
88
77
|
int int_result;
|
89
78
|
long long_result;
|
90
79
|
int i, len;
|
91
80
|
VALUE tmp[1];
|
92
81
|
CFAbsoluteTime time;
|
82
|
+
CFMutableArrayRef array_result;
|
93
83
|
|
94
84
|
switch (TYPE(obj)) {
|
95
85
|
case T_NIL:
|
96
|
-
result =
|
86
|
+
result = NULL;
|
97
87
|
break;
|
98
88
|
case T_TRUE:
|
99
89
|
result = kCFBooleanTrue;
|
@@ -129,29 +119,28 @@ CFTypeRef convert2cf_type(VALUE obj) {
|
|
129
119
|
break;
|
130
120
|
case T_ARRAY:
|
131
121
|
len = RARRAY(obj)->len;
|
132
|
-
|
122
|
+
array_result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
133
123
|
for (i = 0; i < len; i++) {
|
134
124
|
tmp[0] = INT2NUM(i);
|
135
|
-
|
125
|
+
CFArrayAppendValue(array_result, convert2cf_type(rb_ary_aref(1, tmp, obj)));
|
136
126
|
}
|
137
|
-
result =
|
138
|
-
|
127
|
+
result = array_result;
|
128
|
+
break;
|
129
|
+
default:
|
130
|
+
rb_raise(rb_eTypeError, "not valid value");
|
139
131
|
break;
|
140
132
|
}
|
141
133
|
return result;
|
142
134
|
}
|
143
135
|
|
144
|
-
void set_search_scope(MDQueryRef query, VALUE scopeDirectories) {
|
136
|
+
static void set_search_scope(MDQueryRef query, VALUE scopeDirectories) {
|
145
137
|
int i;
|
146
138
|
int len = RARRAY(scopeDirectories)->len;
|
147
|
-
|
139
|
+
CFMutableArrayRef scopesRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
|
148
140
|
for (i=0; i<len; i++) {
|
149
|
-
|
141
|
+
CFArrayAppendValue(scopesRef, rbstr2cfstring(rb_ary_pop(scopeDirectories)));
|
150
142
|
}
|
151
143
|
|
152
|
-
CFArrayRef scopesRef = CFArrayCreate(kCFAllocatorDefault, (const void **)scopes, len, nil);
|
153
|
-
free(scopes);
|
154
|
-
|
155
144
|
MDQuerySetSearchScope(query, scopesRef, 0);
|
156
145
|
RELEASE_IF_NOT_NULL(scopesRef);
|
157
146
|
}
|
@@ -163,33 +152,56 @@ VALUE method_search(VALUE self, VALUE queryString, VALUE scopeDirectories) {
|
|
163
152
|
MDItemRef item;
|
164
153
|
|
165
154
|
CFStringRef qs = rbstr2cfstring(queryString);
|
166
|
-
MDQueryRef query = MDQueryCreate(kCFAllocatorDefault, qs,
|
155
|
+
MDQueryRef query = MDQueryCreate(kCFAllocatorDefault, qs, NULL, NULL);
|
167
156
|
RELEASE_IF_NOT_NULL(qs);
|
168
|
-
|
169
157
|
if (query) {
|
170
158
|
set_search_scope(query, scopeDirectories);
|
171
159
|
if (MDQueryExecute(query, kMDQuerySynchronous)) {
|
172
160
|
result = rb_ary_new();
|
173
161
|
for(i = 0; i < MDQueryGetResultCount(query); ++i) {
|
174
162
|
item = (MDItemRef) MDQueryGetResultAtIndex(query, i);
|
175
|
-
|
176
|
-
|
163
|
+
if (item) {
|
164
|
+
path = MDItemCopyAttribute(item, kMDItemPath);
|
165
|
+
rb_ary_push(result, cfstring2rbstr(path));
|
166
|
+
RELEASE_IF_NOT_NULL(path);
|
167
|
+
}
|
177
168
|
}
|
178
169
|
}
|
179
|
-
RELEASE_IF_NOT_NULL(path);
|
180
170
|
RELEASE_IF_NOT_NULL(query);
|
181
171
|
}
|
182
172
|
|
183
173
|
return result;
|
184
174
|
}
|
185
175
|
|
176
|
+
static MDItemRef createMDItemByPath(VALUE path) {
|
177
|
+
CFStringRef pathRef = rbstr2cfstring(path);
|
178
|
+
MDItemRef mdi = MDItemCreate(kCFAllocatorDefault, pathRef);
|
179
|
+
RELEASE_IF_NOT_NULL(pathRef);
|
180
|
+
if (!mdi) {
|
181
|
+
rb_raise(rb_eTypeError, "Could not find MDItem by given path");
|
182
|
+
}
|
183
|
+
return mdi;
|
184
|
+
}
|
185
|
+
|
186
|
+
static int each_attribute(VALUE name, VALUE value, MDItemRef mdi) {
|
187
|
+
CFStringRef nameRef = rbstr2cfstring(name);
|
188
|
+
CFTypeRef valueRef = convert2cf_type(value);
|
189
|
+
|
190
|
+
if(!MDItemSetAttribute(mdi, nameRef, valueRef)) {
|
191
|
+
printf("set %s failed\n", StringValuePtr(name));
|
192
|
+
}
|
193
|
+
|
194
|
+
RELEASE_IF_NOT_NULL(valueRef);
|
195
|
+
RELEASE_IF_NOT_NULL(nameRef);
|
196
|
+
}
|
197
|
+
|
198
|
+
|
186
199
|
VALUE method_attributes(VALUE self, VALUE path) {
|
187
200
|
int i;
|
188
201
|
CFStringRef attrNameRef;
|
189
202
|
CFTypeRef attrValueRef;
|
190
|
-
|
191
|
-
|
192
|
-
RELEASE_IF_NOT_NULL(pathRef);
|
203
|
+
MDItemRef mdi = createMDItemByPath(path);
|
204
|
+
|
193
205
|
CFArrayRef attrNamesRef = MDItemCopyAttributeNames(mdi);
|
194
206
|
VALUE result = rb_hash_new();
|
195
207
|
for (i = 0; i < CFArrayGetCount(attrNamesRef); i++) {
|
@@ -206,7 +218,7 @@ VALUE method_attributes(VALUE self, VALUE path) {
|
|
206
218
|
}
|
207
219
|
|
208
220
|
VALUE method_get_attribute(VALUE self, VALUE path, VALUE name) {
|
209
|
-
MDItemRef mdi =
|
221
|
+
MDItemRef mdi = createMDItemByPath(path);
|
210
222
|
CFStringRef nameRef = rbstr2cfstring(name);
|
211
223
|
CFTypeRef valueRef = MDItemCopyAttribute(mdi, nameRef);
|
212
224
|
|
@@ -220,17 +232,34 @@ VALUE method_get_attribute(VALUE self, VALUE path, VALUE name) {
|
|
220
232
|
}
|
221
233
|
|
222
234
|
VALUE method_set_attribute(VALUE self, VALUE path, VALUE name, VALUE value) {
|
223
|
-
MDItemRef
|
235
|
+
MDItemRef mdi = createMDItemByPath(path);
|
224
236
|
CFStringRef nameRef = rbstr2cfstring(name);
|
225
237
|
CFTypeRef valueRef = convert2cf_type(value);
|
226
238
|
|
227
|
-
MDItemSetAttribute(
|
239
|
+
if(!MDItemSetAttribute(mdi, nameRef, valueRef)) {
|
240
|
+
printf("set %s failed\n", StringValuePtr(name));
|
241
|
+
}
|
228
242
|
|
229
243
|
RELEASE_IF_NOT_NULL(valueRef);
|
230
244
|
RELEASE_IF_NOT_NULL(nameRef);
|
231
|
-
RELEASE_IF_NOT_NULL(
|
245
|
+
RELEASE_IF_NOT_NULL(mdi);
|
232
246
|
|
233
247
|
return Qtrue;
|
234
248
|
}
|
235
249
|
|
250
|
+
VALUE method_set_attributes(VALUE self, VALUE path, VALUE attributes) {
|
251
|
+
MDItemRef mdi = createMDItemByPath(path);
|
252
|
+
rb_hash_foreach(attributes, each_attribute, (VALUE) mdi);
|
253
|
+
RELEASE_IF_NOT_NULL(mdi);
|
254
|
+
}
|
236
255
|
|
256
|
+
void Init_spotlight (void)
|
257
|
+
{
|
258
|
+
VALUE Spotlight = rb_define_module("Spotlight");
|
259
|
+
VALUE SpotlightIntern = rb_define_module_under(Spotlight, "Intern");
|
260
|
+
rb_define_module_function(SpotlightIntern, "search", method_search, 2);
|
261
|
+
rb_define_module_function(SpotlightIntern, "attributes", method_attributes, 1);
|
262
|
+
rb_define_module_function(SpotlightIntern, "set_attribute", method_set_attribute, 3);
|
263
|
+
rb_define_module_function(SpotlightIntern, "get_attribute", method_get_attribute, 2);
|
264
|
+
rb_define_module_function(SpotlightIntern, "set_attributes", method_set_attributes, 2);
|
265
|
+
}
|
data/lib/spotlight.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'spotlight/spotlight'
|
2
2
|
|
3
3
|
module Spotlight
|
4
|
-
VERSION = "0.0.
|
4
|
+
VERSION = "0.0.2"
|
5
5
|
|
6
6
|
def self.search(query_string, *scope_directories)
|
7
7
|
Intern.search(query_string, scope_directories).collect {|path| MDItem.new(path)}
|
8
8
|
end
|
9
9
|
|
10
10
|
class MDItem
|
11
|
-
VALID_ATTRIBUTE_VALUE_TYPE = [String, Time, Fixnum, Bignum, Float, Array]
|
11
|
+
VALID_ATTRIBUTE_VALUE_TYPE = [String, Time, Fixnum, Bignum, Float, Array, NilClass]
|
12
12
|
|
13
13
|
class InvalidAttributeValueTypeError < StandardError
|
14
14
|
end
|
@@ -20,6 +20,9 @@ module Spotlight
|
|
20
20
|
def attributes
|
21
21
|
Intern.attributes(@path)
|
22
22
|
end
|
23
|
+
def attributes=(attributes)
|
24
|
+
Intern.set_attributes(@path, attributes)
|
25
|
+
end
|
23
26
|
def [](attr_name)
|
24
27
|
Intern.get_attribute(@path, attr_name)
|
25
28
|
end
|
data/mac-spotlight.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{mac-spotlight}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Li Xiao"]
|
9
|
-
s.date = %q{2010-05-
|
9
|
+
s.date = %q{2010-05-09}
|
10
10
|
s.description = %q{Spotlight - Ruby interface to Mac OSX Spotlight}
|
11
11
|
s.email = %q{iam@li-xiao.com}
|
12
12
|
s.extensions = ["ext/spotlight/extconf.rb"]
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.rdoc_options = ["--main", "README.rdoc", "--inline-source", "--line-numbers", "--charset", "UTF-8"]
|
17
17
|
s.require_paths = ["lib", "ext"]
|
18
18
|
s.rubyforge_project = %q{mac-spotlight}
|
19
|
-
s.rubygems_version = %q{1.3.
|
19
|
+
s.rubygems_version = %q{1.3.5}
|
20
20
|
s.summary = %q{Spotlight - Ruby interface to Mac OSX Spotlight}
|
21
21
|
s.test_files = ["test/test_spotlight.rb"]
|
22
22
|
|
data/test/test_spotlight.rb
CHANGED
@@ -23,6 +23,14 @@ class TestSpotlight < Test::Unit::TestCase
|
|
23
23
|
assert_equal [@item], Spotlight.search("kMDItemNow == #{time_now}", @dir)
|
24
24
|
end
|
25
25
|
|
26
|
+
def test_search_with_multi_attribute
|
27
|
+
@item['kMDItemAttr1'] = 1
|
28
|
+
@item['kMDItemAttr2'] = 2
|
29
|
+
assert_equal [], Spotlight.search("kMDItemAttr1 == 2 && kMDItemAttr2 == 3", @dir)
|
30
|
+
assert_equal [], Spotlight.search("kMDItemAttr1 == 1 && kMDItemAttr2 == 3", @dir)
|
31
|
+
assert_equal [@item], Spotlight.search("kMDItemAttr1 == 1 && kMDItemAttr2 == 2", @dir)
|
32
|
+
end
|
33
|
+
|
26
34
|
def test_attributes
|
27
35
|
assert_equal "test_file.txt", @item.attributes['kMDItemDisplayName']
|
28
36
|
end
|
@@ -36,9 +44,11 @@ class TestSpotlight < Test::Unit::TestCase
|
|
36
44
|
def test_get_attribute
|
37
45
|
assert_equal "test_file.txt", @item['kMDItemDisplayName']
|
38
46
|
assert_equal ["public.plain-text", "public.text", "public.data", "public.item", "public.content"], @item['kMDItemContentTypeTree']
|
47
|
+
assert_nil @item['kMDItemNotExist']
|
39
48
|
end
|
40
49
|
|
41
50
|
def test_set_attribute
|
51
|
+
assert_attribute_set('kMDItemTestNilType', nil)
|
42
52
|
assert_attribute_set('kMDItemTestStringType', "Time.now: #{Time.now}")
|
43
53
|
assert_attribute_set('kMDItemTestLongType', Time.now.to_i)
|
44
54
|
assert_attribute_set('kMDItemTestIntType', Time.now.to_i.to_s[-3..-1].to_i)
|
@@ -47,6 +57,14 @@ class TestSpotlight < Test::Unit::TestCase
|
|
47
57
|
assert_attribute_set('kMDItemTestArrayTypeWithLong', [123456789])
|
48
58
|
end
|
49
59
|
|
60
|
+
def test_set_attributes
|
61
|
+
attributes = {'TimeNow' => "Time.now: #{Time.now}", 'SomeThing' => Time.now.to_i}
|
62
|
+
@item.attributes = attributes
|
63
|
+
attributes.each do |key, value|
|
64
|
+
assert_equal value, @item.reload[key]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
50
68
|
def test_set_date_attribute
|
51
69
|
assert_attribute_set('kMDItemTestDateType', Time.now)
|
52
70
|
end
|
metadata
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mac-spotlight
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
segments:
|
6
|
-
- 0
|
7
|
-
- 0
|
8
|
-
- 1
|
9
|
-
version: 0.0.1
|
4
|
+
version: 0.0.2
|
10
5
|
platform: ruby
|
11
6
|
authors:
|
12
7
|
- Li Xiao
|
@@ -14,45 +9,39 @@ autorequire:
|
|
14
9
|
bindir: bin
|
15
10
|
cert_chain: []
|
16
11
|
|
17
|
-
date: 2010-05-
|
12
|
+
date: 2010-05-09 00:00:00 -07:00
|
18
13
|
default_executable:
|
19
14
|
dependencies:
|
20
15
|
- !ruby/object:Gem::Dependency
|
21
16
|
name: rake
|
22
|
-
|
23
|
-
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
20
|
requirements:
|
25
21
|
- - ">="
|
26
22
|
- !ruby/object:Gem::Version
|
27
|
-
segments:
|
28
|
-
- 0
|
29
23
|
version: "0"
|
30
|
-
|
31
|
-
version_requirements: *id001
|
24
|
+
version:
|
32
25
|
- !ruby/object:Gem::Dependency
|
33
26
|
name: echoe
|
34
|
-
|
35
|
-
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
30
|
requirements:
|
37
31
|
- - ">="
|
38
32
|
- !ruby/object:Gem::Version
|
39
|
-
segments:
|
40
|
-
- 0
|
41
33
|
version: "0"
|
42
|
-
|
43
|
-
version_requirements: *id002
|
34
|
+
version:
|
44
35
|
- !ruby/object:Gem::Dependency
|
45
36
|
name: rake-compiler
|
46
|
-
|
47
|
-
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
48
40
|
requirements:
|
49
41
|
- - ">="
|
50
42
|
- !ruby/object:Gem::Version
|
51
|
-
segments:
|
52
|
-
- 0
|
53
43
|
version: "0"
|
54
|
-
|
55
|
-
version_requirements: *id003
|
44
|
+
version:
|
56
45
|
description: Spotlight - Ruby interface to Mac OSX Spotlight
|
57
46
|
email: iam@li-xiao.com
|
58
47
|
executables: []
|
@@ -96,21 +85,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
85
|
requirements:
|
97
86
|
- - ">="
|
98
87
|
- !ruby/object:Gem::Version
|
99
|
-
segments:
|
100
|
-
- 0
|
101
88
|
version: "0"
|
89
|
+
version:
|
102
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
91
|
requirements:
|
104
92
|
- - ">="
|
105
93
|
- !ruby/object:Gem::Version
|
106
|
-
segments:
|
107
|
-
- 1
|
108
|
-
- 2
|
109
94
|
version: "1.2"
|
95
|
+
version:
|
110
96
|
requirements: []
|
111
97
|
|
112
98
|
rubyforge_project: mac-spotlight
|
113
|
-
rubygems_version: 1.3.
|
99
|
+
rubygems_version: 1.3.5
|
114
100
|
signing_key:
|
115
101
|
specification_version: 3
|
116
102
|
summary: Spotlight - Ruby interface to Mac OSX Spotlight
|