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 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
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby -roptparse -rpathname -ryaml
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require "tm_bundle_support"
7
+ require "tm_bundle/command"
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: