openmeta.rb 1.0.0
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/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: []
|