openmeta.rb 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/Manifest.txt +20 -0
- data/README.md +50 -0
- data/Rakefile +39 -0
- data/bin/tag +10 -0
- data/lib/openmeta/cli.rb +110 -0
- data/lib/openmeta/framework/OpenMeta.framework/OpenMeta +0 -0
- data/lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMeta.h +250 -0
- data/lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMetaBackup.h +106 -0
- data/lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMetaPrefs.h +29 -0
- data/lib/openmeta/framework/OpenMeta.framework/Versions/A/OpenMeta +0 -0
- data/lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/Info.plist +40 -0
- data/lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/en.lproj/InfoPlist.strings +0 -0
- data/lib/openmeta/friendly_errors.rb +23 -0
- data/lib/openmeta/ui.rb +73 -0
- data/lib/openmeta/version.rb +4 -0
- data/lib/openmeta.rb +82 -0
- metadata +115 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.md
|
4
|
+
Rakefile
|
5
|
+
bin/tag
|
6
|
+
lib/openmeta.rb
|
7
|
+
lib/openmeta/cli.rb
|
8
|
+
lib/openmeta/framework/OpenMeta.framework/Headers
|
9
|
+
lib/openmeta/framework/OpenMeta.framework/OpenMeta
|
10
|
+
lib/openmeta/framework/OpenMeta.framework/Resources
|
11
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMeta.h
|
12
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMetaBackup.h
|
13
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMetaPrefs.h
|
14
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/A/OpenMeta
|
15
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/Info.plist
|
16
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/en.lproj/InfoPlist.strings
|
17
|
+
lib/openmeta/framework/OpenMeta.framework/Versions/Current
|
18
|
+
lib/openmeta/friendly_errors.rb
|
19
|
+
lib/openmeta/ui.rb
|
20
|
+
lib/openmeta/version.rb
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# openmeta.rb
|
2
|
+
|
3
|
+
* https://github.com/zhaocai/openmeta.rb
|
4
|
+
|
5
|
+
|
6
|
+
## DESCRIPTION:
|
7
|
+
|
8
|
+
openMeta in Ruby (MacRuby, more exactly!)
|
9
|
+
|
10
|
+
|
11
|
+
## SYNOPSIS:
|
12
|
+
|
13
|
+
Port openMeta api to macruby using framework.
|
14
|
+
|
15
|
+
## REQUIREMENTS:
|
16
|
+
|
17
|
+
* macruby
|
18
|
+
* thor
|
19
|
+
|
20
|
+
## INSTALL:
|
21
|
+
|
22
|
+
* `gem install openmeta.rb`
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
## DEVELOPERS:
|
28
|
+
|
29
|
+
After checking out the source, run:
|
30
|
+
|
31
|
+
$ rake newb
|
32
|
+
|
33
|
+
This task will install any missing dependencies, run the tests/specs,
|
34
|
+
and generate the RDoc.
|
35
|
+
|
36
|
+
## LICENSE:
|
37
|
+
|
38
|
+
Copyright (c) 2013 Zhao Cai <caizhaoff@gmail.com>
|
39
|
+
|
40
|
+
This program is free software: you can redistribute it and/or modify it under
|
41
|
+
the terms of the GNU General Public License as published by the Free Software
|
42
|
+
Foundation, either version 3 of the License, or (at your option)
|
43
|
+
any later version.
|
44
|
+
|
45
|
+
This program is distributed in the hope that it will be useful, but WITHOUT
|
46
|
+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
47
|
+
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
48
|
+
|
49
|
+
You should have received a copy of the GNU General Public License along with
|
50
|
+
this program. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
# require 'rake/distribute'
|
6
|
+
|
7
|
+
Hoe.plugin :gemspec
|
8
|
+
Hoe.plugin :git
|
9
|
+
Hoe.plugin :test
|
10
|
+
|
11
|
+
Hoe.spec 'openmeta.rb' do
|
12
|
+
|
13
|
+
developer('Zhao Cai', 'caizhaoff@gmail.com')
|
14
|
+
|
15
|
+
extra_deps << ['thor', '~> 0.17.0']
|
16
|
+
end
|
17
|
+
|
18
|
+
# distribute :FileItem do
|
19
|
+
# from "bin/openmeta"
|
20
|
+
# to "/usr/local/bin/om", :mode => 0755
|
21
|
+
# end
|
22
|
+
|
23
|
+
bindir="/usr/local/bin"
|
24
|
+
|
25
|
+
desc "install"
|
26
|
+
task :install do
|
27
|
+
open("#{bindir}/om", "w") { |f|
|
28
|
+
f.puts "#!/bin/sh",
|
29
|
+
%Q{VM_OPT_LEVEL=0 macruby #{File.expand_path("bin/openmeta")} "$@"}
|
30
|
+
}
|
31
|
+
chmod 0755, "#{bindir}/om"
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "uninstall"
|
35
|
+
task :uninstall do
|
36
|
+
safe_unlink "#{bindir}/om"
|
37
|
+
end
|
38
|
+
|
39
|
+
# vim: syntax=ruby
|
data/bin/tag
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env macruby
|
2
|
+
|
3
|
+
($LOAD_PATH << File.expand_path("../../lib", __FILE__)).uniq!
|
4
|
+
require 'rubygems' unless defined? Gem # rubygems is only needed in 1.8
|
5
|
+
|
6
|
+
require 'openmeta'
|
7
|
+
|
8
|
+
require 'openmeta/cli'
|
9
|
+
require 'openmeta/friendly_errors'
|
10
|
+
Openmeta.with_friendly_errors { Openmeta::CLI.start }
|
data/lib/openmeta/cli.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'openmeta/ui'
|
3
|
+
|
4
|
+
module Openmeta
|
5
|
+
|
6
|
+
class CLI < Thor
|
7
|
+
|
8
|
+
def initialize(*)
|
9
|
+
super
|
10
|
+
the_shell = (options["no-color"] || (not STDOUT.tty?) ? Thor::Shell::Basic.new : shell)
|
11
|
+
Openmeta.ui = UI::Shell.new(the_shell)
|
12
|
+
Openmeta.ui.debug! if options["verbose"]
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "recent", "print recent tags"
|
16
|
+
method_option :format,
|
17
|
+
:aliases => "-f" ,
|
18
|
+
:lazy_default => nil ,
|
19
|
+
:type => :string ,
|
20
|
+
:desc => "print as yaml/plist"
|
21
|
+
def recent
|
22
|
+
tags = OpenMetaPrefs.recentTags
|
23
|
+
|
24
|
+
puts_to(options[:format], tags)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "clone", "clone openmeta tags and rating"
|
28
|
+
method_option :from,
|
29
|
+
:aliases => "-f",
|
30
|
+
:required => true,
|
31
|
+
:type => :string,
|
32
|
+
:desc => "clone openmeta tags and rating from the FILE [required]"
|
33
|
+
def clone(file)
|
34
|
+
unless File.exist?(from_file)
|
35
|
+
raise Openmeta::PathError, "#{from_file} does not exist!"
|
36
|
+
end
|
37
|
+
|
38
|
+
tags = Openmeta.get_tags(from_file)
|
39
|
+
rating = Openmeta.get_rating(from_file)
|
40
|
+
|
41
|
+
Openmeta.set_tags(tags, file) unless tags.empty?
|
42
|
+
Openmeta.set_rating(rating, file) unless rating == 0.0
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
desc "get", "get openmeta tags and rating"
|
47
|
+
method_option :format,
|
48
|
+
:aliases => "-f" ,
|
49
|
+
:lazy_default => nil ,
|
50
|
+
:type => :string ,
|
51
|
+
:desc => "print as yaml/plist"
|
52
|
+
def get(file)
|
53
|
+
|
54
|
+
tags = {
|
55
|
+
:tags => Openmeta.get_tags(file),
|
56
|
+
:rating => Openmeta.get_rating(file)
|
57
|
+
}
|
58
|
+
|
59
|
+
puts_to(options[:format], tags)
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "clear", "clear openmeta tags and rating"
|
63
|
+
def clear(file)
|
64
|
+
Openmeta.set_tags([], file)
|
65
|
+
Openmeta.set_rating(0.0, file)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
desc "set", "set openmeta tags, use ',' to separate multiple tags"
|
70
|
+
method_option :tag,
|
71
|
+
:aliases => "-t" ,
|
72
|
+
:lazy_default => '' ,
|
73
|
+
:type => :string ,
|
74
|
+
:desc => "set tags, use ',' to separate multiple tags"
|
75
|
+
def set(file)
|
76
|
+
tags = options[:tag].split(',')
|
77
|
+
Openmeta.set_tags(tags, file)
|
78
|
+
end
|
79
|
+
|
80
|
+
desc "rate", "set openmeta rating"
|
81
|
+
method_option :rate,
|
82
|
+
:aliases => "-r" ,
|
83
|
+
:lazy_default => 0.0 ,
|
84
|
+
:type => :numeric ,
|
85
|
+
:desc => "set rating"
|
86
|
+
def rate(file)
|
87
|
+
if options[:rate] > 5.0
|
88
|
+
raise RangeError, "rating is between [0.0 - 5.0]"
|
89
|
+
end
|
90
|
+
Openmeta.set_rating(options[:rate], file)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def puts_to(format, data)
|
95
|
+
unless format
|
96
|
+
puts data
|
97
|
+
else
|
98
|
+
require 'yaml' if format == 'yaml'
|
99
|
+
to_format = "to_#{format}"
|
100
|
+
|
101
|
+
begin
|
102
|
+
puts data.send(to_format)
|
103
|
+
rescue NoMethodError
|
104
|
+
raise Openmeta::NoMethodError, "#{to_format} has not implemented!"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
Binary file
|
@@ -0,0 +1,250 @@
|
|
1
|
+
//
|
2
|
+
// OpenMeta.h
|
3
|
+
// OpenMeta
|
4
|
+
//
|
5
|
+
// Created by Tom Andersen on 17/07/08.
|
6
|
+
//
|
7
|
+
/*
|
8
|
+
Copyright (c) 2009 ironic software
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person
|
11
|
+
obtaining a copy of this software and associated documentation
|
12
|
+
files (the "Software"), to deal in the Software without
|
13
|
+
restriction, including without limitation the rights to use,
|
14
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
15
|
+
copies of the Software, and to permit persons to whom the
|
16
|
+
Software is furnished to do so, subject to the following
|
17
|
+
conditions:
|
18
|
+
|
19
|
+
The above copyright notice and this permission notice shall be
|
20
|
+
included in all copies or substantial portions of the Software.
|
21
|
+
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
23
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
24
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
25
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
26
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
27
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
28
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
29
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
30
|
+
*/
|
31
|
+
|
32
|
+
#import <Cocoa/Cocoa.h>
|
33
|
+
|
34
|
+
/*
|
35
|
+
Open Meta - why duplicate attributes that are already defined?
|
36
|
+
|
37
|
+
To answer this, look at an example. kMDItemKeywords:
|
38
|
+
|
39
|
+
When a file with keywords embedded in it is created or lands on the computer, say for example a PDF file, Spotlight
|
40
|
+
will import it. The keywords will be stored under kMDItemKeywords in the Spotlight DB.
|
41
|
+
|
42
|
+
Now a user wants to set keywords (ie tags) on a file - any file on their computer - whether or not
|
43
|
+
the file type supports keywords or not. If Open Meta used kMDItemKeywords to store these - it will work pretty well,
|
44
|
+
until the user stored their own tags, on that PDF file that already had embedded keywords. Then all sorts of problems happen:
|
45
|
+
1) The existing keywords are hidden from the user, as keywords set on the xattr will override the ones set in the meta data.
|
46
|
+
2) These hidden keywords will come back when the file is viewed with Preview, or Acrobat, etc.
|
47
|
+
3) If the keywords on the the file are changed inside Preview, then these changes will not show up in spotlight
|
48
|
+
|
49
|
+
There are two solutions to this sort of problem.
|
50
|
+
|
51
|
+
One is to edit the 'actual keywords' inside the PDF. This solution quickly gets
|
52
|
+
complicated, as for each file type there may be none (eg: text file), one (eg:PDF), several (eg: jpeg, word?)
|
53
|
+
places to store keywords, and the software to read and write keywords into all supported file types
|
54
|
+
quickly grows to be unmanagable. The solution for text and other non keywordable files
|
55
|
+
is to write the tags somewhere else (eg sidecar files).
|
56
|
+
|
57
|
+
The other solution is the tact taken by Open Meta.
|
58
|
+
Keywords are written to their own tag, which is indexed by Spotlight, (kMDItemOMUserTags).
|
59
|
+
These tags are independent of kMDItemkeywords.
|
60
|
+
They can be written in the exact same very simple manner to each and every file on the file system.
|
61
|
+
They do not hide the keywords set on the file.
|
62
|
+
Since they are stored in xattrs, they can easily be included or excluded from a file, when
|
63
|
+
that file is for instance shipped off to a third party.
|
64
|
+
This is useful in order to keep metadata 'in house'. BUT - the data set by OpenMeta is not 'in the file' the same
|
65
|
+
way that tags set on a jpeg are 'in' the EXIF portion of the file when bridge does it.
|
66
|
+
The Open Meta tags follow the file around on the OS - through backups, copies and moves.
|
67
|
+
|
68
|
+
Other keys
|
69
|
+
----------------
|
70
|
+
I used to consider it 'wrong' to be able to override kMDItem* stuff with OpenMeta, and it is for tags. Tags though are a special case,
|
71
|
+
in that there could be for instance 6 keywords (relevant - ish) set on a PDF, and you want to add the open meta tag 'special' to it. You don't want to
|
72
|
+
lose the 6 keywords do you? There are also lots of images from image houses that have a lot of keyword 'noise' in them that you might not want
|
73
|
+
cluttering up your tags that you have set. i have found png files with 50 keywords set. html can also be bad for this. So users want the ability to only look at tags that they have set,
|
74
|
+
or a combination of keywords and tags.
|
75
|
+
BUT - look at ratings - ratings are just one number - it is likely that you don't want to as a user, have to think about 2 different places ratings
|
76
|
+
could be stored, (like tags vs keywords), but would rather have just the one concept of 'rating'. It is also ok, even deisrable, to be able to override the rating
|
77
|
+
on a file. So ratings _should_ use kMDItemStarRating.
|
78
|
+
Also look at 'less used' keys - like camera (kMDItemAcquisitionMake and kMDItemAcquisitionModel) - although they will be set on perhaps thousands of photos in
|
79
|
+
what if you run into a PDF that is a picture taken with a camera, and you want to tag that? openmeta will allow you to to tag it with kMDItemAcquisitionMake and kMDItemAcquisitionModel
|
80
|
+
so that searches for camera make an model do not have to 'know about openmeta' to work.
|
81
|
+
Plus it's always good to keep the number of keys down.
|
82
|
+
|
83
|
+
What about namespaces?
|
84
|
+
----------------------
|
85
|
+
Open Meta is a clean simple way to set user entered searchable metadata on any file on Mac OS X.
|
86
|
+
Concepts like namespaces are not encouraged, as most users have no idea what a namespace is. The tradeoff is a
|
87
|
+
small amount of _understandable_ ambiguity - searching for Tags:apple (i.e. kMDItemOMUserTags == "apple"cd) will find
|
88
|
+
all files having to do with both the fruit one can eat, and the company that makes computers. Users expect this.
|
89
|
+
With namespaces an improperly constructed query will usually result in 'no matches'.
|
90
|
+
*/
|
91
|
+
|
92
|
+
|
93
|
+
/*
|
94
|
+
Note on Backup.
|
95
|
+
|
96
|
+
Whenever you set an item with kMDItemOM* as the key, openmeta will make sure that there is a backup made of the tags, etc, that
|
97
|
+
you have set on the file. The backups go into the folder ~/Library/Application Support/OpenMeta/backups.noindex/2009 etc. The backups are
|
98
|
+
one file per item, and are on a month by month basis. This may be all the backup you need, as time machine will back these up.
|
99
|
+
The .noindex suffix on the backup folder stops Spotlight from indexing the files.
|
100
|
+
|
101
|
+
Note on Backup - "Time Machine", etc.
|
102
|
+
When you set an xattr on a file, the modification date on the file is NOT changed,
|
103
|
+
but something called the status change time - does change.
|
104
|
+
Time Machine, however, will only back up on modified time changes - i.e. it only looks at st_mtimespec, ignoring st_ctimespec.
|
105
|
+
This is a deliberate decision on TimeMachine's part.
|
106
|
+
So if you want your xattrs backed up - you need to set the modifcation date on the file to a newer date - utimes() is the call for this.
|
107
|
+
It may be that you do not want to change the file modification date for each OpenMeta item that you write. If you do change the modification date,
|
108
|
+
it may make sense to not change it 'by much' - thus preserving most of the
|
109
|
+
meaning of the modification date, while still allowing TimeMachine to back the file up.
|
110
|
+
*/
|
111
|
+
|
112
|
+
|
113
|
+
// OpenMeta: Open metadata format for OS X. Data is stored in xattr.
|
114
|
+
// Some items are reflected in the Spotlight Database, others not.
|
115
|
+
// ---------------------
|
116
|
+
// This system allows someone to set metadata on a file, and have that metadata searchable in spotlight.
|
117
|
+
// Several open meta keys are defined: See the schema.xml file in the OpenMeta spotlight plugin for the complete list.
|
118
|
+
//
|
119
|
+
// User Entered Tags: Tags that users have added for workflow and organizational reasons.
|
120
|
+
//
|
121
|
+
// Bookmarks: URLs associated with a document
|
122
|
+
//
|
123
|
+
// Workflow: people, companies, etc that are in the workflow for this document
|
124
|
+
//
|
125
|
+
// Projects: Projects that this file is relevant to
|
126
|
+
|
127
|
+
// on success return nil error
|
128
|
+
// If setting/getting user tags failed we return a negative error code for one of our errors, or the error code from the underlying api (setxattr)
|
129
|
+
// If there is errno set after a call we return that. errno codes seem to be positive numbers
|
130
|
+
// We use NSError, and you can pass in nil if you are not interested in an error.
|
131
|
+
#define OM_ParamError (-1)
|
132
|
+
#define OM_NoDataFromPropertyListError (-2)
|
133
|
+
#define OM_NoMDItemFoundError (-3)
|
134
|
+
#define OM_CantSetMetadataError (-4)
|
135
|
+
#define OM_MetaTooBigError (-5)
|
136
|
+
// A very common errno error code is ENOATTR - the attribute is not set on the file. - which we don't consider an error
|
137
|
+
|
138
|
+
extern NSString* const kMDItemOMUserTags;
|
139
|
+
extern NSString* const kMDItemOMUserTagTime;
|
140
|
+
extern NSString* const kMDItemOMDocumentDate;
|
141
|
+
extern NSString* const kMDItemOMBookmarks; // list of urls - bookmarks as nsarray nsstring
|
142
|
+
extern NSString* const kMDItemOMUserTagApplication;
|
143
|
+
|
144
|
+
extern const double kMDItemOMMaxRating;
|
145
|
+
|
146
|
+
// kMDItemKeywords
|
147
|
+
@interface OpenMeta : NSObject {
|
148
|
+
|
149
|
+
}
|
150
|
+
|
151
|
+
// User tags - an array of tags as entered by the user. This is not the place to
|
152
|
+
// store program generated gook, like GUIDs or urls, etc.
|
153
|
+
// It is not nice to erase tags that are already set (unless the user has deleted them using your UI)
|
154
|
+
// so ususally you would do a getUserTags, then merge/edit/ etc, followed by a setUserTags
|
155
|
+
// Tags - NSStrings - conceptually utf8 - any characters allowed, spaces, commas, etc.
|
156
|
+
// Case sensitive or not? Case preserving. Order of tags is not guaranteed, but it is attempted.
|
157
|
+
// setUserTags will remove duplicates from the array, using case preserving rules.
|
158
|
+
+(NSError*)setUserTags:(NSArray*)tags path:(NSString*)path;
|
159
|
+
+(NSArray*)getUserTags:(NSString*)path error:(NSError**)error;
|
160
|
+
+(NSError*)addUserTags:(NSArray*)tags path:(NSString*)path;
|
161
|
+
+(NSError*)clearUserTags:(NSArray*)tags path:(NSString*)path;
|
162
|
+
|
163
|
+
|
164
|
+
// To change tags on groups of files:
|
165
|
+
// You first obtain the common tags in a list of files,
|
166
|
+
// then edit those common tags, then set the changes
|
167
|
+
// you need to pass in the original common tags when writing the new tags out,
|
168
|
+
// so that we can be sure we are not overwriting other changes made by other users, etc
|
169
|
+
// during the edit cycle:
|
170
|
+
// These calls are case preserving and case insensitive. So Apple and apple will both be the 'same' tag on reading
|
171
|
+
+(NSError*)setCommonUserTags:(NSArray*)paths originalCommonTags:(NSArray*)originalTags replaceWith:(NSArray*)newTags;
|
172
|
+
+(NSArray*)getCommonUserTags:(NSArray*)paths error:(NSError**)error;
|
173
|
+
|
174
|
+
|
175
|
+
// Ratings are 0 - 5 stars. Setting a rating to 0 is the same as having no rating.
|
176
|
+
// rating is 0 - 5 floating point, so you have plenty of room. I clamp 0 - 5 on setting it.
|
177
|
+
// passing 0 to setRating removes the rating. Also I return 0 if I can't find a rating.
|
178
|
+
// Users have a hard time conceptualizing the difference between having no rating set and a rating of 0
|
179
|
+
+(NSError*)setRating:(double)rating05 path:(NSString*)path;
|
180
|
+
+(double)getRating:(NSString*)path error:(NSError**)error;
|
181
|
+
|
182
|
+
// simplest way to set metadata that will be picked up by spotlight:
|
183
|
+
// The string will be stored in the spotlight datastore. Likely you will not want
|
184
|
+
// to set large strings with this, as it will be hard on the spotlight db.
|
185
|
+
+(NSError*)setString:(NSString*)string keyName:(NSString*)keyName path:(NSString*)path;
|
186
|
+
+(NSString*)getString:(NSString*)keyName path:(NSString*)path error:(NSError**)error;
|
187
|
+
|
188
|
+
+(NSError*)setNumber:(NSNumber*)number keyName:(NSString *)keyName path:(NSString *)path;
|
189
|
+
+(NSNumber *)getNumber:(NSString*)keyName path:(NSString*)path error:(NSError**)error;
|
190
|
+
|
191
|
+
+(NSError*)setBoolValue:(BOOL)boolValue keyName:(NSString *)keyName path:(NSString *)path;
|
192
|
+
+(BOOL)getBoolValue:(NSString*)keyName path:(NSString*)path error:(NSError**)error;
|
193
|
+
|
194
|
+
+(NSError*)setDate:(NSDate *)date keyName:(NSString *)keyName path:(NSString *)path;
|
195
|
+
+(NSDate *)getDate:(NSString*)keyName path:(NSString*)path error:(NSError**)error;
|
196
|
+
|
197
|
+
+(NSError*)setData:(NSData *)data keyName:(NSString *)keyName path:(NSString *)path;
|
198
|
+
+(NSData *)getData:(NSString*)keyName path:(NSString*)path error:(NSError**)error;
|
199
|
+
|
200
|
+
|
201
|
+
// If you have a 'lot' (ie 200 bytes to 4k) to set as a metadata on a file, then what you want to do
|
202
|
+
// is use the setDictionaries call. You organize your data in an array of dictionaries,
|
203
|
+
// and each dict will be put into the metadata store and NOT be indexed by spotlight.
|
204
|
+
// In each dictionary, you optionally set one item with the key @"name" and THAT information will be stored in the spotlight DB
|
205
|
+
// Say you set keyName to 'kMDItemOMLastPrintedInfo', then there would be an xattr with the name 'kMDItemOMLastPrintedInfo' that is an array of nsdictionaries
|
206
|
+
// AND an xattr set on the file with com.apple.metadata:kMDItemOMLastPrintedInfo' which will be an array of the 'names' from the dicts.
|
207
|
+
// the 'name' would usually be used for search purposes. Other data can be 'anything'
|
208
|
+
|
209
|
+
|
210
|
+
// for meta data in arrays: The add call weeds out duplicates
|
211
|
+
+(NSArray*)getNSArrayMetaData:(NSString*)metaDataKey path:(NSString*)path error:(NSError**)error;
|
212
|
+
+(NSError*)setNSArrayMetaData:(NSArray*)array metaDataKey:(NSString*)metaDataKey path:(NSString*)path;
|
213
|
+
+(NSError*)addToNSArrayMetaData:(NSArray*)itemsToAdd metaDataKey:(NSString*)metaDataKey path:(NSString*)path;
|
214
|
+
|
215
|
+
|
216
|
+
// extended attributes:
|
217
|
+
// These getters and setters are to set xattr data that will be read and indexed by spotlight
|
218
|
+
// If you pass large amounts of data or objects like dictionaries that spotlght cannot index, results are undefined.
|
219
|
+
// The only things that spotlight can handle (as far as I know) are small arrays and nsstrings, nsdates, and nsnumbers
|
220
|
+
+(id)getXAttrMetaData:(NSString*)metaDataKey path:(NSString*)path error:(NSError**)error;
|
221
|
+
+(NSError*)setXAttrMetaData:(id)plistObject metaDataKey:(NSString*)metaDataKey path:(NSString*)path;
|
222
|
+
|
223
|
+
// dictionaries:
|
224
|
+
// Spotlight can't have dictionaries in it's database.
|
225
|
+
// remember that. You can store dictionaries using getXAttrMetaDataNoSpotlightMirror, or just setXAttr
|
226
|
+
|
227
|
+
|
228
|
+
// to set data with no spotlight mirror, use this. omKey is something like kMDItemOMSomeKeyThatsNotNeededInSpotlight
|
229
|
+
+(id)getXAttrMetaDataNoSpotlightMirror:(NSString*)omKey path:(NSString*)path error:(NSError**)error;
|
230
|
+
+(NSError*)setXAttrNoSpotlightMirror:(id)plistObject omKey:(NSString*)omKey path:(NSString*)path;
|
231
|
+
|
232
|
+
// These getters and setters are to set xattr data that will be NOT read and indexed by spotlight - nor are openmeta time stamps set, nor is restore done on backup.
|
233
|
+
// The passed plist object will be converted to data as a binary plist object. (plist object is for example an nsdictionary or nsarray)
|
234
|
+
// You can pass data up to 4k (or close to that depending on how much the data takes up in binary plist format)
|
235
|
+
+(id)getXAttr:(NSString*)inKeyName path:(NSString*)path error:(NSError**)error;
|
236
|
+
+(NSError*)setXAttr:(id)plistObject forKey:(NSString*)inKeyName path:(NSString*)path;
|
237
|
+
|
238
|
+
|
239
|
+
// utils
|
240
|
+
+(NSArray*)orderedArrayWithDict:(NSDictionary*)inTags sortHint:(NSArray*)inSortedItems;
|
241
|
+
|
242
|
+
// this call makes sure that all the keys in the OpenMeta kMDItemOM group get seen by Spotlight, so that the associated strings.xml. strings.schema, etc can work.
|
243
|
+
+(void)registerUsualOMAttributes; // just call this on launch if you want to be sure.
|
244
|
+
+(void)registerOMAttributes:(NSDictionary*)typicalAttributes forAppName:(NSString*)appName;
|
245
|
+
|
246
|
+
+(NSString*)spotlightKey:(NSString*)inKeyName;
|
247
|
+
+(NSString*)openmetaKey:(NSString*)inKeyName;
|
248
|
+
+(NSString*)openmetaTimeKey:(NSString*)inKeyName;
|
249
|
+
|
250
|
+
@end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
//
|
2
|
+
// OpenMetaBackup.h
|
3
|
+
// Fresh
|
4
|
+
//
|
5
|
+
// Created by Tom Andersen on 26/01/09.
|
6
|
+
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
#import <Cocoa/Cocoa.h>
|
10
|
+
|
11
|
+
/*
|
12
|
+
Backing up xattr:
|
13
|
+
|
14
|
+
Extended Attributes are supposed to 'not dissapear' they are connected to a file. When a user does a 'save' on a file, the default unix and cocoa apis will
|
15
|
+
preserve all the xattrs. BUT there are applications (notably Adobe CS 4 products, like Photoshop) which do a save in such a way as to wipe out all xattrs on each file
|
16
|
+
at each save. Not good. Also when using tools like subversion, etc where files are downloaded from the internet, xattrs can be easily lost.
|
17
|
+
|
18
|
+
So OpenMeta makes a backup file for every edit to xattrs when using the API.
|
19
|
+
|
20
|
+
The exact details may change in the future, the idea though is that when we go to retrieve tags for a file, if we find no tags, we check to see if there is a
|
21
|
+
backup file we can restore from. If no backup file, then there really are no tags on the file.
|
22
|
+
|
23
|
+
The backups are stored in the home folder of the person who made the change to the file, in ~/Library/OpenMeta/backups.noindex. This works well for most usage scenarios,
|
24
|
+
but with network volumes or mobile volumes, it may be better to store the backups in a predetermined place on the volume.
|
25
|
+
|
26
|
+
*/
|
27
|
+
|
28
|
+
@interface OpenMetaBackup : NSObject {
|
29
|
+
|
30
|
+
}
|
31
|
+
|
32
|
+
// individual file handling:
|
33
|
+
// ---------------------------
|
34
|
+
|
35
|
+
// backup is called automatically each time you set any attribute with kMDItemOM*, so you actually don't have to call this.
|
36
|
+
+(void)backupMetadata:(NSString*)inPath;
|
37
|
+
|
38
|
+
// restore is called for you ONLY on tags and ratings. If you are using OpenMeta and not using tags or ratings, you need to call this first, in case the
|
39
|
+
// OpenMeta data has been deleted by an application like Adobe Photoshop CS4 which removes all metadata on files.
|
40
|
+
+(void)restoreMetadata:(NSString*)inPath;
|
41
|
+
|
42
|
+
|
43
|
+
// Restoring all metadata
|
44
|
+
// ---------------------------
|
45
|
+
// call this to restore all backed up meta data. Call when? On every launch is likely too much.
|
46
|
+
// if you call to tell user when done (ie initiated from a menu command, or similar), then the process runs at full speed, putting up a dialog when done.
|
47
|
+
// it does run in a thread, though..
|
48
|
+
+(void)restoreAllMetadataOnBackgroundThread:(BOOL)tellUserWhenDone;
|
49
|
+
|
50
|
+
|
51
|
+
// live repair
|
52
|
+
// Adobe products in particular can erase all xattrs when performing a simple 'Save' on a file - yeuuch
|
53
|
+
// Open Meta will only usually restore tags if a file is re-opened for tagging. This
|
54
|
+
// thread attempts to repair the files as they are damaged
|
55
|
+
// Right now i don't personally run this thread in any ironic apps, other than our 'OM fix' app.
|
56
|
+
+(void)startLiveRepairThread;
|
57
|
+
+(void)stopLiveRepairThread; // you can't start/stop the live repair
|
58
|
+
|
59
|
+
// upgrade to open meta kMDItemOM user tags.
|
60
|
+
+(void)upgradeOpenMetaTokMDItemOM;
|
61
|
+
|
62
|
+
// shutting down OpenMeta backup and restore systems
|
63
|
+
// ---------------------------
|
64
|
+
|
65
|
+
// call this on quit, to leave time for any restores to safely, possibly, partially finish
|
66
|
+
+(void)appIsTerminating;
|
67
|
+
|
68
|
+
// for OpenMeta.m use
|
69
|
+
+(BOOL)attributeKeyMeansAutomaticBackup:(NSString*)attrName;
|
70
|
+
+(void)copyTagsFromOldKeyTokMDItemOMIfNeeded:(NSString*)inPath;
|
71
|
+
|
72
|
+
// these calls are async and send a notification on main thread when they are done.
|
73
|
+
+(void)backupMetadataToSingleFile:(NSArray*)inKeys toFile:(NSString*)toFile;
|
74
|
+
+(void)restoreMetadataFromSingleFile:(NSString*)inBackupFile;
|
75
|
+
|
76
|
+
@end
|
77
|
+
|
78
|
+
// class for single file bullk backup
|
79
|
+
@interface OpenMetaBackupOperation : NSOperation
|
80
|
+
{
|
81
|
+
NSString* singleFile;
|
82
|
+
NSArray* keysToSearch;
|
83
|
+
NSDictionary* returnDict;
|
84
|
+
BOOL writeFile;
|
85
|
+
}
|
86
|
+
|
87
|
+
@property (retain) NSString* singleFile;
|
88
|
+
@property (retain) NSArray* keysToSearch;
|
89
|
+
@property (retain) NSDictionary* returnDict;
|
90
|
+
@property BOOL writeFile;
|
91
|
+
|
92
|
+
@end
|
93
|
+
|
94
|
+
|
95
|
+
// class for converting from kOMUserTags to kMDItemOMUserTags
|
96
|
+
// it actually erases no data, it just adds a few fields of new data
|
97
|
+
@interface OpenMetaUpgradeOperation : NSOperation
|
98
|
+
{
|
99
|
+
NSString* mdQueryString;
|
100
|
+
NSString* keyToSet;
|
101
|
+
}
|
102
|
+
@property (retain) NSString* mdQueryString;
|
103
|
+
@property (retain) NSString* keyToSet;
|
104
|
+
@end
|
105
|
+
|
106
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
//
|
2
|
+
// OpenMetaPrefs.h
|
3
|
+
// adobeFix
|
4
|
+
//
|
5
|
+
// Created by Tom Andersen on 24/04/09.
|
6
|
+
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
#import <Cocoa/Cocoa.h>
|
10
|
+
|
11
|
+
|
12
|
+
@interface OpenMetaPrefs : NSObject {
|
13
|
+
|
14
|
+
}
|
15
|
+
// prefs support - this allows a common set of recently entered tags to be kept:
|
16
|
+
// recently entered tags support:
|
17
|
+
+ (NSArray*)recentTags; // an array of NSStrings, sorted by most recently added at the top. Case preserved.
|
18
|
+
|
19
|
+
// this call is how you maintain the list of recent tags. When a user edits a list of tags on a doc, pass in the originals as 'old' and the entire set of changed ones as new.
|
20
|
+
+ (void)updatePrefsRecentTags:(NSArray*)oldTags newTags:(NSArray*)newTags;
|
21
|
+
|
22
|
+
// To be really sure that the prefs loaded correctly for recentTags, you could call this, but calling it at each keystroke as someone types will likely get slow.
|
23
|
+
// note that it is automatically called every few seconds when you call recentTags, so you likely don't need to call this.
|
24
|
+
+ (void)synchPrefs;
|
25
|
+
|
26
|
+
// for app store, etc - you can set the prefs to use your own app only.. DONT call with .plist on the prefs file name..
|
27
|
+
+(void)setPrefsFile:(NSString*)prefsFileToUse;
|
28
|
+
|
29
|
+
@end
|
Binary file
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>BuildMachineOSBuild</key>
|
6
|
+
<string>12C60</string>
|
7
|
+
<key>CFBundleDevelopmentRegion</key>
|
8
|
+
<string>English</string>
|
9
|
+
<key>CFBundleExecutable</key>
|
10
|
+
<string>OpenMeta</string>
|
11
|
+
<key>CFBundleIdentifier</key>
|
12
|
+
<string>com.openmeta.OpenMeta</string>
|
13
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
14
|
+
<string>6.0</string>
|
15
|
+
<key>CFBundleName</key>
|
16
|
+
<string>OpenMeta</string>
|
17
|
+
<key>CFBundlePackageType</key>
|
18
|
+
<string>FMWK</string>
|
19
|
+
<key>CFBundleShortVersionString</key>
|
20
|
+
<string>1.0</string>
|
21
|
+
<key>CFBundleSignature</key>
|
22
|
+
<string>????</string>
|
23
|
+
<key>CFBundleVersion</key>
|
24
|
+
<string>1</string>
|
25
|
+
<key>DTCompiler</key>
|
26
|
+
<string></string>
|
27
|
+
<key>DTPlatformBuild</key>
|
28
|
+
<string>4G2008a</string>
|
29
|
+
<key>DTPlatformVersion</key>
|
30
|
+
<string>GM</string>
|
31
|
+
<key>DTSDKBuild</key>
|
32
|
+
<string>12C37</string>
|
33
|
+
<key>DTSDKName</key>
|
34
|
+
<string>macosx10.8</string>
|
35
|
+
<key>DTXcode</key>
|
36
|
+
<string>0452</string>
|
37
|
+
<key>DTXcodeBuild</key>
|
38
|
+
<string>4G2008a</string>
|
39
|
+
</dict>
|
40
|
+
</plist>
|
data/lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/en.lproj/InfoPlist.strings
ADDED
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Openmeta
|
2
|
+
def self.with_friendly_errors
|
3
|
+
begin
|
4
|
+
yield
|
5
|
+
rescue Openmeta::OpenmetaError => e
|
6
|
+
Openmeta.ui.error e.message
|
7
|
+
Openmeta.ui.debug e.backtrace.join("\n")
|
8
|
+
exit e.status_code
|
9
|
+
rescue Interrupt => e
|
10
|
+
Openmeta.ui.error "\nQuitting..."
|
11
|
+
Openmeta.ui.debug e.backtrace.join("\n")
|
12
|
+
exit 1
|
13
|
+
rescue SystemExit => e
|
14
|
+
exit e.status
|
15
|
+
rescue Exception => e
|
16
|
+
Openmeta.ui.error(
|
17
|
+
"Unfortunately, a fatal error has occurred. Please see the Openmeta \n" \
|
18
|
+
"troubleshooting documentation. Thanks!\n #{e.inspect} #{e.backtrace.join("\n")}\n")
|
19
|
+
raise e
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
data/lib/openmeta/ui.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
module Openmeta
|
2
|
+
class UI
|
3
|
+
def warn(message, newline = nil)
|
4
|
+
end
|
5
|
+
|
6
|
+
def debug(message, newline = nil)
|
7
|
+
end
|
8
|
+
|
9
|
+
def error(message, newline = nil)
|
10
|
+
end
|
11
|
+
|
12
|
+
def info(message, newline = nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def confirm(message, newline = nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
def debug?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
class Shell < UI
|
23
|
+
attr_writer :shell
|
24
|
+
|
25
|
+
def initialize(shell)
|
26
|
+
@shell = shell
|
27
|
+
@quiet = false
|
28
|
+
@debug = ENV['DEBUG']
|
29
|
+
end
|
30
|
+
|
31
|
+
def info(msg, newline = nil)
|
32
|
+
tell_me(msg, nil, newline) if !@quiet
|
33
|
+
end
|
34
|
+
|
35
|
+
def confirm(msg, newline = nil)
|
36
|
+
tell_me(msg, :green, newline) if !@quiet
|
37
|
+
end
|
38
|
+
|
39
|
+
def warn(msg, newline = nil)
|
40
|
+
tell_me(msg, :yellow, newline)
|
41
|
+
end
|
42
|
+
|
43
|
+
def error(msg, newline = nil)
|
44
|
+
tell_me(msg, :red, newline)
|
45
|
+
end
|
46
|
+
|
47
|
+
def be_quiet!
|
48
|
+
@quiet = true
|
49
|
+
end
|
50
|
+
|
51
|
+
def debug?
|
52
|
+
# needs to be false instead of nil to be newline param to other methods
|
53
|
+
!!@debug && !@quiet
|
54
|
+
end
|
55
|
+
|
56
|
+
def debug!
|
57
|
+
@debug = true
|
58
|
+
end
|
59
|
+
|
60
|
+
def debug(msg, newline = nil)
|
61
|
+
tell_me(msg, nil, newline) if debug?
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
# valimism
|
66
|
+
def tell_me(msg, color = nil, newline = nil)
|
67
|
+
newline.nil? ? @shell.say(msg, color) : @shell.say(msg, color, newline)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/lib/openmeta.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env macruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
def is_macruby?
|
5
|
+
begin
|
6
|
+
MACRUBY_VERSION
|
7
|
+
return true
|
8
|
+
rescue
|
9
|
+
return false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
if is_macruby?
|
14
|
+
($LOAD_PATH << File.expand_path("../openmeta/framework", __FILE__)).uniq!
|
15
|
+
framework "OpenMeta"
|
16
|
+
else
|
17
|
+
raise NotImplementedError, "[Openmeta] only work with macruby"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'openmeta/version'
|
21
|
+
require 'openmeta/ui'
|
22
|
+
module Openmeta
|
23
|
+
class OpenmetaError < RuntimeError
|
24
|
+
def self.status_code(code)
|
25
|
+
define_method(:status_code) { code }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ObjCError < OpenmetaError; status_code(1) ; end
|
30
|
+
class RangeError < OpenmetaError; status_code(2) ; end
|
31
|
+
class NoMethodError < OpenmetaError; status_code(13) ; end
|
32
|
+
class PathError < OpenmetaError; status_code(14) ; end
|
33
|
+
class DslError < OpenmetaError; status_code(15) ; end
|
34
|
+
|
35
|
+
|
36
|
+
class << self
|
37
|
+
attr_writer :ui, :error
|
38
|
+
|
39
|
+
def ui
|
40
|
+
@ui ||= UI.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def error
|
44
|
+
@error ||= error = Pointer.new(:id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_tags(path)
|
48
|
+
tags = OpenMeta.getUserTags(File.expand_path(path), error:error)
|
49
|
+
if error.value
|
50
|
+
raise Openmeta::ObjCError, error.value.inspect
|
51
|
+
end
|
52
|
+
tags
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_tags(tags, path)
|
56
|
+
e = OpenMeta.setUserTags(tags, path:File.expand_path(path))
|
57
|
+
if e
|
58
|
+
raise Openmeta::ObjCError, e.inspect
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_rating(path)
|
63
|
+
rating = OpenMeta.getRating(File.expand_path(path), error:error)
|
64
|
+
if error.value
|
65
|
+
raise Openmeta::ObjCError, error.value.inspect
|
66
|
+
end
|
67
|
+
rating
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_rating(rating, path)
|
71
|
+
e = OpenMeta.setRating(rating, path:File.expand_path(path))
|
72
|
+
if e
|
73
|
+
raise Openmeta::ObjCError, e.inspect
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
|
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: openmeta.rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Zhao Cai
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.17.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 0.17.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rdoc
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '3.10'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '3.10'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: hoe
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.5'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.5'
|
62
|
+
description: openMeta in Ruby (MacRuby, more exactly!)
|
63
|
+
email:
|
64
|
+
- caizhaoff@gmail.com
|
65
|
+
executables:
|
66
|
+
- tag
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files:
|
69
|
+
- History.txt
|
70
|
+
- Manifest.txt
|
71
|
+
files:
|
72
|
+
- History.txt
|
73
|
+
- Manifest.txt
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- bin/tag
|
77
|
+
- lib/openmeta.rb
|
78
|
+
- lib/openmeta/cli.rb
|
79
|
+
- lib/openmeta/framework/OpenMeta.framework/OpenMeta
|
80
|
+
- lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMeta.h
|
81
|
+
- lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMetaBackup.h
|
82
|
+
- lib/openmeta/framework/OpenMeta.framework/Versions/A/Headers/OpenMetaPrefs.h
|
83
|
+
- lib/openmeta/framework/OpenMeta.framework/Versions/A/OpenMeta
|
84
|
+
- lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/Info.plist
|
85
|
+
- lib/openmeta/framework/OpenMeta.framework/Versions/A/Resources/en.lproj/InfoPlist.strings
|
86
|
+
- lib/openmeta/friendly_errors.rb
|
87
|
+
- lib/openmeta/ui.rb
|
88
|
+
- lib/openmeta/version.rb
|
89
|
+
homepage: https://github.com/zhaocai/openmeta.rb
|
90
|
+
licenses: []
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options:
|
93
|
+
- --main
|
94
|
+
- README.md
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ! '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
requirements: []
|
110
|
+
rubyforge_project: openmeta.rb
|
111
|
+
rubygems_version: 1.8.24
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: openMeta in Ruby (MacRuby, more exactly!)
|
115
|
+
test_files: []
|