GithubHelp home page GithubHelp logo

dylanninin / dylanninin.com Goto Github PK

View Code? Open in Web Editor NEW
8.0 4.0 5.0 20.39 MB

Personal blog powered by GitHub Services

Home Page: https://dylanninin.com

Ruby 58.04% CSS 11.05% HTML 30.91%
blog engineer jekyll digest reading mooc notes tech github-actions github-api

dylanninin.com's Introduction

Dylan's GitHub stats

Hi there 👋

dylanninin.com's People

Contributors

dylanninin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

dylanninin.com's Issues

Object/Class in Ruby

Bootstraps the class hierarchy

 //ruby/object.c
 *  Document-class: Class
 *
 *  Classes in Ruby are first-class objects---each is an instance of
 *  class <code>Class</code>.
 *
 *  Typically, you create a new class by using:
 *
 *    class Name
 *     # some code describing the class behavior
 *    end
 *
 *  When a new class is created, an object of type Class is initialized and
 *  assigned to a global constant (<code>Name</code> in this case).
 *
 * 
 * Classes, modules, and objects are interrelated. In the diagram
 *  that follows, the vertical arrows represent inheritance, and the
 *  parentheses metaclasses. All metaclasses are instances
 *  of the class `Class'.
 *
 * Initializes the world of objects and classes.
 *
 * At first, the function bootstraps the class hierarchy.
 * It initializes the most fundamental classes and their metaclasses.
 * - \c BasicObject
 * - \c Object
 * - \c Module
 * - \c Class
 * After the bootstrap step, the class hierarchy becomes as the following
 * diagram.
 *                             +---------+             +-...
 *                             |         |             |
 *             BasicObject-----|-->(BasicObject)-------|-...
 *                 ^           |         ^             |
 *                 |           |         |             |
 *              Object---------|----->(Object)---------|-...
 *                 ^           |         ^             |
 *                 |           |         |             |
 *                 +-------+   |         +--------+    |
 *                 |       |   |         |        |    |
 *                 |    Module-|---------|--->(Module)-|-...
 *                 |       ^   |         |        ^    |
 *                 |       |   |         |        |    |
 *                 |     Class-|---------|---->(Class)-|-...
 *                 |       ^   |         |        ^    |
 *                 |       +---+         |        +----+
 *                 |                     |
 *    obj--->OtherClass---------->(OtherClass)-----------...
 *
 * Then, the function defines classes, modules and methods as usual.
 * \ingroup class

Document-class: BasicObject

 //ruby/object.c
 *
 *  BasicObject is the parent class of all classes in Ruby.  It's an explicit
 *  blank class.
 *
 *  BasicObject can be used for creating object hierarchies independent of
 *  Ruby's object hierarchy, proxy objects like the Delegator class, or other
 *  uses where namespace pollution from Ruby's methods and classes must be
 *  avoided.
 *
 *  To avoid polluting BasicObject for other users an appropriately named
 *  subclass of BasicObject should be created instead of directly modifying
 *  BasicObject:
 *
 *    class MyObjectSystem < BasicObject
 *    end
 *
 *  BasicObject does not include Kernel (for methods like +puts+) and
 *  BasicObject is outside of the namespace of the standard library so common
 *  classes will not be found without using a full class path.
 *
 *  A variety of strategies can be used to provide useful portions of the
 *  standard library to subclasses of BasicObject.  A subclass could
 *  <code>include Kernel</code> to obtain +puts+, +exit+, etc.  A custom
 *  Kernel-like module could be created and included or delegation can be used
 *  via #method_missing:
 *
 *    class MyObjectSystem < BasicObject
 *      DELEGATE = [:puts, :p]
 *
 *      def method_missing(name, *args, &block)
 *        super unless DELEGATE.include? name
 *        ::Kernel.send(name, *args, &block)
 *      end
 *
 *      def respond_to_missing?(name, include_private = false)
 *        DELEGATE.include?(name) or super
 *      end
 *    end
 *
 *  Access to classes and modules from the Ruby standard library can be
 *  obtained in a BasicObject subclass by referencing the desired constant
 *  from the root like <code>::File</code> or <code>::Enumerator</code>.
 *  Like #method_missing, #const_missing can be used to delegate constant
 *  lookup to +Object+:
 *
 *    class MyObjectSystem < BasicObject
 *      def self.const_missing(name)
 *        ::Object.const_get(name)
 *      end
 *    end
 *

Document-class: Object

 //ruby/object.c
 *  Object is the default root of all Ruby objects.  Object inherits from
 *  BasicObject which allows creating alternate object hierarchies.  Methods
 *  on Object are available to all classes unless explicitly overridden.
 *
 *  Object mixes in the Kernel module, making the built-in kernel functions
 *  globally accessible.  Although the instance methods of Object are defined
 *  by the Kernel module, we have chosen to document them here for clarity.
 *
 *  When referencing constants in classes inheriting from Object you do not
 *  need to use the full namespace.  For example, referencing +File+ inside
 *  +YourClass+ will find the top-level File class.
 *
 *  In the descriptions of Object's methods, the parameter <i>symbol</i> refers
 *  to a symbol, which is either a quoted string or a Symbol (such as
 *  <code>:name</code>).
 */

Document-class: Kernel

//ruby/object.c
*
* The Kernel module is included by class Object, so its methods are
* available in every Ruby object.
*
* The Kernel instance methods are documented in class Object while the
* module methods are documented here.  These methods are called without a
* receiver and thus can be called in functional form:
*
*   sprintf "%.1f", 1.234 #=> "1.2"
*
*
rb_mKernel = rb_define_module("Kernel");
rb_include_module(rb_cObject, rb_mKernel);

Document-class: Module

A Module is a collection of methods and constants. The methods in a module may be instance methods or module methods. Instance methods appear as methods in a class when the module is included, module methods do not. Conversely, module methods may be called without creating an encapsulating object, while instance methods may not.

In the descriptions that follow, the parameter sym refers to a symbol, which is either a quoted string or a Symbol (such as :name).

module Mod
  include Math
  CONST = 1
  def meth
    #  ...
  end
end
Mod.class              #=> Module
Mod.constants          #=> [:CONST, :PI, :E]
Mod.instance_methods   #=> [:meth]

Init class hierarchy

//ruby/class.c
void
Init_class_hierarchy(void)
{
    rb_cBasicObject = boot_defclass("BasicObject", 0);
    rb_cObject = boot_defclass("Object", rb_cBasicObject);
    rb_gc_register_mark_object(rb_cObject);

    /* resolve class name ASAP for order-independence */
    rb_class_name(rb_cObject);

    rb_cModule = boot_defclass("Module", rb_cObject);
    rb_cClass =  boot_defclass("Class",  rb_cModule);

    rb_const_set(rb_cObject, rb_intern_const("BasicObject"), rb_cBasicObject);
    RBASIC_SET_CLASS(rb_cClass, rb_cClass);
    RBASIC_SET_CLASS(rb_cModule, rb_cClass);
    RBASIC_SET_CLASS(rb_cObject, rb_cClass);
    RBASIC_SET_CLASS(rb_cBasicObject, rb_cClass);
}

Rails Boot Process

The Rails Command

  • which rails
rails: aliased to _rails_command
  • which _rails_command
_rails_command () {
    if [ -e "bin/rails" ]
    then
        bin/rails $@
    elif [ -e "script/rails" ]
    then
        ruby script/rails $@
    elif [ -e "script/server" ]
    then
        ruby script/$@
    else
        command rails $@
    fi
}
  • bin/rails: base on your rails app's root directory
#!/usr/bin/env ruby
begin
  load File.expand_path('../spring', __FILE__)
rescue LoadError => e
  raise unless e.message.include?('spring')
end
APP_PATH = File.expand_path('../config/application', __dir__)
require_relative '../config/boot'
require 'rails/commands'
  • summary in bin/rails

    • Spring: is a Rails application preloader. It speeds up development by keeping your application running in the background so you don't need to boot it every time you run a test, rake task or migration.
    • APP_PATH: set the absolute path of Rails Application.
    • require_relative '../config/boot': execute bunder setup, install required gems
    ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
    
    require 'bundler/setup' # Set up gems listed in the Gemfile.
    • require 'rails/commands': load rails commands
    ##railties/lib/rails/commands.rb
    ARGV << '--help' if ARGV.empty?
    
    aliases = {
    "g"  => "generate",
    "d"  => "destroy",
    "c"  => "console",
    "s"  => "server",
    "db" => "dbconsole",
    "r"  => "runner",
    "t"  => "test"
    }
    
    command = ARGV.shift
    command = aliases[command] || command
    
    require 'rails/commands/commands_tasks'
    
    Rails::CommandsTasks.new(ARGV).run_command!(command)

Commands Tasks

    #railties/lib/rails/commands/commands_tasks.rb#L45

    def run_command!(command)
      command = parse_command(command)

      if COMMAND_WHITELIST.include?(command)
        send(command)
      else
        run_rake_task(command)
      end
    end
    # railties/lib/rails/commands/commands_tasks.rb#L81
    def server
      set_application_directory!
      require_command!("server")

      Rails::Server.new.tap do |server|
        # We need to require application after the server sets environment,
        # otherwise the --environment option given to the server won't propagate.
        require APP_PATH
        Dir.chdir(Rails.application.root)
        server.start
      end
    end
  • summary in rails server

    • set_application_directory!: set application directory, locate config.ru rack file.
      #railties/lib/rails/commands/commands_tasks.rb#L148
    
      # Change to the application's path if there is no config.ru file in current directory.
      # This allows us to run `rails server` from other directories, but still get
      # the main config.ru and properly set the tmp directory.
      def set_application_directory!
        Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
      end
    • require_command!("server"): require rails server, inherited from Rack Server.
      # railties/lib/rails/commands/commands_tasks.rb#L137
    
      def require_command!(command)
        require "rails/commands/#{command}"
      end
    • server.start: start rack server, than the app in running
    #railties/lib/rails/commands/server.rb#L7
    module Rails
     class Server < ::Rack::Server
      ...
      #railties/lib/rails/commands/server.rb#L51
      def initialize(*)
        super
        set_environment
      end
       ...
      #railties/lib/rails/commands/server.rb#L72
      def start
         print_boot_information
         trap(:INT) { exit }
         create_tmp_directories
         setup_dev_caching
         log_to_stdout if options[:log_stdout]
    
         super
       ensure
         # The '-h' option calls exit before @options is set.
         # If we call 'options' with it unset, we get double help banners.
         puts 'Exiting' unless @options && options[:daemonize]
       end

Rack Server, #4

    # lib/rack/server.rb#L51
    def initialize(options = nil)
      @ignore_options = []

      if options
        @use_default_options = false
        @options = options
        @app = options[:app] if options[:app]
      else
        argv = defined?(SPEC_ARGV) ? SPEC_ARGV : ARGV
        @use_default_options = true
        @options = parse_options(argv)
      end
    end

    def options
      merged_options = @use_default_options ? default_options.merge(@options) : @options
      merged_options.reject { |k, v| @ignore_options.include?(k) }
    end

    def default_options
      environment  = ENV['RACK_ENV'] || 'development'
      default_host = environment == 'development' ? 'localhost' : '0.0.0.0'

      {
        :environment => environment,
        :pid         => nil,
        :Port        => 9292,
        :Host        => default_host,
        :AccessLog   => [],
        :config      => "config.ru"
      }
    end

    def app
      @app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
    end
    ...
    def start &blk
      if options[:warn]
        $-w = true
      end

      if includes = options[:include]
        $LOAD_PATH.unshift(*includes)
      end

      if library = options[:require]
        require library
      end

      if options[:debug]
        $DEBUG = true
        require 'pp'
        p options[:server]
        pp wrapped_app
        pp app
      end

      check_pid! if options[:pid]

      # Touch the wrapped app, so that the config.ru is loaded before
      # daemonization (i.e. before chdir, etc).
      wrapped_app

      daemonize_app if options[:daemonize]

      write_pid if options[:pid]

      trap(:INT) do
        if server.respond_to?(:shutdown)
          server.shutdown
        else
          exit
        end
      end

      server.run wrapped_app, options, &blk
    end

    def server
      @_server ||= Rack::Handler.get(options[:server])

      unless @_server
        @_server = Rack::Handler.default

        # We already speak FastCGI
        @ignore_options = [:File, :Port] if @_server.to_s == 'Rack::Handler::FastCGI'
      end

      @_server
    end
    ...
     def build_app(app)
        middleware[options[:environment]].reverse_each do |middleware|
          middleware = middleware.call(self) if middleware.respond_to?(:call)
          next unless middleware
          klass, *args = middleware
          app = klass.new(app, *args)
        end
        app
      end

      def wrapped_app
        @wrapped_app ||= build_app app
      end
    def self.default
      # Guess.
      if ENV.include?("PHP_FCGI_CHILDREN")
        Rack::Handler::FastCGI
      elsif ENV.include?(REQUEST_METHOD)
        Rack::Handler::CGI
      elsif ENV.include?("RACK_HANDLER")
        self.get(ENV["RACK_HANDLER"])
      else
        pick ['puma', 'thin', 'webrick']
      end
    end
      ...
      def self.run(app, options={})
        if options[:File]
          STDIN.reopen(UNIXServer.new(options[:File]))
        elsif options[:Port]
          STDIN.reopen(TCPServer.new(options[:Host], options[:Port]))
        end
        FCGI.each { |request|
          serve request, app
        }
      end
      ...
      def self.serve(request, app)
        env = request.env
        env.delete "HTTP_CONTENT_LENGTH"

        env[SCRIPT_NAME] = ""  if env[SCRIPT_NAME] == "/"

        rack_input = RewindableInput.new(request.in)

        env.update(
          RACK_VERSION      => Rack::VERSION,
          RACK_INPUT        => rack_input,
          RACK_ERRORS       => request.err,
          RACK_MULTITHREAD  => false,
          RACK_MULTIPROCESS => true,
          RACK_RUNONCE      => false,
          RACK_URL_SCHEME   => ["yes", "on", "1"].include?(env[HTTPS]) ? "https" : "http"
        )

        env[QUERY_STRING] ||= ""
        env[HTTP_VERSION] ||= env[SERVER_PROTOCOL]
        env[REQUEST_PATH] ||= "/"
        env.delete "CONTENT_TYPE"  if env["CONTENT_TYPE"] == ""
        env.delete "CONTENT_LENGTH"  if env["CONTENT_LENGTH"] == ""

        begin
          status, headers, body = app.call(env)
          begin
            send_headers request.out, status, headers
            send_body request.out, body
          ensure
            body.close  if body.respond_to? :close
          end
        ensure
          rack_input.close
          request.finish
        end
      end
  • config.ru: the rack config file
# This file is used by Rack-based servers to start the application.

require_relative 'config/environment'

run Rails.application
  • config/environment.rb: load and initialize Rails application

    # Load the Rails application.
    require_relative 'application'
    
    # Initialize the Rails application.
    Rails.application.initialize!
  • config/application.rb: the Rails application

    require_relative 'boot'
    
    require "rails"
    # Pick the frameworks you want:
    require "active_model/railtie"
    require "active_job/railtie"
    require "active_record/railtie"
    require "action_controller/railtie"
    require "action_mailer/railtie"
    require "action_view/railtie"
    require "action_cable/engine"
    # require "sprockets/railtie"
    require "rails/test_unit/railtie"
    
    # Require the gems listed in Gemfile, including any gems
    # you've limited to :test, :development, or :production.
    Bundler.require(*Rails.groups)
    
    module MyBackend
    class Application < Rails::Application
      # Settings in config/environments/* take precedence over those specified here.
      # Application configuration should go into files in config/initializers
      # -- all .rb files in that directory are automatically loaded.
    
      # Only loads a smaller set of middleware suitable for API only apps.
      # Middleware like session, flash, cookies can be added back manually.
      # Skip views, helpers and assets when generating a new resource.
      config.api_only = true
      #config.time_zone = 'Beijing'
    
      # i18n config
      config.i18n.load_path += Dir[Rails.root.join('locales', '*.{rb,yml}').to_s]
      config.i18n.default_locale = 'zh-CN'
    end
    end

Rails Application

    # railties/lib/rails/application.rb#L78
    class Application < Engine
    autoload :Bootstrap,              'rails/application/bootstrap'
    autoload :Configuration,          'rails/application/configuration'
    autoload :DefaultMiddlewareStack, 'rails/application/default_middleware_stack'
    autoload :Finisher,               'rails/application/finisher'
    autoload :Railties,               'rails/engine/railties'
    autoload :RoutesReloader,         'rails/application/routes_reloader'
    ...
    # railties/lib/rails/application.rb#L348
    # Initialize the application passing the given group. By default, the
    # group is :default
    def initialize!(group=:default) #:nodoc:
      raise "Application has been already initialized." if @initialized
      run_initializers(group, self)
      @initialized = true
      self
    end
 # railties/lib/rails/engine.rb#L345
 class Engine < Railtie
    autoload :Configuration, "rails/engine/configuration"
    ...
    # railties/lib/rails/engine.rb#L519
    # Define the Rack API for this engine.
    def call(env)
      req = build_request env
      app.call req.env
    end
  • Application booting process: https://github.com/rails/rails/blob/master/railties/lib/rails/application.rb#L36

    # The application is also responsible for setting up and executing the booting
    # process. From the moment you require "config/application.rb" in your app,
    # the booting process goes like this:
    #
    #   1)  require "config/boot.rb" to setup load paths
    #   2)  require railties and engines
    #   3)  Define Rails.application as "class MyApp::Application < Rails::Application"
    #   4)  Run config.before_configuration callbacks
    #   5)  Load config/environments/ENV.rb
    #   6)  Run config.before_initialize callbacks
    #   7)  Run Railtie#initializer defined by railties, engines and application.
    #       One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
    #   8)  Custom Railtie#initializers added by railties, engines and applications are executed
    #   9)  Build the middleware stack and run to_prepare callbacks
    #   10) Run config.before_eager_load and eager_load! if eager_load is true
    #   11) Run config.after_initialize callbacks

Reference

Tests in Python

Some core concepts

http://www.martinfowler.com/articles/mocksArentStubs.html

  • Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists.
  • Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example).
  • Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Stubs may also record information about calls, such as an email gateway stub that remembers the messages it 'sent', or maybe only how many messages it 'sent'.
  • Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive.

Simple note

test

Reference

Abstract Data Types

Excerpts from 《Data Structures and Algorithms Using Python》

Introduction

Data items are represented within a computer as a sequence of binary digits. These sequences can appear very similar but have different meanings since computers can store and manipulate different types of data. For example, the binary sequence01001100110010110101110011011100 could be a string of characters, an integer value, or a real value. To distinguish between the different types of data, the term is often used to refer to a collection of values and the term data type to refer to a given type along with a collection of operations for manipulating values of the given type.

Programming languages commonly provide data types as part of the language itself. These data types, known as primitives, come in two categories: simple and complex. The simple data types consist of values that are in the most basic form and cannot be decomposed into smaller parts. Integer and real types, for example, consist of single numeric values. The complex data types, on the other hand, are constructed of multiple components consisting of simple types or other complex types. In Python, objects, strings, lists, and dictionaries, which can contain multiple values, are all examples of complex types. The primitive types provided by a language may not be sufficient for solving large complex problems. Thus, most languages allow for the construction of additional data types, known as user-defined types since they are defined by the programmer and not the language. Some of these data types can themselves be very complex.

Abstractions

To help manage complex problems and complex data types, computer scientists typically work with abstractions. An abstraction is a mechanism for separating the properties of an object and restricting the focus to those relevant in the current context. The user of the abstraction does not have to understand all of the details in order to utilize the object, but only those relevant to the current task or problem.

Two common types of abstractions encountered in computer science are procedural, or functional, abstraction and data abstraction. Procedural abstraction is the use of a function or method knowing what it does but ignoring how it's accomplished. Consider the mathematical square root function which you have probably used at some point. You know the function will compute the square root of a given number, but do you know how the square root is computed? Does it matter if you know how it is computed, or is simply knowing how to correctly use the function sufficient? Data abstraction is the separation of the properties of a data type (its values and operations) from the implementation of that data type. You have used strings in Python many times. But do you know how they are implemented? That is, do you know how the data is structured internally or how the various operations are implemented?

Typically, abstractions of complex problems occur in layers, with each higher layer adding more abstraction than the previous. Consider the problem of representing integer values on computers and performing arithmetic operations on those values. Figure 1.1 illustrates the common levels of abstractions used with integer arithmetic. At the lowest level is the hardware with little to no abstraction since it includes binary representations of the values and logic circuits for performing the arithmetic. Hardware designers would deal with integer arithmetic at this level and be concerned with its correct implementation. A higher level of abstraction for integer values and arithmetic is provided through assembly language, which involves working with binary values and individual instructions corresponding to the underlying hardware. Compiler writers and assembly language programmers would work with integer arithmetic at this level and must ensure the proper selection of assembly language instructions to compute a given mathematical expression. For example, suppose we wish to compute x = a + b - 5. At the assembly language level, this expression must be split into multiple instructions for loading the values from memory, storing them into registers, and then performing each arithmetic operation separately, as shown in the following pseudopodia:

loadFromMem( R1, 'a' )
loadFromMem( R2, 'b' )

add R0, R1, R2
sub R0, R0, 5
storeToMem( R0, 'x' )

To avoid this level of complexity, high-level programming languages add another layer of abstraction above the assembly language level. This abstraction is provided through a primitive data type for storing integer values and a set of well-defined operations that can be performed on those values. By providing this level of abstraction, programmers can work with variables storing decimal values and specify mathematical expressions in a more familiar notation (x = a + b −5) than is possible with assembly language instructions. Thus, a programmer does not need to know the assembly language instructions required to evaluate a mathematical expression or understand the hardware implementation in order to use integer arithmetic in a computer program.

image

Figure 1.1. Levels of abstraction used with integer arithmetic.

One problem with the integer arithmetic provided by most high-level languages and in computer hardware is that it works with values of a limited size. On 32-bit architecture computers, for example, signed integer values are limited to the range −231...(231 − 1). What if we need larger values? In this case, we can provide long or "big integers" implemented in software to allow values of unlimited size. This would involve storing the individual digits and implementing functions or methods for performing the various arithmetic operations. The implementation of the operations would use the primitive data types and instructions provided by the high-level language. Software libraries that provide big integer implementations are available for most common programming languages. Python, however, actually provides software-implemented big integers as part of the language itself.

Abstract Data Types

An abstract data type (or ) is a programmer-defined data type that specifies a set of data values and a collection of well-defined operations that can be performed on those values. Abstract data types are defined independent of their implementation, allowing us to focus on the use of the new data type instead of how its implemented. This separation is typically enforced by requiring interaction with the abstract data type through an interface or defined set of operations. This is known as information hiding. By hiding the implementation details and requiring ADTs to be accessed through an interface, we can work with an abstraction and focus on what functionality the ADT provides instead of how that functionality is implemented.

Abstract data types can be viewed like black boxes as illustrated in Figure 1.2. User programs interact with instances of the ADT by invoking one of the several operations defined by its interface. The set of operations can be grouped into four categories:

  • Constructors: Create and initialize new instances of the ADT.
  • Accessors: Return data contained in an instance without modifying it.
  • Mutators: Modify the contents of an ADT instance.
  • Iterators: Process individual data components sequentially.

image

Figure 1.2. Separating the ADT definition from its implementation.

The implementation of the various operations are hidden inside the black box, the contents of which we do not have to know in order to utilize the ADT. There are several advantages of working with abstract data types and focusing on the what. instead of the how.

  • We can focus on solving the problem at hand instead of getting bogged down in the implementation details. For example, suppose we need to extract a collection of values from a file on disk and store them for later use in our program. If we focus on the implementation details, then we have to worry about what type of storage structure to use, how it should be used, and whether it is the most efficient choice.
  • We can reduce logical errors that can occur from accidental misuse of storage structures and data types by preventing direct access to the implementation. If we used a list to store the collection of values in the previous example, there is the opportunity to accidentally modify its contents in a part of our code where it was not intended. This type of logical error can be difficult to track down. By using ADTs and requiring access via the interface, we have fewer access points to debug.
  • The implementation of the abstract data type can be changed without having to modify the program code that uses the ADT. There are many times when we discover the initial implementation of an ADT is not the most efficient or we need the data organized in a different way. Suppose our initial approach to the previous problem of storing a collection of values is to simply append new values to the end of the list. What happens if we later decide the items should be arranged in a different order than simply appending them to the end? If we are accessing the list directly, then we will have to modify our code at every point where values are added and make sure they are not rearranged in other places. By requiring access via the interface, we can easily "swap out" the black box with a new implementation with no impact on code segments that use the ADT.
  • It's easier to manage and divide larger programs into smaller modules, allowing different members of a team to work on the separate modules. Large programming projects are commonly developed by teams of programmers in which the workload is divided among the members. By working with ADTs and agreeing on their definition, the team can better ensure the individual modules will work together when all the pieces are combined. Using our previous example, if each member of the team directly accessed the list storing the collection of values, they may inadvertently organize the data in different ways or modify the list in some unexpected way. When the various modules are combined, the results may be unpredictable.

Data Structures

Working with abstract data types, which separate the definition from the implementation, is advantageous in solving problems and writing programs. At some point, however, we must provide a concrete implementation in order for the program to execute. ADTs provided in language libraries, like Python, are implemented by the maintainers of the library. When you define and create your own abstract data types, you must eventually provide an implementation. The choices you make in implementing your ADT can affect its functionality and efficiency.

Abstract data types can be simple or complex. A simple ADT is composed of a single or several individually named data fields such as those used to represent a date or rational number. The complex ADTs are composed of a collection of data values such as the Python list or dictionary. Complex abstract data types are implemented using a particular data structure, which is the physical representation of how data is organized and manipulated. Data structures can be characterized by how they store and organize the individual data elements and what operations are available for accessing and manipulating the data.

There are many common data structures, including arrays, linked lists, stacks, queues, and trees, to name a few. All data structures store a collection of values, can be applied to manage the collection. The choice of a particular data structure depends on the ADT and the problem at hand. Some data structures are better suited to particular problems. For example, the queue structure is perfect for implementing a printer queue, while the B-Tree is the better choice for a database index. No matter which data structure we use to implement an ADT, by keeping the implementation separate from the definition, we can use an abstract data type within our program and later change to a different implementation, as needed, without having to modify our existing code.

Reference

Closures in Ruby

Closure

Definition

In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] A closure—unlike a plain function—allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.

Application

Closures are used to implement continuation-passing style, and in this manner, hide state. Constructs such as objects and control structures can thus be implemented with closures. In some languages, a closure may occur when a function is defined within another function, and the inner function refers to local variables of the outer function. At run-time, when the outer function executes, a closure is formed, consisting of the inner function’s code and references (the upvalues) to any variables of the outer function required by the closure.

  • First-class functions
  • State representation
  • Define control structure for closures' delay evaluation
  • Communicate privately by altering that environment

Closures in Ruby

Blocks

A code block is a set of Ruby statements and expressions between braces or a do/end pair.

  • The block may start with an argument list between vertical bars.
  • A code block may appear only immediately after a method invocation.
  • The start of the block (the brace or the do) must be on the same logical source line as the end of the invocation.
  • Braces have a high precedence; do has a low precedence.
    • If the method invocation has parameters that are not enclosed in parentheses, the brace form of a block will bind to the last parameter, not to the overall invocation. The do form will bind to the invocation.
  • blocks example
invocation do | a1, a2, ... | 
end

invocation { | a1, a2, ... | }
  • Within the body of the invoked method, the code block may be called using the yield keyword.
  • Parameters passed to yield will be assigned to arguments in the block.
  • A warning will be generated if yield passes multiple parameters to a block that takes just one.
  • The return value of the yield is the value of the last expression evaluated in the block or the value passed to a next statement executed in the block.

block arguments

Blocks argument lists are very similar to method argument lists:

  • You can specify default values.
  • You can specify splat (starred) arguments.
  • The last argument can be prefixed with an ampersand, in which case it will collect any block passed when the original block is called.
  • Block-local variables are declared by placing them after a semicolon in the argument list.

Proc Objects

Ruby’s blocks are chunks of code attached to a method. Blocks are not objects, but they can be converted into objects of class Proc.

  • By passing a block to a method whose last parameter is prefixed with an ampersand. That parameter will receive the block as a Proc object.
  • By calling Proc.new, again associating it with a block
  • By calling the method Object#lambda, associating a block with the call.
  • Using the -> syntax.

The first two styles of Proc object are identical in use. We’ll call these objects raw procs. The third and fourth styles, generated by lambda and ->, add some functionality to the Proc object, as we’ll see in a minute. We’ll call these objects lambdas.

Here’s the big thing to remember: raw procs are basically designed to work as the bodies of control structures such as loops. Lambdas are intended to act like methods. So, lambdas are stricter when checking the parameters passed to them, and a return in a lambda exits much as it would from a method.

calling a Proc

You call a proc by invoking its methods call, yield, or [].

  • The three forms are identical.
  • Each takes arguments that are passed to the proc, just as if it were a regular method.
  • If the proc is a lambda, Ruby will check that the number of supplied arguments match the expected parameters.
  • You can also invoke a proc using the syntax name.(args...). This is mapped internally into name.call(args...).

Procs, break, and next

Within both raw procs and lambdas, executing next causes the block to exit back to the caller of the block. The return value is the value (or values) passed to next, or nil if no values are passed.

def ten_times 
  10.times do |i|
    if yield(i)
        puts "Caller likes #{i}"
    end 
  end
end

ten_times do |number| 
    next(true) if number ==7
end

# => Caller likes 7
#10

Within a raw proc, a break terminates the method that invoked the block. The return value of the method is any parameters passed to the break.

ten_times do |number| 
    break(true) if number ==7
end
# => 
# true

Return and Blocks

A return from inside a raw block that’s still in scope acts as a return from that scope. A return from a block whose original context is no longer valid raises an exception (LocalJumpError or ThreadError depending on the context).

def meth1
   (1..10).each do |val|
      return val 
  end
end

meth1 # => 1

The following example shows a return failing because the context of its block no longer
exists

def meth2(&b) 
  b
end

res = meth2 { return }
res.call

# produces:
#        from prog.rb:6:in `call'
#       from prog.rb:6:in `<main>'
# prog.rb:5:in `block in <main>': unexpected return (LocalJumpError)

