merrol 0.0.2

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.
Files changed (82) hide show
  1. data/.autotest +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/LICENSE +23 -0
  5. data/README.md +58 -0
  6. data/ROADMAP.md +22 -0
  7. data/Rakefile +63 -0
  8. data/autotest/discover.rb +2 -0
  9. data/bin/m +9 -0
  10. data/config/commands.yml +150 -0
  11. data/config/language.yml +47 -0
  12. data/config/messages.yml +3 -0
  13. data/config/settings.yml +3 -0
  14. data/config/snippets/rails.yml +22 -0
  15. data/config/snippets/rspec.yml +35 -0
  16. data/config/snippets/ruby.yml +97 -0
  17. data/config/styles/Railscasts.xml +121 -0
  18. data/config/templates/rails.yml +21 -0
  19. data/config/templates/rspec.yml +6 -0
  20. data/data/images/merrol.svg +412 -0
  21. data/data/images/merrol2.svg +531 -0
  22. data/data/images/modified.svg +156 -0
  23. data/data/images/saved.svg +156 -0
  24. data/lib/merrol/controllers/controller.rb +26 -0
  25. data/lib/merrol/controllers/edit_controller.rb +24 -0
  26. data/lib/merrol/controllers/file_controller.rb +61 -0
  27. data/lib/merrol/controllers/help_controller.rb +6 -0
  28. data/lib/merrol/controllers/main_controller.rb +26 -0
  29. data/lib/merrol/controllers/search_controller.rb +5 -0
  30. data/lib/merrol/controllers/tools_controller.rb +28 -0
  31. data/lib/merrol/gtk/bin.rb +11 -0
  32. data/lib/merrol/gtk/image.rb +8 -0
  33. data/lib/merrol/gtk/list_view.rb +43 -0
  34. data/lib/merrol/gtk/source_view.rb +22 -0
  35. data/lib/merrol/gtk/widget.rb +12 -0
  36. data/lib/merrol/gtk/window.rb +18 -0
  37. data/lib/merrol/keyboard_map.rb +136 -0
  38. data/lib/merrol/lib/application.rb +29 -0
  39. data/lib/merrol/lib/commands.rb +40 -0
  40. data/lib/merrol/lib/file.rb +6 -0
  41. data/lib/merrol/lib/shortcut.rb +35 -0
  42. data/lib/merrol/lib/widget_builder.rb +49 -0
  43. data/lib/merrol/lib/yaml.rb +10 -0
  44. data/lib/merrol/models/source_model.rb +44 -0
  45. data/lib/merrol/views/edit.yml +50 -0
  46. data/lib/merrol/views/file_list.yml +11 -0
  47. data/lib/merrol/views/file_path.yml +8 -0
  48. data/lib/merrol/views/file_status.yml +6 -0
  49. data/lib/merrol/views/hbox.yml +6 -0
  50. data/lib/merrol/views/main.yml +9 -0
  51. data/lib/merrol/views/scroll_bars.yml +15 -0
  52. data/lib/merrol/views/status_bar.yml +7 -0
  53. data/lib/merrol.rb +16 -0
  54. data/lib/prerequisites.rb +54 -0
  55. data/merrol.gemspec +24 -0
  56. data/spec/controllers/controller_spec.rb +27 -0
  57. data/spec/controllers/edit_controller_spec.rb +38 -0
  58. data/spec/controllers/file_controller_spec.rb +209 -0
  59. data/spec/controllers/main_controller_spec.rb +20 -0
  60. data/spec/gtk/bin_spec.rb +13 -0
  61. data/spec/gtk/list_view_spec.rb +114 -0
  62. data/spec/gtk/source_view_spec.rb +41 -0
  63. data/spec/gtk/widget_spec.rb +37 -0
  64. data/spec/gtk/window_spec.rb +28 -0
  65. data/spec/integration/000_startup_spec.rb +14 -0
  66. data/spec/integration/001_load_from_commandline_spec.rb +25 -0
  67. data/spec/integration/002_loading_and_saving_spec.rb +35 -0
  68. data/spec/integration/003_tab_file_switching_spec.rb +49 -0
  69. data/spec/integration/ideas_spec.rb +23 -0
  70. data/spec/integration_helper.rb +11 -0
  71. data/spec/lib/application_spec.rb +57 -0
  72. data/spec/lib/commands_spec.rb +63 -0
  73. data/spec/lib/file_spec.rb +9 -0
  74. data/spec/lib/shortcut_spec.rb +59 -0
  75. data/spec/lib/widget_builder_spec.rb +77 -0
  76. data/spec/lib/yaml_spec.rb +26 -0
  77. data/spec/models/source_model_spec.rb +80 -0
  78. data/spec/spec_helper.rb +18 -0
  79. data/spec/support/actions.rb +28 -0
  80. data/spec/support/expectations.rb +46 -0
  81. data/spec/support/setup.rb +32 -0
  82. metadata +224 -0
