merrol 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+