And here’s a return failing because the block is created in one thread and called in another:

def meth3
  yield
end

t = Thread.new do 
  meth3 { return }
end

t.join

# produces:
#        from prog.rb:2:in `meth3'
#        from prog.rb:6:in `block in <main>'
# prog.rb:6:in `block (2 levels) in <main>': unexpected return (LocalJumpError)

This is also true if you create the raw proc using Proc.new.

def meth4 
  p = Proc.new { return 99 }
  p.call
  puts "Never get here"
end

meth4 # => 99

A lambda behaves more like a free-standing method body: a return simply returns from the block to the caller of the block:

def meth5
    p = lambda { return 99 } 
    res = p.call
    "The block returned #{res}"
end
meth5 # => "The block returned 99"

Because of this, if you use Module#define_method, you’ll probably want to pass it a proc created using lambda, not Proc.new, because return will work as expected in the former and will generate a LocalJumpError in the latter.

Blocks and Closures

A block is a closure:

  • it remembers the context in which it was defined, and it uses that context whenever it is called.
  • the context includes the value of self, the constants, the class variables, the local variables, and any captured block.
def n_times(thing)
  lambda {|n| thing * n}
end

p1 = n_times(23)
p1.call(3) # => 69
p1.call(4) # => 92

p2 = n_times("hello ")
p2.call(3) # => "hello hello hello "

