Skip to content

Instantly share code, notes, and snippets.

@uvlad7
Last active June 18, 2023 05:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uvlad7/0048d65d288dfe71ee1bb60fcc587e3a to your computer and use it in GitHub Desktop.
Save uvlad7/0048d65d288dfe71ee1bb60fcc587e3a to your computer and use it in GitHub Desktop.
$ sha256sum test_*
9827714b99613aee8458127d590345dd695a869ac75036038d4aca1597734e84 test_gir_ffi_with_save_to_file.desktop
9827714b99613aee8458127d590345dd695a869ac75036038d4aca1597734e84 test_gir_ffi_with_to_data.desktop
9827714b99613aee8458127d590345dd695a869ac75036038d4aca1597734e84 test_glib_with_load_from_data.desktop
9827714b99613aee8458127d590345dd695a869ac75036038d4aca1597734e84 test_glib_with_load_from_file.desktop
2ab4b687dec232b5ca5bba316bd7fa27fbf670a01b3e0c37b05d1179876a630e test_inifile.desktop
9827714b99613aee8458127d590345dd695a869ac75036038d4aca1597734e84 test_iniparse.desktop
$group = 'Desktop Entry'
$keys = ['Icon', 'Name']
$new_file_name = 'new-jetbrains-rubymine.desktop'
$old_file_name = 'old-jetbrains-rubymine.desktop'
gem 'gir_ffi', '0.15.9' # just for reproductivity
require 'gir_ffi'
# I didn't find how to have both this and glib2 in the same process, it raises
# The module GLib was already defined elsewhere (RuntimeError)
# And I'm afraid shared libraries may conflict
GirFFI.setup :GLib
def test_gir_ffi_with_to_data
# not the same as in glib2
new_file = GLib::KeyFile.new
# flags were specified implicitly by glib2, but in native code the default value is 0
new_file.load_from_file($new_file_name, GLib::KeyFileFlags::KEEP_COMMENTS | GLib::KeyFileFlags::KEEP_TRANSLATIONS)
old_file = GLib::KeyFile.new
old_file.load_from_file($old_file_name, GLib::KeyFileFlags::KEEP_COMMENTS | GLib::KeyFileFlags::KEEP_TRANSLATIONS)
# but this is not changed
$keys.each { |key| new_file.set_string($group, key, old_file.get_string($group, key)) }
File.open('test_gir_ffi_with_to_data.desktop', 'w') do |out|
# now new_file.to_data returns two values (string and length)
out.write new_file.to_data.first
end
end
def test_gir_ffi_with_save_to_file
# not the same as in glib2
new_file = GLib::KeyFile.new
# flags were specified implicitly by glib2, but in native code the default value is 0
new_file.load_from_file($new_file_name, GLib::KeyFileFlags::KEEP_COMMENTS | GLib::KeyFileFlags::KEEP_TRANSLATIONS)
old_file = GLib::KeyFile.new
old_file.load_from_file($old_file_name, GLib::KeyFileFlags::KEEP_COMMENTS | GLib::KeyFileFlags::KEEP_TRANSLATIONS)
# but this is not changed
$keys.each { |key| new_file.set_string($group, key, old_file.get_string($group, key)) }
new_file.save_to_file('test_gir_ffi_with_save_to_file.desktop')
end
test_gir_ffi_with_to_data
test_gir_ffi_with_save_to_file
$group = 'Desktop Entry'
$keys = ['Icon', 'Name']
$new_file_name = 'new-jetbrains-rubymine.desktop'
$old_file_name = 'old-jetbrains-rubymine.desktop'
gem 'glib2', '4.1.7' # just for reproductivity
require 'glib2'
def test_glib_with_load_from_file
new_file = GLib::KeyFile.new
new_file.load_from_file($new_file_name)
old_file = GLib::KeyFile.new
old_file.load_from_file($old_file_name)
$keys.each { |key| new_file.set_string($group, key, old_file.get_string($group, key)) }
# the binding doesn't expose save_to_file, so we have no choise
File.open('test_glib_with_load_from_file.desktop', 'w') do |out|
out.write new_file.to_data
end
end
def test_glib_with_load_from_data
new_file = GLib::KeyFile.new
new_file.load_from_file($new_file_name)
old_file = GLib::KeyFile.new
old_file.load_from_data(File.read($old_file_name))
$keys.each { |key| new_file.set_string($group, key, old_file.get_string($group, key)) }
# the binding doesn't expose save_to_file, so we have no choise
File.open('test_glib_with_load_from_data.desktop', 'w') do |out|
out.write new_file.to_data
end
end
test_glib_with_load_from_file
test_glib_with_load_from_data
# 3.0.0 just crashes because of the Exec value with quotes
# 9: from /path/gems/inifile-3.0.0/lib/inifile.rb:31:in `load'
# 8: from /path/gems/inifile-3.0.0/lib/inifile.rb:31:in `new'
# 7: from /path/gems/inifile-3.0.0/lib/inifile.rb:80:in `initialize'
# 6: from /path/gems/inifile-3.0.0/lib/inifile.rb:128:in `read'
# 5: from /path/gems/inifile-3.0.0/lib/inifile.rb:128:in `open'
# 4: from /path/gems/inifile-3.0.0/lib/inifile.rb:128:in `block in read'
# 3: from /path/gems/inifile-3.0.0/lib/inifile.rb:400:in `parse'
# 2: from /path/gems/inifile-3.0.0/lib/inifile.rb:540:in `parse'
# 1: from /path/gems/inifile-3.0.0/lib/inifile.rb:578:in `error'
# IniFile::Error (Unmatched open quote: "StartupNotify=true")
gem 'inifile', '2.0.2'
require 'inifile'
def test_inifile
new_file = IniFile.load($new_file_name)
old_file = IniFile.load($old_file_name)
$keys.each { |key| new_file[$group][key] = old_file[$group][key] }
new_file.save(filename: 'test_inifile.desktop')
end
test_inifile
gem 'iniparse', '1.5.0' # just for reproductivity
require 'iniparse'
def test_iniparse
new_file = IniParse.parse(File.read($new_file_name))
old_file = IniParse.parse(File.read($old_file_name))
$keys.each { |key| new_file[$group][key] = old_file[$group][key] }
File.write('test_iniparse.desktop', new_file.to_ini)
end
test_iniparse
[Desktop Entry]
Name=RubyMine 2023.1.2
Icon=/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/.icon.svg
StartupWMClass=jetbrains-rubymine
Comment=A Ruby and Rails IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh" %u
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
[Desktop Entry]
Name=RubyMine
Icon=rubymine
StartupWMClass=jetbrains-rubymine
Comment=The most intelligent Ruby IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/203.6682.166/bin/rubymine.sh" %f
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
#!/usr/bin/env ruby
require 'fileutils'
# gem 'inifile', '2.0.2'
# require 'inifile'
require 'hashdiff'
require 'awesome_print'
require 'active_support'
require 'active_support/core_ext'
require 'tty-prompt'
DESKTOP = 'Desktop'
APPLICATIONS = '.local/share/applications'
MASK = 'jetbrains-*.desktop'
require 'pry'
require 'colorize'
# Dependencies
# https://github.com/ruby-gnome/ruby-gnome/blob/master/glib2/README.md
# On linux for example
# https://www.gtk.org/docs/installations/linux/
# sudo apt install libgtk-4-dev
# Actually, glib2 [requires](https://github.com/ruby-gnome/ruby-gnome/blob/master/glib2/ext/glib2/extconf.rb#LL29C8-L29C35) libglib2.0-dev, which libgtk-4-dev depends on
# And you don't have to install it manually, as glib2 uses [mkmf-gnome](https://github.com/ruby-gnome/ruby-gnome/blob/master/glib2/lib/mkmf-gnome.rb),
# and it tries to [install missing native packages](https://github.com/ruby-gnome/ruby-gnome/blob/master/glib2/lib/mkmf-gnome.rb#LL480C23-L480C53)
require 'glib2'
# https://docs.gtk.org/glib/struct.KeyFile.html
# https://github.com/ruby-gnome/ruby-gnome/blob/master/glib2/sample/keyfile.rb
class IniFile < GLib::KeyFile
class Section
def initialize(kf, group)
@kf = kf
@group = group
end
def [](key)
@kf.get_string(@group, key) if @kf.has_group?(@group)
end
def []=(key, value)
@kf.set_string(@group, key, value)
end
def to_h
res = {}
get_keys(@group).each do |key|
res[@group][key] = get_string(@group, key)
end if @kf.has_group?(@group)
res
end
end
def self.load(filename, opts = {})
return unless File.file? filename
new(opts.merge(:filename => filename))
end
attr_accessor :filename
def initialize(content = nil, opts = {})
super()
opts, content = content, nil if Hash === content
@filename = opts.fetch(:filename, nil)
if @content
parse(content)
elsif @filename
read
end
end
def read(opts = {})
filename = opts.fetch(:filename, @filename)
load_from_file(filename) if File.file? filename
self
end
alias restore read
def write(opts = {})
filename = opts.fetch(:filename, @filename)
File.open(filename, 'w') do |f|
f.write to_data
end
self
end
alias save write
alias to_s to_data
def to_h
res = {}
groups.each do |group|
res[group] = {}
get_keys(group).each do |key|
res[group][key] = get_string(group, key)
end
end
res
end
def [](group)
@groups ||= {}
@groups[group] ||= Section.new(self, group)
@groups[group]
end
private
def parse(content)
return unless content
load_from_data content
end
end
prompt = TTY::Prompt.new
desktop_files = Dir[File.join(Dir.home, DESKTOP, MASK)].map { |file| [File.basename(file), file] }.to_h
applications_files = Dir[File.join(Dir.home, APPLICATIONS, MASK)].map { |file| [File.basename(file), file] }.to_h
(desktop_files.keys & applications_files.keys).each do |filename|
applications_file = IniFile.load(applications_files[filename])
new_data = applications_file['Desktop Entry']
desktop_file = IniFile.load(desktop_files[filename])
old_data = desktop_file['Desktop Entry']
new_data['Icon'] = old_data['Icon'] if old_data['Icon']
new_data['Name'] = old_data['Name'] if old_data['Name']
diff = Hashdiff.diff desktop_file.to_h, applications_file.to_h
next if diff.empty?
puts "Syncing #{applications_file.filename} ==> #{desktop_file.filename}"
diff.select! { |type, key, *val| type != '~' || key != 'Desktop Entry.Exec' }
if diff.empty?
applications_file.save(filename: desktop_file.filename)
else
puts 'Diff:'.red
ap diff
unless prompt.no?('Overwrite desktop file?')
applications_file.save(filename: desktop_file.filename)
end
end
end
[Desktop Entry]
Name=RubyMine
Icon=rubymine
StartupWMClass=jetbrains-rubymine
Comment=A Ruby and Rails IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh" %u
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
[Desktop Entry]
Name=RubyMine
Icon=rubymine
StartupWMClass=jetbrains-rubymine
Comment=A Ruby and Rails IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh" %u
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
[Desktop Entry]
Name=RubyMine
Icon=rubymine
StartupWMClass=jetbrains-rubymine
Comment=A Ruby and Rails IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh" %u
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
[Desktop Entry]
Name=RubyMine
Icon=rubymine
StartupWMClass=jetbrains-rubymine
Comment=A Ruby and Rails IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh" %u
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
[Desktop Entry]
Name = RubyMine
Icon = rubymine
StartupWMClass = jetbrains-rubymine
Comment = A Ruby and Rails IDE
Exec = /home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh %u
Version = 1.0
Type = Application
Categories = Development
Terminal = false
StartupNotify = true
[Desktop Entry]
Name=RubyMine
Icon=rubymine
StartupWMClass=jetbrains-rubymine
Comment=A Ruby and Rails IDE
Exec="/home/vladimir/.local/share/JetBrains/Toolbox/apps/RubyMine/ch-0/231.9011.41/bin/rubymine.sh" %u
Version=1.0
Type=Application
Categories=Development;IDE;
Terminal=false
StartupNotify=true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment