system_browser 0.1.0
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 +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENCE.txt +19 -0
- data/README.md +72 -0
- data/VERSION +1 -0
- data/lib/system_browser.rb +52 -0
- data/lib/system_browser/behaviour.rb +30 -0
- data/lib/system_browser/client.rb +56 -0
- data/lib/system_browser/gem2markdown.rb +117 -0
- data/lib/system_browser/helpers/behaviour_service_helper.rb +14 -0
- data/lib/system_browser/helpers/gem_service_helper.rb +13 -0
- data/lib/system_browser/request.rb +43 -0
- data/lib/system_browser/request_processor.rb +74 -0
- data/lib/system_browser/response.rb +22 -0
- data/lib/system_browser/server.rb +82 -0
- data/lib/system_browser/services/abstract_service.rb +15 -0
- data/lib/system_browser/services/behaviour_service.rb +68 -0
- data/lib/system_browser/services/gem_service.rb +82 -0
- data/lib/system_browser/services/method_service.rb +30 -0
- data/lib/system_browser/services/source_service.rb +60 -0
- data/lib/system_browser/session.rb +108 -0
- data/lib/system_browser/slogger.rb +22 -0
- metadata +149 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: a327e613373ddd9ce65d00f6709939646690211e
         | 
| 4 | 
            +
              data.tar.gz: e2fa5ca9c87a732d6ec25579d2f90ef185f0d11b
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 24af54e9b78af57ee3ec68a3b4f49d43cdfe2dbb956950476492578c025e57cc9f77208324e035d436d64a634a14a5b148553e8041581aff672882b02070a6d0
         | 
| 7 | 
            +
              data.tar.gz: 332ec858e408c9dd782d72f97ed3dcd43fbc4093ad3411c6cfe7419d27c856034f5e13f8f8439cd34f3077e6082cfb365dedcbc3e5c597adc45bb20318c39b3d
         | 
    
        data/CHANGELOG.md
    ADDED
    
    
    
        data/LICENCE.txt
    ADDED
    
    | @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            Copyright (C) 2015 Kyrylo Silin
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            This software is provided 'as-is', without any express or implied
         | 
| 4 | 
            +
            warranty. In no event will the authors be held liable for any damages
         | 
| 5 | 
            +
            arising from the use of this software.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Permission is granted to anyone to use this software for any purpose,
         | 
| 8 | 
            +
            including commercial applications, and to alter it and redistribute it
         | 
| 9 | 
            +
            freely, subject to the following restrictions:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            1. The origin of this software must not be misrepresented; you must not
         | 
| 12 | 
            +
               claim that you wrote the original software. If you use this software
         | 
| 13 | 
            +
               in a product, an acknowledgment in the product documentation would be
         | 
| 14 | 
            +
               appreciated but is not required.
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            2. Altered source versions must be plainly marked as such, and must not be
         | 
| 17 | 
            +
               misrepresented as being the original software.
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            3. This notice may not be removed or altered from any source distribution.
         | 
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,72 @@ | |
| 1 | 
            +
            System Browser
         | 