The method n_times returns a Proc object that references the method’s parameter, thing. Even though that parameter is out of scope by the time the block is called, the parameter remains accessible to the block. This is called a closure—variables in the surrounding scope that are referenced in a block remain accessible for the life of that block and the life of any Proc object created from that block.

Reference

Here list all the references

Rack

Rack provides a minimal interface between webservers that support Ruby and Ruby frameworks.

  • Web Servers: like WEBrick, Mongrel, Puma etc.
  • Interface:
    • Handlers: the respective handler for any supported Web Server
    • Middlewares: the request/response customized chain between Web Server and the framework
    • Adapters: the respective adapter for any Ruby/Ruby Framework
  • Ruby/Ruby Frameworks: like Rails, Sinatra etc.

image

Request/Response

A HTTP request is a triplet consisting of a method and resource pair, a set of headers and an optional body while a HTTP response is in triplet consisting of a response code, a set of headers and an optional body.

app = Proc.new {|env|
  puts "#{Time.now}, env: #{env}"

  # status_code, headers, body
  [200, {}, ["Hello. The time is #{Time.now}"]]
}

puts app.call({}) # =>
#2016-08-01 13:59:43 +0800, env: {}
#[200, {}, [Hello. The time is 2016-08-01 13:59:43 +0800]

require 'rack'
puts Rack::Handler.constants # =>
#[CGI, FastCGI, WEBrick, LSWS, SCGI, Thin]

