tm_bundle_support 0.0.1
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.
- 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:
|