@@ -0,0 +1,136 @@
1
+ require 'gtk2'
2
+ require 'gtksourceview2'
3
+ require 'yaml'
4
+ require File.expand_path(File.dirname(__FILE__) + '/yaml.rb')
5
+ require File.expand_path(File.dirname(__FILE__) + '/file.rb')
6
+ require_relative 'command_dispatcher'
7
+ include Merrol::CommandDispatcher
8
+ load_commands
9
+
10
+ window = Gtk::Window.new "Keyboard Shortcuts"
11
+ window.set_default_size(300, 200)
12
+ window.signal_connect("destroy") do |w|
13
+ Gtk.main_quit
14
+ end
15
+ window.signal_connect('key_press_event') do |w, e|
16
+ if !toggle_button(shortcut(e), true)
17
+ shortcut = shortcut(e)
18
+ help = command_for(key_from_event(e)).last
19
+ $help.buffer.text = help if help
20
+ end
21
+ end
22
+
23
+ window.signal_connect('key_release_event') do |w, e|
24
+ toggle_button shortcut(e), false
25
+ end
26
+
27
+ def toggle_button modifier, active
28
+ if %w(CTRL ALT SHIFT).include?(modifier)
29
+ $modifiers.each do |button|
30
+ button.active = active if button.children.first.text.split("\n").first == modifier
31
+ end
32
+ render_keys
33
+ true
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ def render_keys
40
+ $rows.children[0..-2].each do |row|
41
+ row.children.each do |button|
42
+ next if button.is_a?(Gtk::Label)
43
+ label = button.children.first
44
+ key = label.text.split("\n").first
45
+ command = command_for(key).first
46
+ button.sensitive = !command.nil? unless %w(CTRL SHIFT ALT).include?(key)
47
+ label.markup = "<small><b>#{key}</b>\n#{command}</small>"
48
+ end
49
+ end
50
+ end
51
+
52
+ def shortcut shortcut
53
+ keys = []
54
+ key = key_from_event(shortcut)
55
+ return key if %w(CTRL SHIFT ALT).include?(key)
56
+ keys << "CTRL" if shortcut.state.control_mask?
57
+ keys << "ALT" if shortcut.state.mod1_mask?
58
+ keys << "SHIFT" if shortcut.state.shift_mask?
59
+ keys << key
60
+ keys.join('+')
61
+ end
62
+
63
+ def key_from_event e
64
+ unmodified_keyval = Gdk::Keymap.default.lookup_key(e.hardware_keycode, 0, 0)
65
+ key = Gdk::Keyval.to_name(unmodified_keyval).upcase
66
+ {'RETURN' => 'ENTER', 'ESCAPE' => 'ESC',
67
+ 'CONTROL_L' => 'CTRL', 'ALT_L' => 'ALT', 'SHIFT_L' => 'SHIFT',
68
+ 'CONTROL_R' => 'CTRL', 'ALT_R' => 'ALT', 'SHIFT_R' => 'SHIFT'
69
+ }[key] || key
70
+ end
71
+
72
+ keys = [
73
+ %w(ESC F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12),
74
+ %w(` 1 2 3 4 5 6 7 8 9 0 - = BACK),
75
+ %w(TAB Q W E R T Y U I O P [ ] ENTER),
76
+ %w(CAPS A S D F G H J K L ; ' #),
77
+ %w(SHIFT \\ Z X C V B N M , . / SHIFT),
78
+ %w(CTRL WIN ALT SPACE ALT FN CTRL)
79
+ ]
80
+
81
+ $modifiers = []
82
+ $rows = Gtk::VBox.new
83
+
84
+ keys.each do |row|
85
+ hbox = Gtk::HBox.new
86
+ $rows.pack_start(hbox, false, false, 1)
87
+ row.each do |key|
88
+ command = @commands[key].first if @commands[key]
89
+ if %w(CTRL SHIFT ALT).include?(key)
90
+ button = Gtk::ToggleButton.new
91
+ $modifiers << button
92
+ button.signal_connect('toggled') do |w, e|
93
+ modifier = w.children.first.text.split("\n").first
94
+ toggle_button modifier, w.active?
95
+ end
96
+
97
+ else
98
+ button = Gtk::Button.new
99
+ button.signal_connect('clicked') do |w, e|
100
+ key = w.children.first.text.split("\n").first
101
+ $help.buffer.text = command_for(key).last
102
+ end
103
+ button.sensitive = false unless command
104
+ end
105
+ label = Gtk::Label.new
106
+ label.markup = "<small><b>#{key}</b>\n#{command}</small>"
107
+ button.add(label)
108
+ button.width_request = key.length * 10 + 40
109
+ button.width_request = 300 if key == 'SPACE'
110
+ hbox.pack_start(button, true, true, 1)
111
+
112
+ hbox.pack_start(Gtk::Label.new.set_width_request(50), true, true, 1) if key == '#'
113
+ end
114
+ end
115
+
116
+ $help = Gtk::TextView.new
117
+ $help.buffer.text = "Use the keyboard or mouse to press (or click) on modifiers and keys to see more information."
118
+ $help.height_request = 60
119
+ $help.wrap_mode = Gtk::TextTag::WRAP_WORD
120
+ $help.sensitive = false
121
+ $help.modify_font Pango::FontDescription.new('FreeSans Normal 11')
122
+ $rows.pack_start(Gtk::Frame.new.add($help), true, true, 2)
123
+ window.add($rows)
124
+
125
+ def command_for key
126
+ modifiers = ''
127
+ modifiers << 'CTRL+' if $modifiers[2].active?
128
+ modifiers << 'ALT+' if $modifiers[3].active?
129
+ modifiers << 'SHIFT+' if $modifiers[0].active?
130
+ @commands[modifiers + key] || []
131
+ end
132
+
133
+ window.show_all
134
+
135
+ Gtk.main
136
+
@@ -0,0 +1,29 @@
1
+ module Merrol
2
+ class Application
3
+ attr_reader :views, :controllers
4
+
5
+ def initialize working_dir, paths
6
+ @views = WidgetBuilder.build :main, :status_bar, :hbox, :file_list, :scroll_bars, :edit, :file_status, :file_path
7
+ main_view = @views[:main]
8
+ commands = Commands.new main_view
9
+
10
+ @controllers = {}
11
+ controller_paths = Dir[File.app_relative('lib/merrol/controllers') + '/*_controller.rb']
12
+ controller_paths.each do |controller|
13
+ controller = File.basename(controller, '.rb')
14
+ name = controller.gsub(/_controller/, '')
15
+ @controllers[name] = eval(controller.classify).new commands, @views
16
+ end
17
+
18
+ @controllers['main'].working_dir = working_dir
19
+ @controllers['file'].load_all paths
20
+
21
+ # TODO: move both to a controller
22
+ @views[:file_path].text = paths.last if paths.last
23
+ @views[:edit].grab_focus
24
+
25
+ main_view.show_all
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,40 @@
1
+ module Merrol
2
+ class Commands
3
+ def initialize(window)
4
+ @accel_group = Gtk::AccelGroup.new
5
+ window.add_accel_group @accel_group
6
+ load
7
+ end
8
+
9
+ def register controller
10
+ @commands[controller.name.to_s].each do |command, detail|
11
+ if detail['key'] =~ /TAB/
12
+ Gtk::Window.toplevels.first.signal_connect('key_press_event') do |widget, event|
13
+ if detail['key'] == Shortcut.from(event)
14
+ controller.send(command)
15
+ else
16
+ false
17
+ end
18
+ end
19
+ else
20
+ @accel_group.connect "<#{APP_NAME}>/#{controller.name}/#{command}" do
21
+ controller.send(command)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def load
30
+ @commands = YAML.load_config 'commands'
31
+ @commands.each do |category, commands|
32
+ commands.each do |command, detail|
33
+ event = Shortcut.to_event(detail['key'])
34
+ Gtk::AccelMap.add_entry("<#{APP_NAME}>/#{category}/#{command}", event.keyval, event.state)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,6 @@
1
+ class File
2
+ def self.app_relative relative_path
3
+ expand_path join(dirname(__FILE__), '..', '..', '..', relative_path)
4
+ end
5
+ end
6
+
@@ -0,0 +1,35 @@
1
+ module Merrol
2
+ class Shortcut
3
+ def self.from event
4
+ shortcut = Gdk::Keyval.to_name(event.keyval).upcase
5
+ shortcut = shortcut.gsub(/(_L|_R)$/, '').gsub('CONTROL', 'CTRL').gsub('ESCAPE', 'ESC').gsub('RETURN', 'ENTER')
6
+
7
+ if !%w(CTRL ALT SHIFT).include? shortcut
8
+ keys = []
9
+ keys << "CTRL" if event.state.control_mask?
10
+ keys << "ALT" if event.state.mod1_mask?
11
+ keys << "SHIFT" if event.state.shift_mask?
12
+ keys << shortcut
13
+ shortcut = keys.join('+')
14
+ end
15
+ shortcut
16
+ end
17
+
18
+ def self.to_event key, release = nil
19
+ event = Gdk::EventKey.new(release ? Gdk::Event::KEY_RELEASE : Gdk::Event::KEY_PRESS)
20
+ key = key.gsub(/ESC/, 'ESCAPE').gsub(/ENTER/, 'RETURN')
21
+
22
+ if %w(CTRL ALT SHIFT).include? key
23
+ key = key.gsub('CTRL', 'CONTROL').capitalize + '_L'
24
+ else
25
+ event.state |= Gdk::Window::CONTROL_MASK if key =~ /CTRL+/
26
+ event.state |= Gdk::Window::MOD1_MASK if key =~ /ALT+/
27
+ event.state |= Gdk::Window::SHIFT_MASK if key =~ /SHIFT+/
28
+ key = key.split('+').last.capitalize
29
+ end
30
+ event.keyval = Gdk::Keyval.from_name(key)
31
+ event
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,49 @@
1
+ module Merrol
2
+ class WidgetBuilder
3
+ def self.build *view_names
4
+ widgets = {}
5
+ view_names.each do |view_name|
6
+ yaml = YAML.load_view(view_name.to_s)
7
+ widget = nil
8
+ add_to = nil
9
+ pack = {:expand => false, :fill => false, :padding => 0}
10
+ yaml['layout'].merge(yaml['options'] || {}).each do |key, value|
11
+ if key == 'type'
12
+ widget = eval('Gtk::' + value).new
13
+ elsif value.is_a?(Hash)
14
+ widget.send("#{key}=", constants_from(value))
15
+ elsif key == 'add_to'
16
+ add_to = widgets[value.to_sym]
17
+ elsif %w(expand fill padding).include?(key)
18
+ pack[key.to_sym] = value
19
+ else
20
+ widget.send("#{key}=", value)
21
+ end
22
+ end
23
+
24
+ if add_to
25
+ container = add_to.container if add_to.respond_to?(:container)
26
+ container ||= add_to
27
+ if container.is_a?(Gtk::Bin)
28
+ container.add(widget)
29
+ else
30
+ container.pack_start(widget, pack[:expand], pack[:fill], pack[:padding])
31
+ end
32
+ end
33
+ widgets[view_name] = widget
34
+ end
35
+ widgets
36
+ end
37
+
38
+ private
39
+
40
+ def self.constants_from value
41
+ type = value['type']
42
+ value = value['value']
43
+ value = [value] unless value.is_a?(Array)
44
+
45
+ eval(value.map{ |v| ['Gtk', type, v].compact.join('::') }.join(' | '))
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,10 @@
1
+ module YAML
2
+ LOCATIONS = {'config' => 'config', 'view' => 'lib/merrol/views'}
3
+
4
+ def self.method_missing(meth, *args, &block)
5
+ dir = LOCATIONS[meth.to_s.gsub(/load_/, '')]
6
+ super unless dir
7
+ self.load File.open(File.app_relative("#{dir}/#{args[0]}.yml"))
8
+ end
9
+ end
10
+
@@ -0,0 +1,44 @@
1
+ module Merrol
2
+ class SourceModel < Gtk::SourceBuffer
3
+
4
+ def initialize path = nil
5
+ super(nil)
6
+ @path = path
7
+ load
8
+ language_from_filetype
9
+ end
10
+
11
+ def save
12
+ File.open(@path, 'w') {|f| f.write(self.text) }
13
+ self.modified = false
14
+ end
15
+
16
+ def load
17
+ if @path && File.exist?(@path)
18
+ begin_not_undoable_action { self.text = File.read(@path) }
19
+ self.modified = false
20
+ end
21
+ end
22
+
23
+ def language_from_filetype
24
+ yaml = YAML.load_config('language')
25
+ yaml.each do |name, hash|
26
+ hash['pattern'].split(' ').each do |pattern|
27
+ if pattern[0..0] == '.' && @path[0..0] != '.'
28
+ name_or_ext = File.extname(@path)
29
+ else
30
+ name_or_ext = File.basename(@path)
31
+ end
32
+
33
+ bang_line = "#!/usr/bin/env #{hash['bang']}"
34
+ file_exists = hash['exists'] ? File.exist?(hash['exists']) : true
35
+ if (pattern == name_or_ext || self.text[0..(bang_line.length - 1)] == bang_line) && file_exists
36
+ self.language = Gtk::SourceLanguageManager.new.get_language name
37
+ return
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,50 @@
1
+ layout:
2
+ type: SourceView
3
+ add_to: scroll_bars
4
+
5
+ options:
6
+ auto_indent: true
7
+ theme: config/styles/Railscasts
8
+ font: monospace 9
9
+ highlight_current_line: true
10
+ indent_on_tab: true
11
+ indent_width: 2
12
+ insert_spaces_instead_of_tabs: true
13
+ right_margin_position: 120
14
+ show_line_marks: false
15
+ show_line_numbers: true
16
+ show_right_margin: true
17
+ tab_width: 2
18
+
19
+ #TODO: Provide support for buffer preferences
20
+ #buffer:
21
+ highlight_matching_brackets: true
22
+ # bracket_match_style:
23
+ # type: SourceStyle
24
+ # background: nil
25
+ # line_background: nil
26
+ # foreground: nil
27
+ # bold: false
28
+ # italic: false
29
+ # mask: false
30
+ # strikethrough: false
31
+ # underline: false
32
+
33
+ # value can be one or more of the following as an array or singular
34
+ # DRAW_SPACES_SPACE, DRAW_SPACES_TAB, DRAW_SPACES_NEWLINE
35
+ # DRAW_SPACES_NBSP, DRAW_SPACES_LEADING, DRAW_SPACES_TEXT
36
+ # DRAW_SPACES_TRAILING, or just DRAW_SPACES_ALL
37
+ # draw_spaces:
38
+ # type: SourceView
39
+ # value: [DRAW_SPACES_SPACE, DRAW_SPACES_NEWLINE]
40
+
41
+ # SMART_HOME_END_BEFORE, SMART_HOME_END_BEFORE, SMART_HOME_END_BEFORE
42
+ smart_home_end:
43
+ type: SourceView
44
+ value: SMART_HOME_END_BEFORE
45
+
46
+ # WRAP_NONE, WRAP_CHAR, WRAP_WORD, WRAP_WORD_CHAR
47
+ wrap_mode:
48
+ type: TextTag
49
+ value: WRAP_WORD
50
+
@@ -0,0 +1,11 @@
1
+ layout:
2
+ type: ListView
3
+ add_to: hbox
4
+ initially_hidden: true
5
+
6
+ options:
7
+ padding: 2
8
+ width_request: 250
9
+ border_width: 2
10
+ font: arial 9
11
+
@@ -0,0 +1,8 @@
1
+ layout:
2
+ type: Label
3
+ add_to: status_bar
4
+ padding: 2
5
+
6
+ options:
7
+ font: arial 9
8
+
@@ -0,0 +1,6 @@
1
+ layout:
2
+ type: Image
3
+ add_to: status_bar
4
+ padding: 2
5
+ file: data/images/saved.svg
6
+
@@ -0,0 +1,6 @@
1
+ layout:
2
+ type: HBox
3
+ add_to: main
4
+ expand: true
5
+ fill: true
6
+
@@ -0,0 +1,9 @@
1
+ layout:
2
+ type: Window
3
+ icon: data/images/merrol2.svg
4
+ container: VBox
5
+
6
+ options:
7
+ default_position: 0, 0
8
+ default_size: 1000, 700
9
+
@@ -0,0 +1,15 @@
1
+ layout:
2
+ type: ScrolledWindow
3
+ add_to: hbox
4
+ expand: true
5
+ fill: true
6
+ padding: 1
7
+
8
+ options:
9
+ hscrollbar_policy:
10
+ type:
11
+ value: POLICY_AUTOMATIC # show horizontal scrollbar: POLICY_NEVER, POLICY_AUTOMATIC, POLICY_ALWAYS
12
+ vscrollbar_policy:
13
+ type:
14
+ value: POLICY_AUTOMATIC # show vertical scrollbar: POLICY_NEVER, POLICY_AUTOMATIC, POLICY_ALWAYS
15
+
@@ -0,0 +1,7 @@
1
+ layout:
2
+ type: HBox
3
+ add_to: main
4
+ expand: false
5
+ fill: true
6
+ padding: 1
7
+
data/lib/merrol.rb ADDED
@@ -0,0 +1,16 @@
1
+ require_relative 'prerequisites'
2
+
3
+ require 'gtk2'
4
+ require 'gtksourceview2'
5
+ require 'yaml'
6
+ require 'active_support/inflector'
7
+ require 'observer'
8
+
9
+ APP_NAME = "Merrol"
10
+ WORKING_DIR = Dir.getwd
11
+
12
+ require "#{File.dirname(__FILE__)}/merrol/controllers/controller"
13
+ merrol_path = "#{File.dirname(__FILE__)}/merrol/**/*.rb"
14
+ to_include = Dir[merrol_path]
15
+ to_include.each { |file| require file unless file.include?('keyboard_map') }
16
+
@@ -0,0 +1,54 @@
1
+ class LibNotFoundError < StandardError; end
2
+
3
+ def check_packages
4
+ selections = `dpkg --get-selections`
5
+ libs = %w(libglib2.0-dev libatk1.0-dev libpango1.0-dev libgtk2.0-dev libgtksourceview2.0-dev)
6
+ libs.each do |lib|
7
+ raise LibNotFoundError unless selections =~ Regexp.new(lib)
8
+ end
9
+ rescue LibNotFoundError => e
10
+ puts 'Some packages needed for installation are missing'
11
+ puts 'To install the rquired packages please run:'
12
+ puts ' sudo apt-get install libglib2.0-dev libatk1.0-dev libpango1.0-dev libgtk2.0-dev libgtksourceview2.0-dev'
13
+ puts
14
+ exit
15
+ end
16
+
17
+ def install_ruby_gnome2
18
+ trap("INT") { puts "\rAborted."; exit }
19
+
20
+ puts "Some ruby libraries need to be compiled and installed to run merrol (gtk2 and gtksourceview2)"
21
+ puts "Press ENTER to install or CTRL+C to abort"
22
+ gets
23
+
24
+ logfile = '/tmp/ruby-gnome2-install.log'
25
+
26
+ package = 'ruby-gnome2-all-0.90.4'
27
+ system "gem install pkg-config > #{logfile}"
28
+ puts 'pkg-config installed'
29
+
30
+ puts 'Downloading sources...'
31
+ system "cd /tmp && wget http://downloads.sourceforge.net/ruby-gnome2/#{package}.tar.gz >> #{logfile}"
32
+
33
+ puts 'Extracting sources...'
34
+ system "cd /tmp && tar xf #{package}.tar.gz"
35
+
36
+ puts 'Configuring ...'
37
+ system "cd /tmp/#{package} && ruby extconf.rb >> #{logfile}"
38
+
39
+ puts 'Building ...'
40
+ system "cd /tmp/#{package} && sudo make >> #{logfile}"
41
+
42
+ puts 'Installing ...'
43
+ system "cd /tmp/#{package} && sudo make install >> #{logfile}"
44
+
45
+ puts 'Done.'
46
+ end
47
+
48
+ begin
49
+ check_packages
50
+ require 'gtksourceview2'
51
+ rescue LoadError
52
+ install_ruby_gnome2
53
+ end
54
+
data/merrol.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "merrol"
3
+ s.version = "0.0.2"
4
+ s.authors = "Phil Thompson"
5
+ s.email = "phil@electricvisions.com"
6
+ s.homepage = "http://merrol.com"
7
+ s.summary = "MERROL - Minimalist Editor for Ruby, Rails and Other Languages"
8
+ s.description = "An editor for Ruby, Rails and supporting langauges, written in pure Ruby with an open design to allow customization and improvement."
9
+ s.required_rubygems_version = ">= 1.3.6"
10
+ s.rubyforge_project = "merrol"
11
+
12
+ s.add_dependency "activesupport"
13
+
14
+ s.add_development_dependency "autotest"
15
+ s.add_development_dependency "simplecov"
16
+ s.add_development_dependency "rspec", '2.0.0'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- spec/*`.split("\n")
20
+
21
+ s.executables = ['m']
22
+ s.require_path = 'lib'
23
+ end
24
+
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Controller do
4
+ it 'gets the name without controller suffix' do
5
+ mock_view = mock Gtk::Window, :signal_connect => nil
6
+ MainController.new(mock(Commands, :register => nil), {:main => mock_view}).name.should == :main
7
+ end
8
+
9
+ it 'access views using <name>_view method' do
10
+ mock_view = mock Gtk::Window, :signal_connect => nil
11
+ controller = Controller.new(mock(Commands, :register => nil), {:main => mock_view})
12
+ controller.main_view.should == mock_view
13
+ end
14
+
15
+ it 'raises method missing error when view not found' do
16
+ mock_view = mock Gtk::Window, :signal_connect => nil
17
+ controller = Controller.new(mock(Commands, :register => nil), {:main => mock_view})
18
+ lambda {controller.non_existent_view}.should raise_error NoMethodError
19
+ end
20
+
21
+ it 'raises method missing error when not a view method' do
22
+ mock_view = mock Gtk::Window, :signal_connect => nil
23
+ controller = Controller.new(mock(Commands, :register => nil), {:main => mock_view})
24
+ lambda {controller.non_existent_method}.should raise_error NoMethodError
25
+ end
26
+ end
27
+
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe EditController do
4
+ before(:each) do
5
+ @mock_commands = mock Commands, :register => nil
6
+ @mock_view = mock(Gtk::SourceView)
7
+ @edit = EditController.new(@mock_commands, {:edit => @mock_view})
8
+ end
9
+
10
+ it 'selects all text' do
11
+ @mock_view.should_receive(:select_all).with true
12
+ @edit.select_all
13
+ end
14
+
15
+ it 'deselects all text' do
16
+ @mock_view.should_receive(:select_all).with false
17
+ @edit.select_none
18
+ end
19
+
20
+ it 'calls redo on view' do
21
+ @mock_view.should_receive(:redo)
22
+ @edit.redo
23
+ end
24
+
25
+ it 'calls undo on view' do
26
+ @mock_view.should_receive(:undo)
27
+ @edit.undo
28
+ end
29
+
30
+ it 'calls delete on view' do
31
+ @mock_buffer = mock SourceModel
32
+ @mock_view.stub(:buffer).and_return @mock_buffer
33
+ @mock_buffer.should_receive(:delete_selection).with(true, true)
34
+ @edit.delete
35
+ end
36
+
37
+ end
38
+