├── .gemspec ├── README.md ├── bin └── mgem ├── lib └── mgem.rb └── zsh └── _mgem /.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = 'mgem' 3 | s.version = '0.3.0' 4 | s.summary = 'A program to manage GEMs for mruby.' 5 | s.description = 'mgem helps you search and find GEMs specifically written for mruby. It also supports by creating a mruby build configuration.' 6 | s.author = 'Daniel Bovensiepen' 7 | s.email = 'daniel@bovensiepen.net' 8 | s.files = ['bin/mgem', 'lib/mgem.rb'] 9 | s.bindir = 'bin' 10 | s.executables << 'mgem' 11 | s.homepage = 'http://github.com/bovi/mgem' 12 | s.license = 'MIT' 13 | end 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mgem 2 | ======= 3 | 4 | A program to manage GEMs for mruby. 5 | 6 | See http://mruby.sh/201301040627.html for an introduction. 7 | -------------------------------------------------------------------------------- /bin/mgem: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "#{File.dirname(__FILE__)}/../lib/mgem" 4 | extend Mrbgem 5 | 6 | main_cmd = ARGV[0] 7 | arg1 = ARGV[1] 8 | arg2 = ARGV[2] 9 | 10 | gems = load_gems unless main_cmd == 'help' 11 | 12 | info_proc = lambda do |obj| 13 | puts " #{obj.name.ljust(30)}#{obj.description}" 14 | end 15 | r = lambda { |o| /^(#{1.upto(o.size).map{|x|o[0...(x)]}.join('|')})$/ } 16 | 17 | detail_info_proc = lambda do |obj| 18 | puts < 'bin-mirb', 64 | 'Interpreter [mruby]' => 'bin-mruby', 65 | 'Compiler [mrbc]' => 'bin-mrbc', 66 | 'Configurator [mruby-config]' => 'bin-mruby-config', 67 | 'Debugger [mrdb]' => 'bin-debugger', 68 | 69 | 'Array Extensions' => 'array-ext', 70 | 'Enumerator' => 'enumerator', 71 | 'Enumerator Lazy' => 'enum-lazy', 72 | 'Enumerator Extensions' => 'enum-ext', 73 | 'eval' => 'eval', 74 | 'exit' => 'exit', 75 | 'Fiber' => 'fiber', 76 | 'Hash Extensions' => 'hash-ext', 77 | 'Kernel Extensions' => 'kernel-ext', 78 | 'Math' => 'math', 79 | 'Numeric Extensions' => 'numeric-ext', 80 | 'Object Extensions' => 'object-ext', 81 | 'ObjectSpace' => 'objectspace', 82 | 'print' => 'print', 83 | 'Proc Extensions' => 'proc-ext', 84 | 'random' => 'random', 85 | 'Range Extensions' => 'range-ext', 86 | 'sprintf' => 'sprintf', 87 | 'String Extensions' => 'string-ext', 88 | 'Struct' => 'struct', 89 | 'Symbol Extensions' => 'symbol-ext', 90 | 'Time' => 'time', 91 | 'Toplevel Extensions' => 'toplevel-ext' 92 | } 93 | 94 | COMPILE_OPTIONS = { 95 | 'bin-debugger' => [ 96 | 'conf.cc.defines = %w(MRB_ENABLE_DEBUG_HOOK)' 97 | ], 98 | 'test' => [ 99 | 'conf.enable_bintest', 100 | 'conf.enable_test' 101 | ], 102 | 'debug' => [ 103 | 'enable_debug' 104 | ] 105 | } 106 | 107 | DEFAULT_NO = ['bin-debugger', 'bin-mruby-config'] 108 | 109 | def get_default_gems 110 | puts 111 | gems = [] 112 | CORE_GEMS.each do |gem, path| 113 | y_n = 'Y/n' 114 | y_n = 'y/N' if DEFAULT_NO.include? path 115 | print "Include '#{gem}' GEM? (#{y_n}): " 116 | question = STDIN.gets.chomp 117 | 118 | if DEFAULT_NO.include? path 119 | if question == 'y' 120 | gems << path 121 | end 122 | else 123 | unless question == 'n' 124 | gems << path 125 | end 126 | end 127 | end 128 | gems 129 | end 130 | 131 | case main_cmd 132 | when r.('info'), r.('detail') 133 | puts "Detail information of '#{arg1}':" 134 | gems.search(arg1, :name).each {|gem| detail_info_proc.call(gem) } 135 | when r.('size') 136 | puts "Total # of GEMs: #{gems.size}" 137 | when r.('list') 138 | if arg1 =~ r.('active') 139 | list_active_gems(gems) 140 | else 141 | puts "List of all GEMs:" 142 | gems.each { |gem| info_proc.call(gem) } 143 | end 144 | when r.('add') 145 | gems.activate(arg1) 146 | puts 147 | list_active_gems(gems) 148 | when r.('rm') 149 | gems.deactivate(arg1) 150 | puts 151 | list_active_gems(gems) 152 | when r.('find'), 'search' 153 | puts "Search result for '#{arg1}':" 154 | gems.search(arg1).each {|gem| info_proc.call(gem) } 155 | when r.('update') 156 | puts 'Update list of GEMs...' 157 | amount = gems.size 158 | gems.update! 159 | gems = load_gems 160 | new_gems = gems.size - amount 161 | if new_gems == 1 162 | puts "1 new GEM!" 163 | elsif new_gems > 1 164 | puts "#{new_gems} new GEMs!" 165 | else 166 | puts "No new GEM" 167 | end 168 | when r.('config') 169 | test_env = ' # Deactivated!' 170 | debug_flags = ' # Deactivated!' 171 | toolchain = 'gcc' 172 | if arg1 =~ r.('default') 173 | default_gems = CORE_GEMS.each_value.to_a 174 | default_gem_command = " conf.gembox 'default'" 175 | else 176 | toolchain = get_toolchain 177 | 178 | puts 179 | print "Build testing environment [mrbtest]? (y/N): " 180 | question = STDIN.gets.chomp 181 | if question =~ r.('yes') 182 | test_env = "#{COMPILE_OPTIONS['test'].map {|o| " #{o}"}.join("\n")}\n" 183 | end 184 | 185 | puts 186 | print "Enable debug flags? (y/N): " 187 | question = STDIN.gets.chomp 188 | if question =~ r.('yes') 189 | debug_flags = "#{COMPILE_OPTIONS['debug'].map {|o| " #{o}"}.join("\n")}\n" 190 | end 191 | 192 | default_gem_command = get_default_gems.map do |g| 193 | opt = '' 194 | if COMPILE_OPTIONS.include? g 195 | opt = "#{COMPILE_OPTIONS[g].map {|o| " #{o}"}.join("\n")}\n" 196 | end 197 | "#{opt} conf.gem :core => 'mruby-#{g}'" 198 | end.join("\n") 199 | if default_gem_command == '' 200 | default_gem_command = ' # No GEMs activated!' 201 | end 202 | end 203 | 204 | write_file = nil 205 | [arg1, arg2].each {|x| write_file ||= x =~ r.('write')} 206 | 207 | gem_command = gems.active.map do |g| 208 | options = ", :options => \'#{g.repooptions}\'" unless g.repooptions.nil? 209 | " conf.gem :git => \'#{g.repository}\'#{options}" 210 | end.join("\n") 211 | if gem_command == '' 212 | gem_command = ' # No GEMs activated!' 213 | end 214 | 215 | build_config = <<__CONFIG__ 216 | 217 | ############################ 218 | # Start of your build_config 219 | 220 | MRuby::Build.new do |conf| 221 | toolchain :#{toolchain} 222 | 223 | # mruby's Core GEMs 224 | #{default_gem_command} 225 | 226 | # user-defined GEMs 227 | #{gem_command} 228 | 229 | # Testing environment 230 | #{test_env} 231 | 232 | # Debug Flags 233 | #{debug_flags} 234 | end 235 | 236 | # End of your build_config 237 | ############################ 238 | __CONFIG__ 239 | 240 | if write_file 241 | open('./build_config.rb', 'w') {|f| f.puts build_config} 242 | else 243 | puts build_config 244 | end 245 | else 246 | puts < {:desc => 'GNU Compiler Collection', :id => 'gcc'}, 16 | 'clang' => {:desc => 'LLVM C Compiler', :id => 'clang'}, 17 | 'vc' => {:desc => 'Visual C++', :id => 'visualcpp'}, 18 | 'wrt' => {:desc => 'OpenWRT Build environment', :id => 'openwrt'}, 19 | 'ndk' => {:desc => 'Android NDK', :id => 'android'} 20 | } 21 | 22 | include Enumerable 23 | 24 | def load_gems 25 | config = {} 26 | config[:mgem_dir] = [ENV["MGEM_HOME"], MGEM_DIR].join File::SEPARATOR 27 | config[:mgem_active] = [config[:mgem_dir], GEMS_ACTIVE].join File::SEPARATOR 28 | config[:mgem_list] = [config[:mgem_dir], GEMS_LIST].join File::SEPARATOR 29 | 30 | initialize_mgem_list(config) 31 | 32 | MrbgemList.new(config) 33 | end 34 | 35 | def initialize_mgem_list(config = {}) 36 | unless File.exist? config[:mgem_list] 37 | puts "Loading fresh GEM list..." 38 | cmd = %Q(git clone "#{GEMS_REPO}" "#{config[:mgem_list]}") 39 | `#{cmd}` 40 | puts "done!" 41 | end 42 | 43 | unless File.exist? config[:mgem_active] 44 | FileUtils.touch config[:mgem_active] 45 | end 46 | end 47 | end 48 | 49 | class MrbgemData 50 | def initialize(gem_data) 51 | @gem_data = gem_data 52 | end 53 | 54 | def search(pattern, *fields) 55 | fields.flatten! 56 | if fields == [] 57 | fields = [:name, :description] 58 | elsif not fields.respond_to? :each 59 | fields = [fields] 60 | end 61 | fields.to_a.each do |field| 62 | if self.send(field) =~ /#{pattern}/i 63 | return true 64 | end 65 | end 66 | 67 | return false 68 | end 69 | 70 | # list of GEM properties 71 | 72 | def name; @gem_data["name"]; end 73 | def description; @gem_data["description"]; end 74 | def author; @gem_data["author"]; end 75 | def website; @gem_data["website"]; end 76 | def protocol; @gem_data["protocol"]; end 77 | def repository; @gem_data["repository"]; end 78 | def repooptions; @gem_data["repooptions"]; end 79 | def dependencies; @gem_data['dependencies']; end 80 | 81 | def method_missing(method_name) 82 | err = "Mrbgem Field \"#{method_name}\" doesn't exist!" 83 | raise ArgumentError.new err 84 | end 85 | end 86 | 87 | class MrbgemList 88 | def initialize(config) 89 | @config = config 90 | @gems = get_gems(@config[:mgem_list]) 91 | end 92 | 93 | def each(&block) 94 | @gems.each {|gem| block.call(gem)} 95 | end 96 | 97 | def search(pattern, *fields) 98 | @gems.select do |mrbgem| 99 | mrbgem.search pattern, fields 100 | end 101 | end 102 | 103 | def active 104 | f = File.open(@config[:mgem_active], 'r+') 105 | active_gems = f.each_line.map {|g| File.basename(g.chomp)} 106 | @gems.select {|g| active_gems.include? g.name} 107 | end 108 | 109 | def size; @gems.size; end 110 | 111 | def activate(gem_name) 112 | if check_gem(gem_name) 113 | gems = active + @gems.select {|g| g.name == gem_name} 114 | save_active_gems(gems) 115 | puts "'#{gem_name}' activated!" 116 | else 117 | puts "'#{gem_name}' NOT activated!" 118 | end 119 | end 120 | 121 | def deactivate(gem_name) 122 | if check_gem(gem_name) 123 | gems = active.reject {|g| g.name == gem_name} 124 | save_active_gems(gems) 125 | puts "'#{gem_name}' deactivated!" 126 | else 127 | puts "'#{gem_name}' NOT deactivated!" 128 | end 129 | end 130 | 131 | def update! 132 | temp_stderr, $stderr = $stderr, StringIO.new 133 | git_dir = [@config[:mgem_list], '.git'].join File::SEPARATOR 134 | dir_arg = "--git-dir=#{git_dir} --work-tree=#{@config[:mgem_list]} " 135 | current_hash = `git #{dir_arg} log -n 1 --pretty=format:%H` 136 | `git #{dir_arg} pull` 137 | result = `git #{dir_arg} log #{current_hash}..HEAD --pretty=format:''` 138 | count = result.lines.count 139 | ensure 140 | $stderr = temp_stderr 141 | end 142 | 143 | private 144 | 145 | def check_gem(gem_name) 146 | if gem_name == "" or gem_name.nil? 147 | puts "Error: Empty GEM name!" 148 | false 149 | elsif not @gems.find_index {|g| g.name == gem_name} 150 | puts "Error: GEM doesn't exist!" 151 | false 152 | else 153 | true 154 | end 155 | end 156 | 157 | def save_active_gems(active_gem_list) 158 | File.open(@config[:mgem_active], 'w+') do |f| 159 | active_gem_list.uniq.each do |mrbgem| 160 | f.puts mrbgem.name 161 | end 162 | end 163 | end 164 | 165 | def get_gems(gem_dir) 166 | gems = [] 167 | Dir.foreach(gem_dir) do |filename| 168 | next unless filename =~ /\.gem$/ 169 | 170 | yaml_gems = YAML.load_file([gem_dir, filename].join(File::SEPARATOR)) 171 | gems << MrbgemData.new(yaml_gems) 172 | end 173 | gems 174 | end 175 | end 176 | -------------------------------------------------------------------------------- /zsh/_mgem: -------------------------------------------------------------------------------- 1 | #compdef mgem 2 | # 3 | # DESCRIPTION 4 | # ============== 5 | # 6 | # Completion script for mgem (https://github.com/bovi/mgem) 7 | # 8 | # MANUAL INSTALLATION 9 | # ====================== 10 | # 11 | # * Clone the repository: 12 | # 13 | # git clone git://github.com/zsh-users/zsh-completions.git 14 | # 15 | # * Add the directory path into your `$fpath`: 16 | # 17 | # fpath=(/path/to/mgem/zsh $fpath) 18 | # or 19 | # fpath+="/path/to/mgem/zsh" 20 | # 21 | _mgem() 22 | { 23 | local context state state_descr line ret=1 24 | typeset -A opt_args 25 | 26 | _arguments -C \ 27 | '1: :__mgem_commands' \ 28 | '*:: :->args' \ 29 | && ret=0 30 | 31 | case $state in 32 | args) 33 | case "$words[1]" in 34 | list) 35 | _values -w 'option' active 36 | ;; 37 | info) 38 | __all_gems 39 | ;; 40 | add) 41 | __all_gems 42 | ;; 43 | rm) 44 | __active_gems 45 | ;; 46 | esac 47 | ;; 48 | esac 49 | 50 | return ret 51 | } 52 | 53 | __mgem_commands() 54 | { 55 | _1st_arguments=( 56 | 'size:How many GEMs are available?' 57 | 'list:List GEMs' 58 | 'info:Show detail information abount a GEM' 59 | 'add:Activate a GEM' 60 | 'rm:De-Activate a GEM' 61 | 'search:Search for GEMs' 62 | 'config:Generate a mruby build config including all active GEMs' 63 | 'update:Update the list of GEMs' 64 | ) 65 | 66 | _describe -t commands "commands" _1st_arguments 67 | } 68 | 69 | __all_gems() 70 | { 71 | local -a _gems 72 | _gems=( $(find ~/.mgem/mgem-list -name "*.gem" | xargs basename | sed 's/\.gem$//g') ) 73 | _describe -t mgemnames "gem name" _gems 74 | } 75 | 76 | __active_gems() 77 | { 78 | local -a _gems 79 | _gems=( $(mgem list active | awk 'NR>=2 {print $2}') ) 80 | _describe -t mgemnames "active gems" _gems 81 | } 82 | 83 | _mgem "$@" 84 | --------------------------------------------------------------------------------