blur 1.8.6 → 2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 37ce3e4e8a98062906f53c98b5baa1b798fe089b
4
- data.tar.gz: 4406a27f69812c4b181949a2f2106f55e663c0a9
3
+ metadata.gz: 976b042f31815f52310f120ccc9c2a3082fb0a93
4
+ data.tar.gz: 5b9a9266a92d19dd4b90494abe093dd26548b308
5
5
  SHA512:
6
- metadata.gz: f3f6a46334910c4be692cfa1c56587613bc981dcaeb8be10b5a931951a10e8a1e20607a66c8afea0837f066648591d3b1e64675388633352f5834e11d7f292ec
7
- data.tar.gz: 1aac96349671e30b299bc4fdb92b8a6df71d57e93b36361ee036780ca37b713a41d6ecdf0b831cad43dbb9ee92128d6c9bf3e14aa8bad3aaa7b1a3bacf6e966e
6
+ metadata.gz: 6630820a3dba98df19c1995971b0fab17187226356306f1354c19ea71819748bf6cb85299efddf556ffe101aec19d2a073ea8564e06655f774bc5e53d5a3e340
7
+ data.tar.gz: 7d7b87e2dc3952bcd6ce7b1360dc2f7e7cb88e795f61fcba3898d5713f42ebc144bc437dcf66daa4f1dedb93e87f0de01f409fadc26e54577a500f9e90105f8b
data/README.md CHANGED
@@ -6,15 +6,6 @@ There are a bunch of other well-written, well-running IRC libraries made for
6
6
  Ruby, but for me, they don't quite cut it as **the** library I wanted to use for
7
7
  my IRC services. That's how Blur came to be.
8
8
 
9
- Blur scales. A lot.
10
-
11
- When I stresstested the library on my network, I ended up throttling my VDSL
12
- connection before Blur even broke a sweat - albeit I only have 20/2.
13
-
14
- I managed to connect with 5000 clones before it couldn't resolve the hostname
15
- anymore, while this is an excellent feature, I would not suggest doing it.
16
-
17
- [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/mkroman/blur/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
18
9
  [![Build Status](https://travis-ci.org/mkroman/blur.svg?branch=isupport)](https://travis-ci.org/mkroman/blur)
19
10
  [![Dependency Status](https://gemnasium.com/mkroman/blur.svg)](https://gemnasium.com/mkroman/blur)
20
11
 
@@ -22,7 +13,6 @@ Features
22
13
  --------
23
14
  * SSL/TLS encryption
24
15
  * Connect to multiple networks
25
- * FiSH (channel-wide) encryptions
26
16
  * Non-blocking connections (no threading)
27
17
  * Extensible with scripts, (re)loadable during runtime
28
18
  * Modular, makes it a piece of cake to extend its IRC-capability
@@ -31,5 +21,4 @@ Future Plans
31
21
  ------------
32
22
  * DCC File-transfers
33
23
  * DH1080 Key-Exchange
34
- * ISupport implementation
35
24
  * Better event-handling in scripts
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ $:.unshift File.join __dir__, '../library'
5
+
6
+ require 'optparse'
7
+
8
+ options = {
9
+ verbose: false,
10
+ environment: 'development',
11
+ config_path: 'config.yml'
12
+ }
13
+
14
+ OptionParser.new do |opts|
15
+ opts.banner = "Usage: #$0 [-c <config>] [-e <env>]"
16
+
17
+ opts.separator ""
18
+ opts.separator "Specific options:"
19
+
20
+ opts.on '-v', '--[no-]verbose', 'Enable verbose logging' do |verbose|
21
+ options[:verbose] = verbose
22
+ end
23
+
24
+ opts.on '-c', '--config=PATH', 'Set the configuration file' do |config_path|
25
+ options[:config_path] = config_path
26
+ end
27
+
28
+ opts.on '-eENV', '--environment=ENV', 'Environment to run in' do |environment|
29
+ options[:environment] = environment
30
+ end
31
+
32
+ opts.on '-r', '--require LIBRARY', 'Require the LIBRARY before running' do |lib|
33
+ require lib
34
+ end
35
+
36
+ opts.on_tail '-h', '--help', 'Show this message' do
37
+ puts opts
38
+ exit
39
+ end
40
+ end.parse!
41
+
42
+ begin
43
+ require 'blur'
44
+ rescue LoadError => exception
45
+ puts 'Ruby was unable to load the blur library!'
46
+ puts
47
+ puts "Please ensure that you've installed it using the following command:"
48
+ puts 'gem install blur'
49
+ raise exception
50
+ exit 1
51
+ end
52
+
53
+ config_path = File.expand_path options[:config_path]
54
+ unless File.readable? config_path
55
+ fail "Configuration file `#{config_path}' is not readable"
56
+ exit 1
57
+ end
58
+
59
+ @client = Blur::Client.new options
60
+ @client.connect
61
+
62
+ # vim: syntax=ruby
@@ -1,36 +1,61 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'pp'
4
3
  require 'yaml'
5
- require 'majic'
6
4
  require 'socket'
7
5
  require 'ostruct'
8
6
  require 'openssl'
7
+
8
+ require 'deep_merge/rails_compat'
9
9
  require 'eventmachine'
10
+ require 'ircparser'
10
11
 
11
12
  # Require all library files.
13
+ require 'blur/logging'
12
14
  require 'blur/version'
13
- require 'blur/client'
14
- require 'blur/evaluable'
15
- require 'blur/script/dsl'
16
- require 'blur/extension'
15
+ require 'blur/callbacks'
17
16
  require 'blur/script'
17
+ require 'blur/script_cache'
18
18
  require 'blur/network'
19
- require 'blur/encryption'
19
+ require 'blur/client'
20
+ require 'blur/user'
21
+ require 'blur/channel'
20
22
  require 'blur/enhancements'
21
- require 'blur/script/cache'
22
- require 'blur/network/user'
23
- require 'blur/network/channel'
24
- require 'blur/network/command'
25
23
  require 'blur/network/isupport'
26
24
  require 'blur/network/connection'
27
- require 'blur/script/commands'
28
25
 
29
26
  # Blur is a very modular IRC-framework for ruby.
30
27
  #
31
28
  # It allows the developer to extend it in multiple ways.
32
29
  # It can be by handlers, scripts, communications, and what have you.
33
30
  module Blur
31
+ # Contains all superscript classes for scripts that may be used.
32
+ @@scripts = {}
33
+
34
+ # Creates a new superscript class and inserts it into the list of scripts.
35
+ def self.Script name, *args, &block
36
+ klass = Class.new SuperScript
37
+ klass.name = name
38
+ klass.events = {}
39
+ klass.class_exec &block
40
+ klass.init
41
+
42
+ @@scripts[name] = klass
43
+ end
44
+
45
+ # Gets all superscript classes.
46
+ def self.scripts
47
+ @@scripts
48
+ end
49
+
50
+ # Resets all scripts.
51
+ #
52
+ # This method will call `deinit` on each script class before removing them to
53
+ # give them a chance to clean up.
54
+ def self.reset_scripts!
55
+ @@scripts.each_value &:deinit
56
+ @@scripts.clear
57
+ end
58
+
34
59
  # Instantiates a client with given options and then makes the client instance
35
60
  # evaluate the given block to form a DSL.
36
61
  #
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ module Blur
4
+ module Callbacks
5
+ # Our list of callbacks.
6
+ @@callbacks = {}
7
+
8
+ # Get a list of callbacks registered.
9
+ #
10
+ # @returns [Array] the list of callbacks
11
+ def callbacks
12
+ @@callbacks
13
+ end
14
+
15
+ # Emit a new event with given arguments.
16
+ #
17
+ # @param name [Symbol] The event name.
18
+ # @param args [optional, Array] The list of arguments to pass.
19
+ def emit name, *args
20
+ EM.defer do
21
+ notify_scripts name, *args
22
+ end
23
+
24
+ if (callbacks = @@callbacks[name]) and callbacks.any?
25
+ EM.defer do
26
+ callbacks.each{|callback| callback.call *args }
27
+ end
28
+ end
29
+ end
30
+
31
+ # Add a new event callback.
32
+ #
33
+ # @param name [Symbol] The event name.
34
+ # @yield [args, ...] The arguments passed from #emit.
35
+ def on name, &block
36
+ (@@callbacks[name] ||= []) << block
37
+ end
38
+
39
+ protected
40
+
41
+ def notify_scripts name, *args
42
+ scripts = @scripts.values.select{|script| script.class.events.key? name }
43
+ scripts.each do |script|
44
+ begin
45
+ script.class.events[name].each do |method|
46
+ if method.is_a? Proc
47
+ method.call script, *args
48
+ else
49
+ script.__send__ method, *args
50
+ end
51
+ end
52
+ rescue => exception
53
+ STDERR.puts "#{exception.class}: #{exception.message}"
54
+ STDERR.puts nil, 'Backtrace:', '---', exception.backtrace
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,75 @@
1
+ # encoding: utf-8
2
+
3
+ module Blur
4
+ # The +Channel+ class is used for encapsulating a channel and its properties.
5
+ #
6
+ # Users inside the channel is stored in the {#channels} attribute.
7
+ #
8
+ # Modes can be set for a channel, but Blur is not
9
+ # {http://www.irc.org/tech_docs/005.html ISupport}-compliant yet.
10
+ #
11
+ # @todo make so that channels *and* users belongs to the network, and not
12
+ # like now where the user belongs to the channel, resulting in multiple
13
+ # user instances.
14
+ class Channel
15
+ # @return [String] the channels name.
16
+ attr_accessor :name
17
+ # @return [String] the channels topic.
18
+ attr_accessor :topic
19
+ # @return [Array] list of references to users in the channel.
20
+ attr_accessor :users
21
+ # @return [String] all the modes set on the channel.
22
+ attr_accessor :modes
23
+ # @return [Network] a reference to the network.
24
+ attr_accessor :network
25
+
26
+ # Instantiate a user with a nickname, a network and a user list.
27
+ def initialize name, network = nil
28
+ @name = name
29
+ @users = []
30
+ @modes = String.new
31
+ @network = network
32
+ end
33
+
34
+ # Merge the channels mode corresponding to the leading character (+ or -).
35
+ #
36
+ # @param [String] modes the modes to merge with.
37
+ def merge_modes modes
38
+ addition = true
39
+
40
+ modes.each_char do |char|
41
+ case char
42
+ when ?+
43
+ addition = true
44
+ when ?-
45
+ addition = false
46
+ else
47
+ addition ? @modes.concat(char) : @modes.delete!(char)
48
+ end
49
+ end
50
+ end
51
+
52
+ # Send a message to the channel.
53
+ #
54
+ # @param [String] message the message to send.
55
+ def say message
56
+ @network.say self, message
57
+ end
58
+
59
+ # Convert it to a debug-friendly format.
60
+ def inspect
61
+ %{#<#{self.class.name}:0x#{self.object_id.to_s 16} @name=#{@name.inspect} @topic=#{@topic.inspect} @users=#{@users.inspect}}
62
+ end
63
+
64
+ # Called when YAML attempts to save the object, which happens when a
65
+ # scripts cache contains this user and the script is unloaded.
66
+ def to_yaml options = {}
67
+ @name.to_yaml options
68
+ end
69
+
70
+ # Get the channels name.
71
+ def to_s
72
+ @name
73
+ end
74
+ end
75
+ end
@@ -8,44 +8,72 @@ module Blur
8
8
  # It stores networks, scripts and callbacks, and is also encharge of
9
9
  # distributing the incoming commands to the right networks and scripts.
10
10
  class Client
11
+ include Callbacks
11
12
  include Handling, Logging
13
+
14
+ # Client error.
15
+ Error = Class.new StandardError
16
+
17
+ # The default environment.
18
+ ENVIRONMENT = ENV['BLUR_ENV'] || 'development'
19
+
20
+ # The default configuration.
21
+ DEFAULT_CONFIG = {
22
+ 'blur' => {
23
+ 'cache_dir' => 'cache/',
24
+ 'scripts_dir' => 'scripts/',
25
+ 'networks' => []
26
+ },
27
+ 'scripts' => {},
28
+ }.freeze
12
29
 
13
- # @return [Array] the options that is passed upon initialization.
14
- attr_accessor :options
15
- # @return [Array] a list of scripts that is loaded during runtime.
16
- attr_accessor :scripts
17
30
  # @return [Array] a list of instantiated networks.
18
31
  attr_accessor :networks
19
-
32
+ # @return [Hash] client configuration.
33
+ attr_accessor :config
34
+ # @return [Hash] initialized scripts.
35
+ attr_accessor :scripts
36
+
37
+ attr_accessor :verbose
38
+
20
39
  # Instantiates the client, stores the options, instantiates the networks
21
40
  # and then loads available scripts.
22
41
  #
23
42
  # @param [Hash] options the options for the client.
24
- # @option options [Array] networks list of hashes that contain network
25
- # options.
26
- def initialize options
27
- @options = options
28
- @scripts = []
29
- @networks = []
30
- @callbacks = {}
31
-
32
- @networks = @options[:networks].map {|options| Network.new options }
33
-
34
- load_scripts
43
+ # @option options [String] :config_path path to a configuration file.
44
+ # @option options [String] :environment the client environment.
45
+ def initialize options = {}
46
+ @scripts = {}
47
+ @networks = []
48
+ @config_path = options[:config_path]
49
+ @environment = options[:environment]
50
+ @verbose = options[:verbose] == true
51
+
52
+ load_config!
53
+
54
+ networks = @config['blur']['networks']
55
+
56
+ if networks and networks.any?
57
+ networks.each do |network_options|
58
+ @networks.<< Network.new network_options, self
59
+ end
60
+ end
61
+
35
62
  trap 2, &method(:quit)
36
63
  end
37
64
 
38
65
  # Connect to each network available that is not already connected, then
39
66
  # proceed to start the run-loop.
40
67
  def connect
41
- networks = @networks.select {|network| not network.connected? }
68
+ networks = @networks.reject &:connected?
42
69
 
43
70
  EventMachine.run do
44
- EventMachine.error_handler{|e| p e }
71
+ load_scripts!
72
+ networks.each &:connect
45
73
 
46
- networks.each do |network|
47
- network.delegate = self
48
- network.connect
74
+ EventMachine.error_handler do |exception|
75
+ log.error "#{exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
76
+ puts exception.backtrace.join "\n"
49
77
  end
50
78
  end
51
79
  end
@@ -56,43 +84,17 @@ module Blur
56
84
  #
57
85
  # @param [Network] network the network that received the command.
58
86
  # @param [Network::Command] command the received command.
59
- def got_command network, command
60
- log "#{'←' ^ :green} #{command.name.to_s.ljust(8, ' ') ^ :light_gray} #{command.params.map(&:inspect).join ' '}"
61
- name = :"got_#{command.name.downcase}"
62
-
63
- if respond_to? name
64
- __send__ name, network, command
87
+ def got_message network, message
88
+ if @verbose
89
+ log "#{'←' ^ :green} #{message.command.to_s.ljust(8, ' ') ^ :light_gray} #{message.parameters.map(&:inspect).join ' '}"
65
90
  end
66
- end
67
-
68
- # Searches for scripts in working_directory/scripts and then loads them.
69
- def load_scripts
70
- # Load script extensions.
71
- Script.load_extensions!
91
+ name = :"got_#{message.command.downcase}"
72
92
 
73
- # Load the scripts.
74
- script_path = File.dirname $0
75
-
76
- Dir.glob("#{script_path}/scripts/*.rb").each do |path|
77
- script = Script.new path
78
- script.__client = self
79
-
80
- @scripts << script
93
+ if respond_to? name
94
+ __send__ name, network, message
81
95
  end
82
96
  end
83
97
 
84
- # Unload all scripts gracefully that have been loaded into the client.
85
- #
86
- # @see Script#unload!
87
- def unload_scripts
88
- # Unload script extensions.
89
- Script.unload_extensions!
90
-
91
- @scripts.each do |script|
92
- script.unload!
93
- end.clear
94
- end
95
-
96
98
  # Called when a network connection is either closed, or terminated.
97
99
  def network_connection_closed network
98
100
  emit :connection_close, network
@@ -103,8 +105,6 @@ module Blur
103
105
  #
104
106
  # @param [optional, Symbol] signal The signal received by the system, if any.
105
107
  def quit signal = :SIGINT
106
- unload_scripts
107
-
108
108
  @networks.each do |network|
109
109
  network.transmit :QUIT, "Got SIGINT?"
110
110
  network.disconnect
@@ -112,46 +112,77 @@ module Blur
112
112
 
113
113
  EventMachine.stop
114
114
  end
115
-
116
- private
117
- # Finds all callbacks with name `name` and then calls them.
118
- # It also sends `name` to {Script} if the script responds to `name`, to all
119
- # available scripts.
120
- #
121
- # @param [Symbol] name the corresponding event-handlers name.
122
- # @param [...] args Arguments that is passed to the event-handler.
123
- # @private
124
- def emit name, *args
125
- EM.defer do
126
- @callbacks[name].each do |callback|
127
- begin
128
- callback.call *args
129
- rescue Exception => e
130
- log.error "Callback `#{name}' threw an exception - #{exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
131
- puts exception.backtrace.join "\n"
132
- end
133
- end if @callbacks[name]
134
-
135
- scripts = @scripts.select{|script| script.__emissions.include? name }
136
- scripts.each do |script|
137
- begin
138
- script.__send__ name, *args
139
- rescue Exception => exception
140
- log.error "#{File.basename(script.__path) << " - " << exception.message ^ :bold} on line #{exception.line.to_s ^ :bold}"
141
- puts exception.backtrace.join "\n"
142
- end
115
+
116
+ # Reloads configuration file and scripts.
117
+ def reload!
118
+ EM.schedule do
119
+ unload_scripts!
120
+ load_config!
121
+ load_scripts!
122
+
123
+ yield if block_given?
124
+ end
125
+ end
126
+
127
+ # Loads all scripts in the script directory.
128
+ def load_scripts!
129
+ scripts_dir = File.expand_path @config['blur']['scripts_dir']
130
+ scripts_cache_dir = File.expand_path @config['blur']['cache_dir']
131
+
132
+ Dir.glob File.join(scripts_dir, '*.rb') do |file|
133
+ begin
134
+ load file, true
135
+ rescue Exception => e
136
+ STDERR.puts "The script `#{file}' failed to load"
137
+ STDERR.puts "#{e.class}: #{e.message}"
138
+ STDERR.puts
139
+ STDERR.puts 'Backtrace:', '---', e.backtrace
143
140
  end
144
141
  end
142
+
143
+ scripts_config = @config['scripts']
144
+
145
+ Blur.scripts.each do |name, superscript|
146
+ script = superscript.allocate
147
+ script.cache = ScriptCache.load name, scripts_cache_dir
148
+ script.config = scripts_config.fetch name, {}
149
+ script._client_ref = self
150
+ script.send :initialize
151
+
152
+ @scripts[name] = script
153
+ end
154
+
155
+ emit :scripts_loaded
145
156
  end
146
-
147
- # Stores the block as an event-handler with name `name`.
157
+
158
+ # Unloads initialized scripts and superscripts.
148
159
  #
149
- # @param [Symbol] name the corresponding event-handlers name.
150
- # @param [Block] block the event-handlers block that serves as a trigger.
151
- # @private
152
- def catch name, &block
153
- (@callbacks[name] ||= []) << block
160
+ # This method will call #unloaded on the instance of each loaded script to
161
+ # give it a chance to clean up any resources.
162
+ def unload_scripts!
163
+ @scripts.each do |name, script|
164
+ script.__send__ :unloaded if script.respond_to? :unloaded
165
+ end.clear
166
+
167
+ Blur.reset_scripts!
168
+ end
169
+
170
+ private
171
+
172
+ # Load the user-specified configuration file.
173
+ #
174
+ # @returns true on success, false otherwise.
175
+ def load_config!
176
+ config = YAML.load_file @config_path
177
+
178
+ if config.key? @environment
179
+ @config = config[@environment]
180
+ @config.deeper_merge! DEFAULT_CONFIG
181
+
182
+ emit :config_load
183
+ else
184
+ raise Error, "No configuration found for specified environment `#{@environment}'"
185
+ end
154
186
  end
155
-
156
187
  end
157
188
  end