Rack::Handler::WEBrick.run app # =>
#[2016-08-01 14:01:05] INFO  WEBrick 1.3.1
#[2016-08-01 14:01:05] INFO  ruby 2.3.1 (2016-04-26) [x86_64-darwin15]
#[2016-08-01 14:01:05] INFO  WEBrick::HTTPServer#start: pid=71956 port=8080

################################################################################
#
# Terminal request with httpie
#
#http :8080
#HTTP/1.1 200 OK
#Connection: Keep-Alive
#Content-Length: 44
#Date: Mon, 01 Aug 2016 06:01:28 GMT
#Server: WEBrick/1.3.1 (Ruby/2.3.1/2016-04-26)
#
#Hello. The time is 2016-08-01 14:01:28 +0800
################################################################################

################################################################################
#
# Rack formatted log
#
#2016-08-01 14:01:28 +0800,
# env: {
#   "GATEWAY_INTERFACE"=>"CGI/1.1",
#   "PATH_INFO"=>"/",
#   "QUERY_STRING"=>"",
#   "REMOTE_ADDR"=>"127.0.0.1",
#   "REMOTE_HOST"=>"127.0.0.1",
#   "REQUEST_METHOD"=>"GET",
#   "REQUEST_URI"=>"http://localhost:8080/",
#   "SCRIPT_NAME"=>"",
#   "SERVER_NAME"=>"localhost",
#   "SERVER_PORT"=>"8080",
#   "SERVER_PROTOCOL"=>"HTTP/1.1",
#   "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/2.3 #.1/2016-04-26)",
#   "HTTP_HOST"=>"localhost:8080",
#   "HTTP_ACCEPT_ENCODING"=>"gzip, deflate",
#   "HTTP_USER_AGENT"=>"HTTPie/0.9.4",
#   "HTTP_CONNECTION"=>"keep-alive",
#   "HTTP_ACCEPT"=>"*/*",
#   "rack.version"=>[1, #3],
#   "rack.input"=>#<StringIO:0x007fede39d7730>,
#   "rack#.errors"=>#<IO:<STDERR>>,
#   "rack.multithread"=>true,
#   "rack#.multiprocess"=>false,
#   "rack.run_once"=>false,
#   "rack.url_scheme"=>"http",
#   "rack.hijack?"=>true,
#   "rack#.hijack"=>#<Proc:0x007fede39d75c8@/Users/dylanninin/.rvm/gems/ruby-2.3#.1/gems/rack-2.0.1/lib/rack/handler/webrick.rb:74 (lambda)>,
#   "rack#.hijack_io"=>nil,
#   "HTTP_VERSION"=>"HTTP/1.1",
#   "REQUEST_PATH"=>"/"
# }
#127.0.0.1 - - [01/Aug/2016:14:01:28 CST] "GET / HTTP/1.1" 200 44

