tm_bundle_support 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/tm_bundle +7 -0
- data/lib/tm_bundle.rb +107 -0
- data/lib/tm_bundle/command.rb +36 -0
- data/lib/tm_bundle/item.rb +68 -0
- data/lib/tm_bundle/menu.rb +90 -0
- data/lib/tm_bundle/plutil.rb +112 -0
- data/lib/tm_bundle_support.rb +1 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 18b7a29af1e0c9e5131802a604c3d59e665effab
|
4
|
+
data.tar.gz: 61c2739bce6ad29a9d58ff1dbe131d6baa3be8a4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59788c4fd619fe1fb0579a6190b2d7dd0ce08ed343113e23c52fb5adf741c219454b61a4e60644c1e927839cbfe590632a7e9c3b75d0fcc7563ba799f2d2e3be
|
7
|
+
data.tar.gz: 8fca42f07ef672b1f062f974bdbf37efaeb093330d9fae339605fc4b2eed63c3bad3170db390dfaf5ab008255d3ecc8d031f7faa8344951ced349bd798d38476
|
data/bin/tm_bundle
ADDED
data/lib/tm_bundle.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext"
|
3
|
+
require "active_support/dependencies/autoload"
|
4
|
+
require "tm_bundle/plutil"
|
5
|
+
|
6
|
+
class TMBundle
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
|
9
|
+
eager_autoload do
|
10
|
+
autoload :Menu, "tm_bundle/menu"
|
11
|
+
end
|
12
|
+
|
13
|
+
class Action
|
14
|
+
attr_accessor :path, :data
|
15
|
+
def initialize(path)
|
16
|
+
@path = path
|
17
|
+
@data = Plutil.load_json(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"#{self.class.name}:> #{data['name']} > `#{path.basename}`"
|
22
|
+
end
|
23
|
+
|
24
|
+
def expected_path
|
25
|
+
@expected_path ||= data['name'].gsub(/[.:\/]+/, '.' => '_', ':' => '', '/' => '') + path.extname
|
26
|
+
end
|
27
|
+
|
28
|
+
def unmatching_path?
|
29
|
+
expected_path != path.basename.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def method_missing(meth, *args, &blk)
|
33
|
+
meth_name = meth.to_s
|
34
|
+
if @data.key?(meth_name)
|
35
|
+
@data[meth_name]
|
36
|
+
else
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
class Command < Action; end
|
42
|
+
class Snippet < Action; end
|
43
|
+
class Macro < Action; end
|
44
|
+
|
45
|
+
attr_accessor :path, :actions, :data, :menu
|
46
|
+
|
47
|
+
def initialize(path = nil)
|
48
|
+
self.path = path ? Pathname.new(path) : Pathname.pwd
|
49
|
+
self.data = Plutil.load_json(@path/'info.plist')
|
50
|
+
self.menu = Menu.new(data['mainMenu'], @path)
|
51
|
+
self.actions = []
|
52
|
+
locate_actions
|
53
|
+
fill_missing_menu_names!
|
54
|
+
# menu.write_yaml_hierarchy!
|
55
|
+
end
|
56
|
+
|
57
|
+
def locate_actions
|
58
|
+
Pathname.glob("#{path}/{Commands,Snippets,Macros}/*.{plist,tm{Command,Snippet}}")
|
59
|
+
.group_by {|p| p.dirname.basename.to_s }
|
60
|
+
.each do |group, files|
|
61
|
+
# p group.downcase.to_sym, actions
|
62
|
+
files.each do |pathname|
|
63
|
+
actions << TMBundle.const_get(group.singularize.to_sym).new(pathname)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def fix_names!
|
69
|
+
actions.select(&:unmatching_path?).each do |action|
|
70
|
+
action.path.rename(action.path.dirname/action.expected_path)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def fill_missing_menu_names!
|
75
|
+
actions.each do |action|
|
76
|
+
if item = menu.uuids[action.uuid]
|
77
|
+
item.name ||= action.name
|
78
|
+
else
|
79
|
+
Menu.find_or_create_new_item(action.uuid, 0, name: action.name)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def menu_export!(prefix)
|
85
|
+
fill_missing_menu_names!
|
86
|
+
menu.write_yaml_hierarchy!(prefix && "-#{prefix}")
|
87
|
+
end
|
88
|
+
|
89
|
+
def save_plist!(type = :default)
|
90
|
+
fill_missing_menu_names!
|
91
|
+
|
92
|
+
file = case type
|
93
|
+
when :last then Pathname.glob((path/"menu-*.yml").to_s).last
|
94
|
+
when String then path/"menu-#{type}.yml"
|
95
|
+
else path/"menu.yml" end
|
96
|
+
|
97
|
+
hash = menu.read_yaml_hieararchy! file
|
98
|
+
xml = process_xml_output Plutil::JSON.dump(hash)
|
99
|
+
puts Plutil.replace(@path/'info.plist', 'mainMenu', xml, &:read)
|
100
|
+
end
|
101
|
+
|
102
|
+
def process_xml_output(xml)
|
103
|
+
xml.lines[3...-1]
|
104
|
+
.join
|
105
|
+
.gsub(/(?<=$\n|\t)\t/, "\n\t" => "\n", "\t" => " ")
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# params[:a] = true # -a
|
2
|
+
# params[:b] = "1" # -b1
|
3
|
+
# params[:foo] = "1" # --foo
|
4
|
+
# params[:bar] = "x" # --bar x
|
5
|
+
# params[:zot] = "z" # --zot Z
|
6
|
+
|
7
|
+
def start
|
8
|
+
TMBundle.eager_load!
|
9
|
+
@tmb = TMBundle.new
|
10
|
+
yield
|
11
|
+
end
|
12
|
+
|
13
|
+
case ARGV.shift
|
14
|
+
when "fix-names"
|
15
|
+
start {@tmb.fix_names!}
|
16
|
+
when "menu"
|
17
|
+
case ARGV.shift
|
18
|
+
when "export"
|
19
|
+
params = ARGV.getopts("fp:")
|
20
|
+
prefix = params['p'] || Time.now.to_formatted_s(:number) unless params['f']
|
21
|
+
start{
|
22
|
+
p params
|
23
|
+
@tmb.menu_export!(prefix)
|
24
|
+
}
|
25
|
+
when "apply"
|
26
|
+
start {
|
27
|
+
params = ARGV.getopts("v:", "last")
|
28
|
+
type = params['last'] || params['v']
|
29
|
+
@tmb.save_plist! type
|
30
|
+
}
|
31
|
+
else
|
32
|
+
puts " (export,apply)"
|
33
|
+
end
|
34
|
+
else
|
35
|
+
puts " (fix-names,menu)"
|
36
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class TMBundle
|
2
|
+
class Menu
|
3
|
+
class Item
|
4
|
+
attr_accessor :uuid, :name, :items, :exclude, :parent, :order
|
5
|
+
def initialize(uuid, order, name: nil, items: [], parent: nil)
|
6
|
+
@exclude = false
|
7
|
+
self.uuid = uuid
|
8
|
+
self.name = name
|
9
|
+
self.items = items
|
10
|
+
self.parent = parent
|
11
|
+
self.order = order
|
12
|
+
end
|
13
|
+
|
14
|
+
def checkout(options = {})
|
15
|
+
self.name ||= options[:name]
|
16
|
+
self.parent ||= options[:parent]
|
17
|
+
self.order ||= options[:order]
|
18
|
+
self.items = Array.wrap(options[:items])
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def items=(uuids)
|
23
|
+
@items = process_items(uuids)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_items(uuids)
|
27
|
+
uuids.map.with_index{|id, index| Menu.find_or_create_new_item(id, index, parent: uuid) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def eql?(other)
|
31
|
+
uuid == other.uuid
|
32
|
+
end
|
33
|
+
|
34
|
+
def inspect
|
35
|
+
"#{order||:nil}> * #{name || uuid}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def name?
|
39
|
+
name.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def items; @items ||= []; end
|
43
|
+
def to_s; name; end
|
44
|
+
def root?; !@parent; end
|
45
|
+
def separator?; false; end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Separator < Item
|
49
|
+
def inspect
|
50
|
+
"#{order}> — #{name}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(uuid, order, options = {})
|
54
|
+
@exclude = false
|
55
|
+
self.uuid = uuid
|
56
|
+
self.name = uuid
|
57
|
+
self.order = order
|
58
|
+
self.parent = options[:parent]
|
59
|
+
end
|
60
|
+
|
61
|
+
def eql?
|
62
|
+
order == order && parent == parent
|
63
|
+
end
|
64
|
+
|
65
|
+
def separator?; true end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
class TMBundle
|
2
|
+
class Menu
|
3
|
+
extend ActiveSupport::Autoload
|
4
|
+
|
5
|
+
eager_autoload do
|
6
|
+
autoload :Item, "tm_bundle/item"
|
7
|
+
autoload :Separator, "tm_bundle/item"
|
8
|
+
end
|
9
|
+
|
10
|
+
cattr_accessor(:uuids){ Hash[] }
|
11
|
+
cattr_accessor(:items){ Set.new }
|
12
|
+
|
13
|
+
def self.find_or_create_new_item(uuid, order, attributes = {})
|
14
|
+
item = if uuid.squeeze == '-'
|
15
|
+
Separator.new(uuid, order, attributes)
|
16
|
+
elsif exists?(uuid)
|
17
|
+
uuids[uuid].checkout(attributes.to_h)
|
18
|
+
else
|
19
|
+
Item.new(uuid, order, attributes.to_h)
|
20
|
+
end
|
21
|
+
|
22
|
+
items.add item if item.is_a?(Item) && item.root?
|
23
|
+
unless item.is_a? Separator
|
24
|
+
uuids[uuid] ||= item
|
25
|
+
end
|
26
|
+
item
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.exists?(uuid)
|
30
|
+
uuids.key? uuid
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(tree, path)
|
34
|
+
@path = path
|
35
|
+
submenus, items, @excludedItems = *tree.values_at("submenus", "items", "excludedItems")
|
36
|
+
process_subtree(submenus)
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_subtree(submenus)
|
40
|
+
submenus.each do |uuid, hash|
|
41
|
+
self.class.find_or_create_new_item(uuid, submenus.keys.index(uuid), hash.symbolize_keys)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def write_yaml_hierarchy!(prefix = nil)
|
46
|
+
p prefix
|
47
|
+
tree = make_hash_tree! uuids.values.select(&:root?)
|
48
|
+
yaml = tree.to_yaml.gsub(/^(.*)\s*$\n\s*:uuid:(.*)$/){|s,n| "%-60s # %s" % [$1, $2] }
|
49
|
+
(Pathname.new(@path)/"menu#{prefix}.yml").write(yaml)
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_yaml_hieararchy!(file)
|
53
|
+
yaml = file.read
|
54
|
+
yaml.gsub!(/^(\s*)(.*)\s*#\s*(.*)$/){|s,n| "#{$1}%s \n#{$1} :uuid: %s" % [$2, $3] }
|
55
|
+
prepare_items_to_plist YAML.load(yaml)
|
56
|
+
end
|
57
|
+
|
58
|
+
def make_hash_tree!(items)
|
59
|
+
items.reduce(Hash[]) do |m, item|
|
60
|
+
hash = { uuid: item.uuid }
|
61
|
+
hash[:items] = make_hash_tree!(item.items) if item.items.any?
|
62
|
+
|
63
|
+
m.merge! item.name => hash
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def prepare_items_to_plist(hash)
|
68
|
+
@menu = { submenus: {}, items: [], excludedItems: Array.wrap(@excludedItems) }
|
69
|
+
@menu[:items] = Array.wrap(iterate_to_plist(hash))
|
70
|
+
@menu
|
71
|
+
end
|
72
|
+
|
73
|
+
def iterate_to_plist(hash, parent = nil)
|
74
|
+
ary = []
|
75
|
+
hash.each do |name, tree|
|
76
|
+
uuid = tree[:uuid]
|
77
|
+
if tree[:items]
|
78
|
+
ary << uuid
|
79
|
+
@menu[:submenus][uuid] = {
|
80
|
+
:items => iterate_to_plist(tree[:items]),
|
81
|
+
:name => name
|
82
|
+
}
|
83
|
+
else
|
84
|
+
ary << uuid
|
85
|
+
end
|
86
|
+
end
|
87
|
+
ary
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class Plutil
|
4
|
+
module JSON
|
5
|
+
def self.load(plist)
|
6
|
+
Plutil.convert plist, to: :json do |converted_io|
|
7
|
+
::JSON.load(converted_io)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.call(*args)
|
12
|
+
load(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.dump(object, options = {})
|
16
|
+
Plutil.convert :stdin, options.reverse_merge(to: :xml) do |io|
|
17
|
+
io.write ::JSON.dump(object.to_h)
|
18
|
+
io.close_write
|
19
|
+
io.read
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Also aliased as `Plutil.plutil`
|
25
|
+
# Usage:
|
26
|
+
#
|
27
|
+
# Plutil.(:remove, "keypath", file: plist, &:read)
|
28
|
+
# Plutil.(:extract, "keypath", :json, file: plist, &:read)
|
29
|
+
#
|
30
|
+
def self.call(*args, &block)
|
31
|
+
new(*args).execute(&block)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Usage:
|
35
|
+
#
|
36
|
+
# Plutil.insert('keypath', "-bool", "YES", file: plist, &:read)
|
37
|
+
#
|
38
|
+
class << self
|
39
|
+
alias_method :plutil, :call
|
40
|
+
|
41
|
+
[:remove, :extract, :insert].each do |command|
|
42
|
+
define_method(command) {|*args, &block| plutil(command, *args, &block) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Usage:
|
47
|
+
#
|
48
|
+
# Plutil.convert plist, to: 'json' do |converted_io|
|
49
|
+
# JSON.load(converted_io)
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
def self.convert(path, to: :json, &block)
|
53
|
+
to = "xml1" if to.to_s == 'xml'
|
54
|
+
plutil(:convert, to, out: :stdin, file: path.to_s, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Usage:
|
58
|
+
#
|
59
|
+
# Plutil.replace(plist, 'name', data, as: 'xml', &:read)
|
60
|
+
#
|
61
|
+
def self.replace(path, keypath, data, as: :xml, &block)
|
62
|
+
plutil(:replace, keypath, "-#{as}", data, file: path.to_s, &block)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Shorthand to `Plutil::JSON.load(plist)`
|
66
|
+
def self.load_json(*args)
|
67
|
+
Plutil::JSON.load(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.dump_json(*args)
|
71
|
+
Plutil::JSON.dump(*args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def initialize(*args)
|
75
|
+
options = args.extract_options!
|
76
|
+
@command, *@args = *args
|
77
|
+
@in, @out, @mode = *options.values_at(:in, :out, :mode)
|
78
|
+
@in ||= options[:file]
|
79
|
+
@mode ||= auto_mode
|
80
|
+
end
|
81
|
+
|
82
|
+
def execute(&block)
|
83
|
+
io = IO.popen(cmd, @mode)
|
84
|
+
block.call(io)
|
85
|
+
ensure
|
86
|
+
io.close if io && !io.closed?
|
87
|
+
end
|
88
|
+
|
89
|
+
def output_args; @out && ['-o', normalize_io_arg(@out)]; end
|
90
|
+
def input_args; @in && ['--', normalize_io_arg(@in)]; end
|
91
|
+
def stdin?; @in.to_s == 'stdin'; end
|
92
|
+
|
93
|
+
private
|
94
|
+
def cmd
|
95
|
+
['plutil', "-#@command"] + Array(@args.map(&:to_s)) + Array(output_args) + Array(input_args)
|
96
|
+
end
|
97
|
+
|
98
|
+
def normalize_io_arg(io)
|
99
|
+
case io.to_s
|
100
|
+
when 'stdin', 'stdout' then '-'
|
101
|
+
else io
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def auto_mode
|
106
|
+
case @command
|
107
|
+
when :convert then stdin? ? 'w+' : 'r'
|
108
|
+
when :replace then 'w+'
|
109
|
+
else 'r'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "tm_bundle"
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tm_bundle_support
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tōnis Simo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '4.2'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '4.2'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.7'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '10.0'
|
61
|
+
description: Helpful ruby scripts for textmate bundle developers.
|
62
|
+
email:
|
63
|
+
- anton.estum@gmail.com
|
64
|
+
executables:
|
65
|
+
- tm_bundle
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- bin/tm_bundle
|
70
|
+
- lib/tm_bundle.rb
|
71
|
+
- lib/tm_bundle/command.rb
|
72
|
+
- lib/tm_bundle/item.rb
|
73
|
+
- lib/tm_bundle/menu.rb
|
74
|
+
- lib/tm_bundle/plutil.rb
|
75
|
+
- lib/tm_bundle_support.rb
|
76
|
+
homepage: http://github.com/estum/tm_bundle_support
|
77
|
+
licenses:
|
78
|
+
- MIT
|
79
|
+
metadata: {}
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.1'
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 2.4.5
|
97
|
+
signing_key:
|
98
|
+
specification_version: 4
|
99
|
+
summary: Textmate Bundle Support Tools
|
100
|
+
test_files: []
|
101
|
+
has_rdoc:
|