| 2 | 
            +
            ==
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            * [Repository](https://github.com/kyrylo/system_browser_server/)
         | 
| 5 | 
            +
            * [Client][client]
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Description
         | 
| 8 | 
            +
            -----------
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            System Browser is a Ruby gem that serves as a bridge between a Ruby process
         | 
| 11 | 
            +
            and the [System Browser Client][client]. It allows you to browse Ruby
         | 
| 12 | 
            +
            behaviours (classes and modules), its methods and the methods' source code.
         | 
| 13 | 
            +
            _Make sure that you have the client installed_.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            Examples
         | 
| 16 | 
            +
            --------
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ### Basic example
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            ```ruby
         | 
| 21 | 
            +
            require 'system_browser'
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            SystemBrowser.start
         | 
| 24 | 
            +
            ```
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            ### Nonblocking start
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            By default `SystemBrowser.start` blocks the current thread. This is useful if you
         | 
| 29 | 
            +
            launch the browser from a small script. If you start the browser inside a
         | 
| 30 | 
            +
            complex framework such as Rails, blocking may be unwanted. In this case start
         | 
| 31 | 
            +
            the browser like this:
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            ```ruby
         | 
| 34 | 
            +
            SystemBrowser.start(block: false)
         | 
| 35 | 
            +
            ```
         | 
| 36 | 
            +
             | 
| 37 | 
            +
            The `block` flag will run the browser in a separate thread and return it (it's
         | 
| 38 | 
            +
            up to you if you want to join it).
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            ### Debugging
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            If you wish to contribute, you may find the `debug` flag useful. For debugging
         | 
| 43 | 
            +
            purposes invoke the browser like this:
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            ```ruby
         | 
| 46 | 
            +
            SystemBrowser.start(debug: true)
         | 
| 47 | 
            +
            ```
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            For additional information see the `examples/` directory.
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            Installation
         | 
| 52 | 
            +
            ------------
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            All you need is to install the gem.
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                gem install system_browser
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            Limitations
         | 
| 59 | 
            +
            -----------
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            Supports *only* CRuby.
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            * CRuby 2.2.2 and higher
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            Other Ruby versions were not tested, but in theory Ruby 2.2.x should work fine.
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            License
         | 
| 68 | 
            +
            -------
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            The project uses the Zlib License. See LICENCE.txt file for more information.
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            [client]: https://github.com/kyrylo/system_browser_client/
         | 
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.1.0
         | 
| @@ -0,0 +1,52 @@ | |
| 1 | 
            +
            require 'socket'
         | 
| 2 | 
            +
            require 'json'
         | 
| 3 | 
            +
            require 'logger'
         | 
| 4 | 
            +
            require 'shellwords'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'system_navigation'
         | 
| 7 | 
            +
            require 'core_classes'
         | 
| 8 | 
            +
            require 'coderay'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            require_relative 'system_browser/server'
         | 
| 11 | 
            +
            require_relative 'system_browser/client'
         | 
| 12 | 
            +
            require_relative 'system_browser/session'
         | 
| 13 | 
            +
            require_relative 'system_browser/request'
         | 
| 14 | 
            +
            require_relative 'system_browser/request_processor'
         | 
| 15 | 
            +
            require_relative 'system_browser/response'
         | 
| 16 | 
            +
            require_relative 'system_browser/slogger'
         | 
| 17 | 
            +
            require_relative 'system_browser/behaviour'
         | 
| 18 | 
            +
            require_relative 'system_browser/gem2markdown'
         | 
| 19 | 
            +
            require_relative 'system_browser/helpers/gem_service_helper'
         | 
| 20 | 
            +
            require_relative 'system_browser/helpers/behaviour_service_helper'
         | 
| 21 | 
            +
            require_relative 'system_browser/services/abstract_service'
         | 
| 22 | 
            +
            require_relative 'system_browser/services/gem_service'
         | 
| 23 | 
            +
            require_relative 'system_browser/services/behaviour_service'
         | 
| 24 | 
            +
            require_relative 'system_browser/services/method_service'
         | 
| 25 | 
            +
            require_relative 'system_browser/services/source_service'
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            module SystemBrowser
         | 
| 28 | 
            +
              ##
         | 
| 29 | 
            +
              # Starts the system browser.
         | 
| 30 | 
            +
              #
         | 
| 31 | 
            +
              # @param debug [Boolean] If true, prints debugging information
         | 
| 32 | 
            +
              # @param nonblock [Boolean] If true, then creates a new thread. Otherwise
         | 
| 33 | 
            +
              #   runs in the current thread
         | 
| 34 | 
            +
              # @return [Session.init]
         | 
| 35 | 
            +
              def self.start(debug: false, block: true)
         | 
| 36 | 
            +
                $DEBUG_SB = debug
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                if $DEBUG_SB
         | 
| 39 | 
            +
                  Thread.abort_on_exception = true
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                if block
         | 
| 43 | 
            +
                  SLogger.debug('[browser] Initialising a session, blocking')
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  Session.init
         | 
| 46 | 
            +
                else
         | 
| 47 | 
            +
                  SLogger.debug('[browser] Initialising a session, NOT blocking')
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  Thread.new { Session.init }
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class Behaviour
         | 
| 3 | 
            +
                DEFAULT_INSPECT = /#<(?:Module|Class):(0x[0-9a-f]+)>/
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def self.from_str(behaviour_str)
         | 
| 6 | 
            +
                  self.new(behaviour_str).extract
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(behaviour_str)
         | 
| 10 | 
            +
                  @behaviour_str = behaviour_str
         | 
| 11 | 
            +
                  @sn = SystemNavigation.default
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def extract
         | 
| 15 | 
            +
                  behaviour = eval(@behaviour_str)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  if behaviour.nil? && @behaviour_str.match(DEFAULT_INSPECT)
         | 
| 18 | 
            +
                    self.find_behaviour_by_object_id(Integer($1))
         | 
| 19 | 
            +
                  else
         | 
| 20 | 
            +
                    behaviour
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                protected
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def find_behaviour_by_object_id(behaviour_id)
         | 
| 27 | 
            +
                  @sn.all_objects.find { |obj| (obj.__id__ << 1) == behaviour_id  }
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,56 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class Client
         | 
| 3 | 
            +
                ##
         | 
| 4 | 
            +
                # The command that the client sends to the server at the session
         | 
| 5 | 
            +
                # initialisation.
         | 
| 6 | 
            +
                PID_COMMAND = 'set-window-pid'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                ##
         | 
| 9 | 
            +
                # The name of the executable of the client application.
         | 
| 10 | 
            +
                CLIENT_EXECUTABLE = 'system_browser'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                ##
         | 
| 13 | 
            +
                # @return [SystemBrowser::Session]
         | 
| 14 | 
            +
                attr_writer :session
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize
         | 
| 17 | 
            +
                  @init_pid = nil
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                ##
         | 
| 21 | 
            +
                # Spawns a new process in a new process group. I really wanted to find the
         | 
| 22 | 
            +
                # way to spawn the process in the same group. However, when I do that, I
         | 
| 23 | 
            +
                # cannot send any signals to the window anymore (the app crashes, because
         | 
| 24 | 
            +
                # the ruby process exits).
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                # @note +@init_pid+ and +@window_pid+ are two different processes. The
         | 
| 27 | 
            +
                # client uses +@init_id+ to bootstrap itself and +@window_pid+ is the
         | 
| 28 | 
            +
                # process that can be used to send signals to the client.
         | 
| 29 | 
            +
                #
         | 
| 30 | 
            +
                # @return [Integer] the process ID of the client application
         | 
| 31 | 
            +
                def start
         | 
| 32 | 
            +
                  @init_pid = spawn(CLIENT_EXECUTABLE, pgroup: true)
         | 
| 33 | 
            +
                  Process.wait(@init_pid)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  @init_pid
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                ##
         | 
| 39 | 
            +
                # @return [Integer] the process ID of the client process (window).
         | 
| 40 | 
            +
                def window_pid=(window_pid)
         | 
| 41 | 
            +
                  SLogger.debug("[client] setting window pid (#{window_pid})")
         | 
| 42 | 
            +
                  @window_pid = window_pid
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                ##
         | 
| 46 | 
            +
                # Destroys the window by sending the SIGINT signal (the window has its own
         | 
| 47 | 
            +
                # handlers to destroy itself, so it's not our job). Does not wait for
         | 
| 48 | 
            +
                # anything.
         | 
| 49 | 
            +
                # @return [void]
         | 
| 50 | 
            +
                def close
         | 
| 51 | 
            +
                  SLogger.debug("[client] interrupting window (#{@window_pid})...")
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  Process.kill(:INT, @window_pid)
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
            end
         | 
| @@ -0,0 +1,117 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class Gem2Markdown
         | 
| 3 | 
            +
                def self.convert(gem)
         | 
| 4 | 
            +
                  self.new(gem).convert
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(gem)
         | 
| 8 | 
            +
                  @gem = gem
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def convert
         | 
| 12 | 
            +
                  description = ''
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  [header,
         | 
| 15 | 
            +
                   summary,
         | 
| 16 | 
            +
                   homepage,
         | 
| 17 | 
            +
                   license,
         | 
| 18 | 
            +
                   author,
         | 
| 19 | 
            +
                   email,
         | 
| 20 | 
            +
                   newline,
         | 
| 21 | 
            +
                   description,
         | 
| 22 | 
            +
                   newline(2)
         | 
| 23 | 
            +
                  ].each do |desc|
         | 
| 24 | 
            +
                    description += (desc || '')
         | 
| 25 | 
            +
                   end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  {
         | 
| 28 | 
            +
                    description: description,
         | 
| 29 | 
            +
                    development_deps: development_deps,
         | 
| 30 | 
            +
                    runtime_deps: runtime_deps
         | 
| 31 | 
            +
                  }
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                private
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                def header
         | 
| 37 | 
            +
                  "#{@gem.full_name}\n==" + newline
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def summary
         | 
| 41 | 
            +
                  if @gem.summary
         | 
| 42 | 
            +
                    @gem.summary + newline
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def homepage
         | 
| 47 | 
            +
                  if @gem.homepage
         | 
| 48 | 
            +
                    li("homepage: #{@gem.homepage}") + newline
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                def license
         | 
| 53 | 
            +
                  if @gem.licenses.any?
         | 
| 54 | 
            +
                    licenses = @gem.licenses.join(', ')
         | 
| 55 | 
            +
                    li("license: #{licenses}") + newline
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def author
         | 
| 60 | 
            +
                  if @gem.authors.any?
         | 
| 61 | 
            +
                    authors = @gem.authors.join(', ')
         | 
| 62 | 
            +
                    li("by #{authors}") + newline
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def description
         | 
| 67 | 
            +
                  if @gem.description
         | 
| 68 | 
            +
                    @gem.description + newline
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                def email
         | 
| 73 | 
            +
                  if @gem.email
         | 
| 74 | 
            +
                    li_h = 'email: '
         | 
| 75 | 
            +
                    email = @gem.email
         | 
| 76 | 
            +
                    item = nil
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    item = case email
         | 
| 79 | 
            +
                           when String
         | 
| 80 | 
            +
                             li(li_h + email)
         | 
| 81 | 
            +
                           when Array
         | 
| 82 | 
            +
                             li(li_h + email.join(', '))
         | 
| 83 | 
            +
                           else
         | 
| 84 | 
            +
                             fail RuntimeError, 'wrong email format'
         | 
| 85 | 
            +
                           end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    item + newline
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                def runtime_deps
         | 
| 93 | 
            +
                  if @gem.runtime_dependencies.any?
         | 
| 94 | 
            +
                    @gem.runtime_dependencies.map do |(name, _ver, _type)|
         | 
| 95 | 
            +
                      name.to_s
         | 
| 96 | 
            +
                    end
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def development_deps
         | 
| 101 | 
            +
                  if @gem.development_dependencies.any?
         | 
| 102 | 
            +
                    @gem.development_dependencies.map do |(name, _ver, _type)|
         | 
| 103 | 
            +
                      name.to_s
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
             | 
| 109 | 
            +
                def li(item)
         | 
| 110 | 
            +
                  '* ' + item
         | 
| 111 | 
            +
                end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                def newline(n = 1)
         | 
| 114 | 
            +
                  "\n" * n
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
            end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class Request
         | 
| 3 | 
            +
                ##
         | 
| 4 | 
            +
                # Represents a request that ends connection.
         | 
| 5 | 
            +
                FIN = 'FIN'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                attr_reader :action, :resource, :scope, :other, :callback_id
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def initialize(json)
         | 
| 10 | 
            +
                  @req = self.get_data(json)
         | 
| 11 | 
            +
                  @data = @req['system_browser_server']
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  @action = nil
         | 
| 14 | 
            +
                  @resource = nil
         | 
| 15 | 
            +
                  @scope = nil
         | 
| 16 | 
            +
                  @other = nil
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def parse
         | 
| 20 | 
            +
                  @callback_id = @req['callbackId']
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  @action = @data['action']
         | 
| 23 | 
            +
                  @resource = @data['resource']
         | 
| 24 | 
            +
                  @scope = @data['scope']
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  @other = @data['other']
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def sets_client_pid?
         | 
| 30 | 
            +
                  @action == Client::PID_COMMAND
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def client_pid
         | 
| 34 | 
            +
                  @resource
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                protected
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def get_data(json)
         | 
| 40 | 
            +
                  JSON.parse(json)
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
| @@ -0,0 +1,74 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class RequestProcessor
         | 
| 3 | 
            +
                ACTIONS = {
         | 
| 4 | 
            +
                  'get' => 'add',
         | 
| 5 | 
            +
                  'autoget' => 'autoadd'
         | 
| 6 | 
            +
                }.tap { |h| h.default = 'add' }
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def initialize(request:, session:)
         | 
| 9 | 
            +
                  @request = request
         | 
| 10 | 
            +
                  @session = session
         | 
| 11 | 
            +
                  @services = [
         | 
| 12 | 
            +
                    Services::GemService,
         | 
| 13 | 
            +
                    Services::BehaviourService,
         | 
| 14 | 
            +
                    Services::MethodService,
         | 
| 15 | 
            +
                    Services::SourceService
         | 
| 16 | 
            +
                  ]
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def process
         | 
| 20 | 
            +
                  @request.parse
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  if @request.sets_client_pid?
         | 
| 23 | 
            +
                    @session.set_client_pid(@request.client_pid)
         | 
| 24 | 
            +
                  else
         | 
| 25 | 
            +
                    self.process_services
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                protected
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def process_services
         | 
| 32 | 
            +
                  service = self.find_service_for(@request.resource).new(
         | 
| 33 | 
            +
                    data: @request.scope,
         | 
| 34 | 
            +
                    other: @request.other)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  data = service.__send__(@request.action)
         | 
| 37 | 
            +
                  data = self.replace_weird_characters(data) if data.instance_of?(String)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  action = self.process_action
         | 
| 40 | 
            +
                  scope = self.process_scope(action)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  data[:behaviour] = @request.scope if scope.empty?
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  action_str = "#{action}:#{@request.resource}:#{scope}"
         | 
| 45 | 
            +
                  response = Response.new(action: action_str, data: data)
         | 
| 46 | 
            +
                  response.set_callback_id(@request.callback_id)
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  @session.send(response)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def process_action
         | 
| 52 | 
            +
                  ACTIONS[@request.action]
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def process_scope(action)
         | 
| 56 | 
            +
                  case action
         | 
| 57 | 
            +
                  when 'add' then @request.scope
         | 
| 58 | 
            +
                  when 'autoadd' then ''
         | 
| 59 | 
            +
                  else @request.scope
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def find_service_for(req_service)
         | 
| 64 | 
            +
                  @services.find { |service| service.service_name == req_service }
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                ##
         | 
| 68 | 
            +
                # Temporary hack before we support weird characters for real.
         | 
| 69 | 
            +
                def replace_weird_characters(str)
         | 
| 70 | 
            +
                  ascii_str = str.force_encoding('ASCII-8BIT')
         | 
| 71 | 
            +
                  ascii_str.encode('UTF-8', undef: :replace, replace: '')
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class Response
         | 
| 3 | 
            +
                def initialize(data: nil, action: nil, resource: nil)
         | 
| 4 | 
            +
                  @response = {
         | 
| 5 | 
            +
                    callback_id: nil,
         | 
| 6 | 
            +
                    system_browser_client: {
         | 
| 7 | 
            +
                      action: action,
         | 
| 8 | 
            +
                      data: data,
         | 
| 9 | 
            +
                      resource: resource
         | 
| 10 | 
            +
                    }
         | 
| 11 | 
            +
                  }
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def set_callback_id(callback_id)
         | 
| 15 | 
            +
                  @response[:callback_id] = callback_id
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def to_json
         | 
| 19 | 
            +
                  JSON.generate(@response) + "\n"
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              class Server
         | 
| 3 | 
            +
                ##
         | 
| 4 | 
            +
                # @return [SystemBrowser::Session]
         | 
| 5 | 
            +
                attr_accessor :session
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(port = 9696)
         | 
| 8 | 
            +
                  self.create_tcpserver(port)
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                ##
         | 
| 12 | 
            +
                # Starts the TCP server.
         | 
| 13 | 
            +
                #
         | 
| 14 | 
            +
                # @note This method blocks the thread.
         | 
| 15 | 
            +
                def start
         | 
| 16 | 
            +
                  Socket.accept_loop(@tcpserver) do |connection|
         | 
| 17 | 
            +
                    SLogger.debug("[server] accepted a new connection (#{connection})")
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    self.session.connection = connection
         | 
| 20 | 
            +
                    self.handle_connection(connection)
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                rescue IOError
         | 
| 23 | 
            +
                  Thread.exit
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def shutdown
         | 
| 27 | 
            +
                  SLogger.debug("[server] shutting down the TCP server...")
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  @tcpserver.close
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                protected
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                ##
         | 
| 35 | 
            +
                # Creates a new TCP server and tries to find a free port.
         | 
| 36 | 
            +
                # @return [void]
         | 
| 37 | 
            +
                def create_tcpserver(port)
         | 
| 38 | 
            +
                  @tcpserver = TCPServer.new(port)
         | 
| 39 | 
            +
                rescue Errno::EADDRINUSE
         | 
| 40 | 
            +
                  SLogger.debug("[server] port #{port} is occupied. Trying port #{port + 1}")
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  port += 1
         | 
| 43 | 
            +
                  retry
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                ##
         | 
| 47 | 
            +
                # Handles incoming connections.
         | 
| 48 | 
            +
                #
         | 
| 49 | 
            +
                # @param connection [TCPSocket]
         | 
| 50 | 
            +
                # @return [void]
         | 
| 51 | 
            +
                def handle_connection(connection)
         | 
| 52 | 
            +
                  loop do
         | 
| 53 | 
            +
                    unless readval = connection.gets
         | 
| 54 | 
            +
                      SLogger.debug("[server] connection #{connection} interrupted")
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      shutdown
         | 
| 57 | 
            +
                      break
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    if readval == Request::FIN
         | 
| 61 | 
            +
                      SLogger.debug("[server] received the FIN request")
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                      self.session.destroy
         | 
| 64 | 
            +
                      break
         | 
| 65 | 
            +
                    else
         | 
| 66 | 
            +
                      SLogger.debug("[server] received a request")
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      self.process_request(Request.new(readval))
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                ##
         | 
| 74 | 
            +
                # @param request [SystemBrowser::Request]
         | 
| 75 | 
            +
                # @return [void]
         | 
| 76 | 
            +
                def process_request(request)
         | 
| 77 | 
            +
                  RequestProcessor.new(request: request, session: self.session).process
         | 
| 78 | 
            +
                rescue => e
         | 
| 79 | 
            +
                  SLogger.log_error(e)
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
| @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              module Services
         | 
| 3 | 
            +
                class AbstractService
         | 
| 4 | 
            +
                  def self.service_name
         | 
| 5 | 
            +
                    self.name.split('::').last.split('Service').first.downcase
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(data:, other: nil)
         | 
| 9 | 
            +
                    @sn = SystemNavigation.default
         | 
| 10 | 
            +
                    @data = data
         | 
| 11 | 
            +
                    @other = other
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
            end
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              module Services
         | 
| 3 | 
            +
                class BehaviourService < AbstractService
         | 
| 4 | 
            +
                  CACHED_BEHAVIOURS = {}
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def self.all_from(gem)
         | 
| 7 | 
            +
                    if CACHED_BEHAVIOURS.has_key?(gem)
         | 
| 8 | 
            +
                      CACHED_BEHAVIOURS[gem]
         | 
| 9 | 
            +
                    else
         | 
| 10 | 
            +
                      CACHED_BEHAVIOURS[gem] = SystemNavigation.new.all_classes_and_modules_in_gem_named(gem)
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
                  end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def get
         | 
| 15 | 
            +
                     self.behaviours.map do |behaviour|
         | 
| 16 | 
            +
                      is_module = behaviour.instance_of?(Module)
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      superclass = unless is_module
         | 
| 19 | 
            +
                                     behaviour.superclass
         | 
| 20 | 
            +
                                   end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      {name: (behaviour.name ? behaviour.name : behaviour.inspect),
         | 
| 23 | 
            +
                       isModule: is_module,
         | 
| 24 | 
            +
                       isException: behaviour.ancestors.include?(Exception),
         | 
| 25 | 
            +
                       superclass: superclass}
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def autoget
         | 
| 30 | 
            +
                    behaviour = SystemBrowser::Behaviour.from_str(@data)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    if CoreClasses.as_set.find { |c| c == behaviour }
         | 
| 33 | 
            +
                      {gem: GemService::CORE_LABEL}
         | 
| 34 | 
            +
                    elsif self.stdlib_behaviours.find { |c| c == behaviour }
         | 
| 35 | 
            +
                      {gem: GemService::STDLIB_LABEL}
         | 
| 36 | 
            +
                    else
         | 
| 37 | 
            +
                      behaviours = @sn.all_classes_and_modules_in_gem_named(@other)
         | 
| 38 | 
            +
                      gem = if behaviours.include?(behaviour)
         | 
| 39 | 
            +
                              @other
         | 
| 40 | 
            +
                            else
         | 
| 41 | 
            +
                              self.all_gems.keys.find do |g|
         | 
| 42 | 
            +
                                behaviours = @sn.all_classes_and_modules_in_gem_named(g)
         | 
| 43 | 
            +
                                behaviours.include?(behaviour)
         | 
| 44 | 
            +
                              end
         | 
| 45 | 
            +
                            end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                      {gem: gem}
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  protected
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  include SystemBrowser::Helpers::GemServiceHelper
         | 
| 54 | 
            +
                  include SystemBrowser::Helpers::BehaviourServiceHelper
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def behaviours
         | 
| 57 | 
            +
                    case @data
         | 
| 58 | 
            +
                    when GemService::CORE_LABEL
         | 
| 59 | 
            +
                      CoreClasses.as_set
         | 
| 60 | 
            +
                    when GemService::STDLIB_LABEL
         | 
| 61 | 
            +
                      self.stdlib_behaviours
         | 
| 62 | 
            +
                    else
         | 
| 63 | 
            +
                      self.class.all_from(@data)
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              module Services
         | 
| 3 | 
            +
                class GemService < AbstractService
         | 
| 4 | 
            +
                  CORE_LABEL = 'Ruby Core'
         | 
| 5 | 
            +
                  STDLIB_LABEL = 'Ruby Stdlib'
         | 
| 6 | 
            +
                  DEFAULT_GEMS = [{name: CORE_LABEL}, {name: STDLIB_LABEL}]
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def get
         | 
| 9 | 
            +
                    gems = self.all_gems.map { |gem| {name: gem.first} }
         | 
| 10 | 
            +
                    [*DEFAULT_GEMS, *gems]
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def description(*args)
         | 
| 14 | 
            +
                    gem = self.find_gem(@data)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    case @data
         | 
| 17 | 
            +
                    when CORE_LABEL
         | 
| 18 | 
            +
                      desc = <<DESC
         | 
| 19 | 
            +
            Ruby Core-#{RUBY_VERSION}
         | 
| 20 | 
            +
            ===
         | 
| 21 | 
            +
            The Ruby Core defines common Ruby behaviours available to every program.
         | 
| 22 | 
            +
            DESC
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      {
         | 
| 25 | 
            +
                        description: desc,
         | 
| 26 | 
            +
                        behaviours: self.count_behaviours(CoreClasses.as_set),
         | 
| 27 | 
            +
                        development_deps: [],
         | 
| 28 | 
            +
                        runtime_deps: [],
         | 
| 29 | 
            +
                      }
         | 
| 30 | 
            +
                    when STDLIB_LABEL
         | 
| 31 | 
            +
                      desc = <<DESC
         | 
| 32 | 
            +
            Ruby Standard Library-#{RUBY_VERSION}
         | 
| 33 | 
            +
            ===
         | 
| 34 | 
            +
            The Ruby Standard Library is a vast collection of classes and modules that you can require in your code for additional features. System Browser shows only those behaviours that were required by this Ruby process.
         | 
| 35 | 
            +
            DESC
         | 
| 36 | 
            +
                      {
         | 
| 37 | 
            +
                        description: desc,
         | 
| 38 | 
            +
                        behaviours: self.count_behaviours(BehaviourService.stdlib_behaviours),
         | 
| 39 | 
            +
                        development_deps: [],
         | 
| 40 | 
            +
                        runtime_deps: []
         | 
| 41 | 
            +
                      }
         | 
| 42 | 
            +
                    else
         | 
| 43 | 
            +
                      gemdata = Gem2Markdown.convert(gem)
         | 
| 44 | 
            +
                      behs = BehaviourService.all_from(gem.name)
         | 
| 45 | 
            +
                      gemdata[:behaviours] = count_behaviours(behs)
         | 
| 46 | 
            +
                      gemdata
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def open
         | 
| 51 | 
            +
                    editor = [ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? }
         | 
| 52 | 
            +
                    path = self.find_gem(@data).full_gem_path
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    command = [*Shellwords.split(editor), path]
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    system(*command)
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    :ok
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  protected
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  include SystemBrowser::Helpers::GemServiceHelper
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def count_behaviours(collection)
         | 
| 66 | 
            +
                    behaviours = {}
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    grouped = collection.group_by(&:class)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    exceptions = (grouped[Class] || []).group_by do |beh|
         | 
| 71 | 
            +
                      beh.ancestors.include?(Exception)
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    behaviours['modules'] = (grouped[Module] || []).count
         | 
| 75 | 
            +
                    behaviours['classes'] = (exceptions[false] || []).count
         | 
| 76 | 
            +
                    behaviours['exceptions'] = (exceptions[true] || []).count
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    behaviours
         | 
| 79 | 
            +
                  end
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              module Services
         | 
| 3 | 
            +
                class MethodService < AbstractService
         | 
| 4 | 
            +
                  def get
         | 
| 5 | 
            +
                    behaviour = SystemBrowser::Behaviour.from_str(@data)
         | 
| 6 | 
            +
                    method_hash = @sn.all_methods_in_behavior(behaviour)
         | 
| 7 | 
            +
                    method_names_hash(method_hash)
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  protected
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def method_names_hash(method_hash)
         | 
| 13 | 
            +
                    new_h = {}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    method_hash.keys.each do |key|
         | 
| 16 | 
            +
                      new_val = method_hash[key].map do |k, values|
         | 
| 17 | 
            +
                        {
         | 
| 18 | 
            +
                          k => values.map do |m|
         | 
| 19 | 
            +
                           {name: m.name.to_s, c_method: m.source_location.nil? }
         | 
| 20 | 
            +
                          end
         | 
| 21 | 
            +
                        }
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                      new_h[key] = new_val.first.merge(new_val.last)
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    new_h
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              module Services
         | 
| 3 | 
            +
                class SourceService < AbstractService
         | 
| 4 | 
            +
                  def get
         | 
| 5 | 
            +
                    method = @other['method']['displayName']
         | 
| 6 | 
            +
                    owner = SystemBrowser::Behaviour.from_str(@other['owner'])
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    owner = (method.start_with?('#') ? owner : owner.singleton_class)
         | 
| 9 | 
            +
                    unbound_method = owner.instance_method(method[1..-1].to_sym)
         | 
| 10 | 
            +
                    source = FastMethodSource.comment_and_source_for(unbound_method)
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    CodeRay.scan(self.unindent(source), :ruby).div
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  protected
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  # Remove any common leading whitespace from every line in `text`.
         | 
| 18 | 
            +
                  #
         | 
| 19 | 
            +
                  # This can be used to make a HEREDOC line up with the left margin, without
         | 
| 20 | 
            +
                  # sacrificing the indentation level of the source code.
         | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
                  # e.g.
         | 
| 23 | 
            +
                  #   opt.banner unindent <<-USAGE
         | 
| 24 | 
            +
                  #     Lorem ipsum dolor sit amet, consectetur adipisicing elit,
         | 
| 25 | 
            +
                  #     sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
         | 
| 26 | 
            +
                  #       "Ut enim ad minim veniam."
         | 
| 27 | 
            +
                  #   USAGE
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # Heavily based on textwrap.dedent from Python, which is:
         | 
| 30 | 
            +
                  #   Copyright (C) 1999-2001 Gregory P. Ward.
         | 
| 31 | 
            +
                  #   Copyright (C) 2002, 2003 Python Software Foundation.
         | 
| 32 | 
            +
                  #   Written by Greg Ward <gward@python.net>
         | 
| 33 | 
            +
                  #
         | 
| 34 | 
            +
                  #   Licensed under <http://docs.python.org/license.html>
         | 
| 35 | 
            +
                  #   From <http://hg.python.org/cpython/file/6b9f0a6efaeb/Lib/textwrap.py>
         | 
| 36 | 
            +
                  #
         | 
| 37 | 
            +
                  # @param [String] text The text from which to remove indentation
         | 
| 38 | 
            +
                  # @return [String] The text with indentation stripped.
         | 
| 39 | 
            +
                  def unindent(text, left_padding = 0)
         | 
| 40 | 
            +
                    # Empty blank lines
         | 
| 41 | 
            +
                    text = text.sub(/^[ \t]+$/, '')
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # Find the longest common whitespace to all indented lines
         | 
| 44 | 
            +
                    # Ignore lines containing just -- or ++ as these seem to be used by
         | 
| 45 | 
            +
                    # comment authors as delimeters.
         | 
| 46 | 
            +
                    margin = text.scan(/^[ \t]*(?!--\n|\+\+\n)(?=[^ \t\n])/).inject do |current_margin, next_indent|
         | 
| 47 | 
            +
                      if next_indent.start_with?(current_margin)
         | 
| 48 | 
            +
                        current_margin
         | 
| 49 | 
            +
                      elsif current_margin.start_with?(next_indent)
         | 
| 50 | 
            +
                        next_indent
         | 
| 51 | 
            +
                      else
         | 
| 52 | 
            +
                        ""
         | 
| 53 | 
            +
                      end
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    text.gsub(/^#{margin}/, ' ' * left_padding)
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              ##
         | 
| 3 | 
            +
              # This class glues {SystemBrowser::Server} and {SystemBrowser::Client}
         | 
| 4 | 
            +
              # providing the support for interaction between them.
         | 
| 5 | 
            +
              class Session
         | 
| 6 | 
            +
                @@running_session = false
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                ##
         | 
| 9 | 
            +
                # Initialises a new session.
         | 
| 10 | 
            +
                def self.init
         | 
| 11 | 
            +
                  if @@running_session
         | 
| 12 | 
            +
                    SLogger.debug("can't init a new session! Kill the old one first")
         | 
| 13 | 
            +
                    return
         | 
| 14 | 
            +
                  else
         | 
| 15 | 
            +
                    @@running_session = true
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  self.new(Server.new, Client.new).init
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                # @return [TCPSocket] the connection between the server and the client
         | 
| 22 | 
            +
                attr_reader :connection
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def initialize(server, client)
         | 
| 25 | 
            +
                  @server = server
         | 
| 26 | 
            +
                  @client = client
         | 
| 27 | 
            +
                  [@server, @client].each { |o| o.session = self }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  @previous_sigint_callback = nil
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  self.register_sigint_hook
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                ##
         | 
| 35 | 
            +
                # Runs {SystemBrowser::Server} in background. Invokes {SystemBrowser::Client}
         | 
| 36 | 
            +
                # and suspends the calling thread for the duration of the client.
         | 
| 37 | 
            +
                # @return [void]
         | 
| 38 | 
            +
                def init
         | 
| 39 | 
            +
                  Thread.new { @server.start }
         | 
| 40 | 
            +
                  Thread.new { @client.start }.join
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  true
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def destroy
         | 
| 46 | 
            +
                  self.restore_previous_sigint
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  @client.close
         | 
| 49 | 
            +
                  @server.shutdown
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  @@running_session = false
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  SLogger.debug('[SESSION] the session was destroyed')
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                ##
         | 
| 57 | 
            +
                # Sets the client's window pid (real pid).
         | 
| 58 | 
            +
                def set_client_pid(pid)
         | 
| 59 | 
            +
                  @client.window_pid = pid
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                ##
         | 
| 63 | 
            +
                # Sends a response to the client.
         | 
| 64 | 
            +
                #
         | 
| 65 | 
            +
                # @param response [SystemBrowser::Response] the data to be sent to the client
         | 
| 66 | 
            +
                # @return [void]
         | 
| 67 | 
            +
                def send(response)
         | 
| 68 | 
            +
                  self.connection.puts(response.to_json)
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                ##
         | 
| 72 | 
            +
                # @param connection [TCPSocket] the connection between the server and the
         | 
| 73 | 
            +
                # client
         | 
| 74 | 
            +
                # @return [void]
         | 
| 75 | 
            +
                def connection=(connection)
         | 
| 76 | 
            +
                  @connection = connection
         | 
| 77 | 
            +
                  self.initialize_connection
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                protected
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                ##
         | 
| 83 | 
            +
                # This method bootstraps the connection between the server and the client.
         | 
| 84 | 
            +
                # @return [void]
         | 
| 85 | 
            +
                def initialize_connection
         | 
| 86 | 
            +
                  self.send(Response.new(action: 'init'))
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def restore_previous_sigint
         | 
| 90 | 
            +
                  Signal.trap(:INT, @previous_sigint_callback)
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def register_sigint_hook
         | 
| 94 | 
            +
                  @previous_sigint_callback = Signal.trap(:INT, '')
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  Signal.trap(:INT) do
         | 
| 97 | 
            +
                    SLogger.debug('[SESSION] received Ctrl-C, killing myself softly')
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    self.destroy
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    if @previous_sigint_callback.instance_of?(Proc)
         | 
| 102 | 
            +
                      self.restore_previous_sigint
         | 
| 103 | 
            +
                      @previous_sigint_callback.call
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            module SystemBrowser
         | 
| 2 | 
            +
              module SLogger
         | 
| 3 | 
            +
                class << self
         | 
| 4 | 
            +
                  @@logger = ::Logger.new(STDOUT)
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  [:debug, :error, :info].each do |m|
         | 
| 7 | 
            +
                    define_method(m) do |*args, &block|
         | 
| 8 | 
            +
                      if $DEBUG_SB
         | 
| 9 | 
            +
                        # Avoid the 'log writing failed due to trap' message in trap contexts.
         | 
| 10 | 
            +
                        Thread.new { @@logger.__send__(m, *args, &block) }.join
         | 
| 11 | 
            +
                      end
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def log_error(error)
         | 
| 16 | 
            +
                    SLogger.error(error.class) do
         | 
| 17 | 
            +
                      [error.to_s, error.backtrace].join("\n")
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,149 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: system_browser
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.1.0
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Kyrylo Silin
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2015-07-20 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: system_navigation
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: core_classes
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '0'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '0'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: coderay
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - "~>"
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '1.1'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - "~>"
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '1.1'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: bundler
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - "~>"
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '1.9'
         | 
| 62 | 
            +
              type: :development
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - "~>"
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '1.9'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: rake
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - "~>"
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '10.4'
         | 
| 76 | 
            +
              type: :development
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - "~>"
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '10.4'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: pry
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - "~>"
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '0.10'
         | 
| 90 | 
            +
              type: :development
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - "~>"
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '0.10'
         | 
| 97 | 
            +
            description: ''
         | 
| 98 | 
            +
            email: silin@kyrylo.org
         | 
| 99 | 
            +
            executables: []
         | 
| 100 | 
            +
            extensions: []
         | 
| 101 | 
            +
            extra_rdoc_files: []
         | 
| 102 | 
            +
            files:
         | 
| 103 | 
            +
            - CHANGELOG.md
         | 
| 104 | 
            +
            - LICENCE.txt
         | 
| 105 | 
            +
            - README.md
         | 
| 106 | 
            +
            - VERSION
         | 
| 107 | 
            +
            - lib/system_browser.rb
         | 
| 108 | 
            +
            - lib/system_browser/behaviour.rb
         | 
| 109 | 
            +
            - lib/system_browser/client.rb
         | 
| 110 | 
            +
            - lib/system_browser/gem2markdown.rb
         | 
| 111 | 
            +
            - lib/system_browser/helpers/behaviour_service_helper.rb
         | 
| 112 | 
            +
            - lib/system_browser/helpers/gem_service_helper.rb
         | 
| 113 | 
            +
            - lib/system_browser/request.rb
         | 
| 114 | 
            +
            - lib/system_browser/request_processor.rb
         | 
| 115 | 
            +
            - lib/system_browser/response.rb
         | 
| 116 | 
            +
            - lib/system_browser/server.rb
         | 
| 117 | 
            +
            - lib/system_browser/services/abstract_service.rb
         | 
| 118 | 
            +
            - lib/system_browser/services/behaviour_service.rb
         | 
| 119 | 
            +
            - lib/system_browser/services/gem_service.rb
         | 
| 120 | 
            +
            - lib/system_browser/services/method_service.rb
         | 
| 121 | 
            +
            - lib/system_browser/services/source_service.rb
         | 
| 122 | 
            +
            - lib/system_browser/session.rb
         | 
| 123 | 
            +
            - lib/system_browser/slogger.rb
         | 
| 124 | 
            +
            homepage: https://github.com/kyrylo/system_browser
         | 
| 125 | 
            +
            licenses:
         | 
| 126 | 
            +
            - Zlib
         | 
| 127 | 
            +
            metadata: {}
         | 
| 128 | 
            +
            post_install_message: 
         | 
| 129 | 
            +
            rdoc_options: []
         | 
| 130 | 
            +
            require_paths:
         | 
| 131 | 
            +
            - lib
         | 
| 132 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 133 | 
            +
              requirements:
         | 
| 134 | 
            +
              - - ">="
         | 
| 135 | 
            +
                - !ruby/object:Gem::Version
         | 
| 136 | 
            +
                  version: '0'
         | 
| 137 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 138 | 
            +
              requirements:
         | 
| 139 | 
            +
              - - ">="
         | 
| 140 | 
            +
                - !ruby/object:Gem::Version
         | 
| 141 | 
            +
                  version: '0'
         | 
| 142 | 
            +
            requirements: []
         | 
| 143 | 
            +
            rubyforge_project: 
         | 
| 144 | 
            +
            rubygems_version: 2.4.5
         | 
| 145 | 
            +
            signing_key: 
         | 
| 146 | 
            +
            specification_version: 4
         | 
| 147 | 
            +
            summary: ''
         | 
| 148 | 
            +
            test_files: []
         | 
| 149 | 
            +
            has_rdoc: 
         |