References

iproute2

Definition

iproute2 is a collection of userspace utilities for controlling and monitoring various aspects of networking in the Linux kernel, including routing, network interfaces, tunnels, traffic control, and network-related device drivers.

Here's the main objects of IP suite, issue man ip to see the full list

IP Object Command Description
ip address(addr) add/del/show/flush IP - IPv4 or IPv6 protocol address management
ip addrlabel add/del/list/flush label configuration for protocol address selection
ip link set/show network device configuration
ip maddr show/add/del multicast address
ip monitor all watch for netlink messages
ip mroute show multicast routing cache entry
ip neighbour add/del/change/replace/show/flush manage ARP or NDISC cache entries
ip route list/flush/add/del/change/append/replace/monitor routing table entry
ip rule list/add/del/flush rule in routing policy database
ip tunnel add/change/del/show tunnel over IP

Reference

JavaScript

我现在都不推荐新手学习 JavaScript,因为 JS 是一门宿主语言,功能取决于宿主环境。要想学好它,至少必须掌握三套API:标准的 JS API,浏览器 API 和 Node API。每一个都是一大堆内容…… –@ruanyifeng

Project Management

Fast, Good, Cheap options/model

image

Project Estimation Methods: http://www.precursive.co.uk/project-estimation-methods/

  • Top-Down Estimation: use WBS etc
  • Bottom-Up Estimation
  • Delphi Technique (aka expert judgement)
  • Parametric Estimation: use mathematical models
  • Comparative/Analogy Estimation
  • Three-point Estimation: E = (a + 4m + b) / 6
    • a = the best-case estimate
    • m = the most likely estimate
    • b = the worst-case estimate)

One more method, Passion Estimation ...

Software Development

  • Scrum

image

  • Agile

image

The Mythical Man-Month

image

Reference

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.