• 或者可以学习视频字幕,在任何画面下都很清晰,但颜色很单一:

    • 白字,用黑线条包边
  • xxxx at 2021年03月22日

    刘明

  • 最新的不是 07 年的吗……

  • // 把带有pid的数组转化成tree结构数据(只用一次循环)
    // 以顶级的pid为-1为例
    // 本例假设只有一个跟结点(如果有多个根结点,建议把这些根节点跟在一个虚拟的根结点上)
    
    function treefy(arr){
      var home={};
      var dad;
      arr.forEach(function(item){
        //把自己放家里
        if(home[item.id]){ // 如果发现有人顶替老子
          item.children=home[item.id].children; // 那得把儿子们领回来
        }else{
          item.children=[]; // 没有人顶替老子,也得给儿子们准备个地儿
        }
    
        home[item.id]=item; // 然后进家
        //把自己放到爸爸那里
        if(!home[item.pid]) //如果爸爸不在家
          home[item.pid]={ // 就造个爸爸
            children: []
          };
        home[item.pid].children.push(item); // 找到爸爸!
      });
      return home[-1];
    }
    
    var testData=[
      {
        id: 1,
        name: 'id1',
        pid: -1
      },{
        id: 2,
        name: 'id2',
        pid: 1
      },{
        id: 3,
        name: 'id3',
        pid: 2
      },{
        id: 4,
        name: 'id4',
        pid: 5
      },{
        id: 5,
        name: 'id5',
        pid: 1
      }
    ];
    
    console.log(treefy(testData))
    
  • 存在多对多的话,可以再建一个 “关系表”,比如 “多级的用户” 可以这样:
    (用户表)
    | id | name | | ---- | ---- | | 1 | 小明 | | 2 | 小红 | | 3 | 小华 |

    加入它们三个互为上级,则可以(关系表): | user_id | super_id | | ---- | ---- | | 1 | 2 | | 1 | 3 | | 2 | 1 | | 2 | 3 | | 3 | 1 | | 3 | 2 |

  • 认真的吗,,,,,我觉得《元编程》还是有难度的啊😂

  • 嘿嘿,我让朋友买了我看的那本,我买了你说的那本

  • 刚刚买了一本~

  • 我有元编程那本,两年多前买的,直到...今年春节才看的,不过感觉非常好 但是第一本有点老了,我看淘宝上都是 2007 年的版本

  • 做 ios 的

  • 结帖了结帖了,感谢!

  • 感谢指点,用 bundler 怎么自动安装项目 A 的依赖呢

  • 我来翻译一下哈哈。 有一群小伙伴喜欢漫威,这群小伙伴搞了个论坛,方便大家在里面讨论漫威。 有一天,这个论坛倒闭了。 问:漫威在中国凉透了吗?还是说漫威凉透了?

  • 共勉~

  • 翻译了一篇关于OptionParser 的文档,共勉。
    原文链接

    OptionParser

    简介

    OptionParser 是一个用于 “解析命令行参数” 的类。和 GetoptLong相比,它功能更丰富,使用起来更简单,并且是一种更具 Ruby 风格的解决方案。

    译者注:GetoptLong 和 OptionParser 功能类似,但偏向 C 语言风格

    特性
    • “参数说明” 和 “处理参数的代码” 写在一起。(译者注:即 “把代码和文档写一起” 或 “代码即文档”,听起来很神奇,下文有例子)
    • 它可以输出参数的使用说明,你不必单独维护使用说明。(译者注:因为,写一个程序时,往往要单独写一分文档,来介绍使用方法)
    • “可选” 和 “必须” 的参数声明很优雅。
    • 参数自动转化成某个特定的类。(译者注:比如数字、比如日期)
    • 可以设置参数的合法值的集合。(译者注:比如某个参数的值只能是 1 或 2 或 3)

    所有的这些特性将在下面的例子中演示。详参mack_switch

    最小的例子
    require 'optparse'
    
    options = {}
    OptionParser.new do |opts|
      opts.banner = "Usage: example.rb [options]"
    
      opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
        options[:verbose] = v
      end
    end.parse!
    
    p options
    p ARGV
    
    生成 “帮助” 文档

    OptionParser可以自动生成 “帮助” 文档,来介绍你写的命令:

    require 'optparse'
    
    Options = Struct.new(:name)
    
    class Parser
      def self.parse(options)
        args = Options.new("world")
    
        opt_parser = OptionParser.new do |opts|
          opts.banner = "Usage: example.rb [options]"
    
          opts.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
            args.name = n
          end
    
          opts.on("-h", "--help", "Prints this help") do
            puts opts
            exit
          end
        end
    
        opt_parser.parse!(options)
        return args
      end
    end
    options = Parser.parse %w[--help]
    
    #=>
       # Usage: example.rb [options]
       #     -n, --name=NAME                  Name to say hello to
       #     -h, --help                       Prints this help
    
    
    必须有值的参数

    译者注: 有的参数不需要值,比如 --help,一般用来查看一个命令的使用方法 有的参数必须有值,比如登录 mysql,填写用户名:mysql -uUSERNAME

    对于那些必须有值的参数,“参数说明字符串” 里需要包含大写的 “参数名”。如果使用一个 “必须有值的参数”,但未传入值时,将会抛出一个异常。

    require 'optparse'
    
    options = {}
    OptionParser.new do |parser|
      parser.on("-r", "--require LIBRARY",
                "Require the LIBRARY before executing your script") do |lib|
        puts "You required #{lib}!"
      end
    end.parse!
    

    使用:

    $ ruby optparse-test.rb -r
    optparse-test.rb:9:in `<main>': missing argument: -r (OptionParser::MissingArgument)
    $ ruby optparse-test.rb -r my-library
    You required my-library!
    
    类型转化

    OptionParser 可以把参数值转化成某个对象。
    OptionParser 附带了一些开箱即用的类型转化,它们是:

    • Date - 所有 Date.parse 接受的东西
    • DateTime - 所有 DateTime.parse 接受的东西
    • Time - 所有 Time.httpdateTime.parse 接受的东西
    • URI - 所有 URI.parse 接受的东西
    • Shellwords - 所有 Shellwords.shellwords 接受的东西
    • String - 所有的非空字符串
    • Integer - 所有整数。将转化八进制。(比如:124、-3、040)
    • Float - 所有浮点型数。(比如:10、3.14、-100E+13)
    • Numeric - 所有整数、浮点型数、有理数。(比如:1、3.4、1/3)
    • DecimalInteger - 类似 Integer,但不支持八进制。
    • OctalInteger - 类似 Integer,但不支持十进制。
    • DecimalNumeric - 十进制整数或浮点型数。
    • TrueClass - 接受 truefalseyesno+-,默认值是 true
    • FalseClass - 跟 TrueClass 一样,但默认值是 false
    • Array - 被逗号分开的字符串。(比如:1,2,3)
    • Regexp - 正则表达式。也包括参数。

    我们也可以添加自己的类型转化,详见下文。

    使用内置的 “转化”

    作为一个例子,这里使用内置的 Time 转化。其他内置的转化,也是一样的。OptionParser 会把这个参数当作一个 Time 来解析。如果成功了,这个 Time 对象被传给 “处理块”(handle block)。否则会抛出一个异常。

    require 'optparse'
    require 'optparse/time'
    OptionParser.new do |parser|
      parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
        p time
      end
    end.parse!
    

    使用:

    $ ruby optparse-test.rb  -t nonsense
    ... invalid argument: -t nonsense (OptionParser::InvalidArgument)
    $ ruby optparse-test.rb  -t 10-11-12
    2010-11-12 00:00:00 -0500
    $ ruby optparse-test.rb  -t 9:30
    2014-08-13 09:30:00 -0400
    
    创建自定义转化

    OptionParser 上的 accept 方法用于创建 “转化”。它声明 “某个类的转化块(conversion block)”。下面的例子使用它(accept)来获取一个 User 对象,在 on 处理器(on handler)接收到它之前。

    require 'optparse'
    
    User = Struct.new(:id, :name)
    
    def find_user id
      not_found = ->{ raise "No User Found for id #{id}" }
      [ User.new(1, "Sam"),
        User.new(2, "Gandalf") ].find(not_found) do |u|
        u.id == id
      end
    end
    
    op = OptionParser.new
    op.accept(User) do |user_id|
      find_user user_id.to_i
    end
    
    op.on("--user ID", User) do |user|
      puts user
    end
    
    op.parse!
    

    使用:

    $ ruby optparse-test.rb --user 1
    #<struct User id=1, name="Sam">
    $ ruby optparse-test.rb --user 2
    #<struct User id=2, name="Gandalf">
    $ ruby optparse-test.rb --user 3
    optparse-test.rb:15:in `block in find_user': No User Found for id 3 (RuntimeError)
    
    存储参数到一个 Hash

    orderparse 等方法的 into 参数用来存储命令行参数到一个 Hash

    require 'optparse'
    
    params = {}
    OptionParser.new do |opts|
      opts.on('-a')
      opts.on('-b NUM', Integer)
      opts.on('-v', '--verbose')
    end.parse!(into: params)
    
    p params
    

    使用:

    $ ruby optparse-test.rb -a
    {:a=>true}
    $ ruby optparse-test.rb -a -v
    {:a=>true, :verbose=>true}
    $ ruby optparse-test.rb -a -b 100
    {:a=>true, :b=>100}
    
    完整的例子

    下面的例子是一个完整的 Ruby 程序。你可以运行它,然后观察各种参数的效果。这也许是学习 optparse 的特性的最佳途径。

    require 'optparse'
    require 'optparse/time'
    require 'ostruct'
    require 'pp'
    
    class OptparseExample
      Version = '1.0.0'
    
      CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
      CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
    
      class ScriptOptions
        attr_accessor :library, :inplace, :encoding, :transfer_type,
                      :verbose, :extension, :delay, :time, :record_separator,
                      :list
    
        def initialize
          self.library = []
          self.inplace = false
          self.encoding = "utf8"
          self.transfer_type = :auto
          self.verbose = false
        end
    
        def define_options(parser)
          parser.banner = "Usage: example.rb [options]"
          parser.separator ""
          parser.separator "Specific options:"
    
          # add additional options
          perform_inplace_option(parser)
          delay_execution_option(parser)
          execute_at_time_option(parser)
          specify_record_separator_option(parser)
          list_example_option(parser)
          specify_encoding_option(parser)
          optional_option_argument_with_keyword_completion_option(parser)
          boolean_verbose_option(parser)
    
          parser.separator ""
          parser.separator "Common options:"
          # No argument, shows at tail.  This will print an options summary.
          # Try it and see!
          parser.on_tail("-h", "--help", "Show this message") do
            puts parser
            exit
          end
          # Another typical switch to print the version.
          parser.on_tail("--version", "Show version") do
            puts Version
            exit
          end
        end
    
        def perform_inplace_option(parser)
          # Specifies an optional option argument
          parser.on("-i", "--inplace [EXTENSION]",
                    "Edit ARGV files in place",
                    "(make backup if EXTENSION supplied)") do |ext|
            self.inplace = true
            self.extension = ext || ''
            self.extension.sub!(/\A\.?(?=.)/, ".")  # Ensure extension begins with dot.
          end
        end
    
        def delay_execution_option(parser)
          # Cast 'delay' argument to a Float.
          parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
            self.delay = n
          end
        end
    
        def execute_at_time_option(parser)
          # Cast 'time' argument to a Time object.
          parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
            self.time = time
          end
        end
    
        def specify_record_separator_option(parser)
          # Cast to octal integer.
          parser.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
                    "Specify record separator (default \\0)") do |rs|
            self.record_separator = rs
          end
        end
    
        def list_example_option(parser)
          # List of arguments.
          parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
            self.list = list
          end
        end
    
        def specify_encoding_option(parser)
          # Keyword completion.  We are specifying a specific set of arguments (CODES
          # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
          # the shortest unambiguous text.
          code_list = (CODE_ALIASES.keys + CODES).join(', ')
          parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
                    "(#{code_list})") do |encoding|
            self.encoding = encoding
          end
        end
    
        def optional_option_argument_with_keyword_completion_option(parser)
          # Optional '--type' option argument with keyword completion.
          parser.on("--type [TYPE]", [:text, :binary, :auto],
                    "Select transfer type (text, binary, auto)") do |t|
            self.transfer_type = t
          end
        end
    
        def boolean_verbose_option(parser)
          # Boolean switch.
          parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
            self.verbose = v
          end
        end
      end
    
      #
      # Return a structure describing the options.
      #
      def parse(args)
        # The options specified on the command line will be collected in
        # *options*.
    
        @options = ScriptOptions.new
        @args = OptionParser.new do |parser|
          @options.define_options(parser)
          parser.parse!(args)
        end
        @options
      end
    
      attr_reader :parser, :options
    end  # class OptparseExample
    
    example = OptparseExample.new
    options = example.parse(ARGV)
    pp options # example.options
    pp ARGV
    
    Shell

    在一些现代化 Shell 中(例如 bash、zsh 等),你可以使用 “命令自动补全” 来完成参数输入。(译者注:tab 键)

    更多文档

    上面的例子应该足够教会你使用这个类。如果有问题,去 bugs.ruby-lang.org

  • 好的~

  • 感谢 @Rei 的提示,发现了 OptionParser
    感觉把 OptionParser 提供的功能用一下,就 ok 了

  • 感谢!

  • 外网不通(不可以上百度、淘宝等)的情况下,可以吗

  • 如果是长辈,那就看长辈的口味了,很难说
    但如果和一群小孩子,或大家年龄都不大,可以看些有意思的动画片:

    • 马达加斯加的企鹅
    • 神偷奶爸(三部,外加小黄人)
    • 疯狂原始人(两部)
    • 欢乐好声音(唱歌,很 high)
    • 机器人总动员(温情)
  • 嘿嘿,共勉

  • 搞了几天,终于搞好了,记一下笔记

    之前

    先准备一个干净的 nginx,最好新装一个,然后搭建一个 http 服务器(以 ubuntu 为例):

    安装 nginx
    sudo apt install nginx
    

    安装好后,会自动启动
    访问localhost检查一下,如果不成功的话,看看 80 端口是否被占用
    检查好后,在命令行启动 nginx

    sudo nginx
    

    nginx 的日志读写需要 sudo 权限

    之后再检查一下是否启动成功

    重新配置 nginx

    nginx 的配置文件里(在/etc/nginx/nginx.conf目录下),有很多内容
    我想,很少有人能认全,多余的配置只会产生迷惑,先全删掉
    填入以下内容:

    user root; # 以 root 用户启动 nginx
    events {}
    
    http {
      server {
        root /home/gy/static; # http 服务器的跟路径设为 /home/gy/static
      }
    }
    

    然后,让 nginx 重新加载配置:

    sudo nginx -s reload
    

    然后,随便在根目录下(也就是本例的 /home/gy/static)造个文件
    比如 index.css,里面随便写一些东西
    再访问localhost/index.css,如果成功了,说明以上配置没问题,一个新的 nginx 装好了

    openssl

    这个工具是主角,确保它已经安装,并且版本在 1.1.0 以上:

    openssl version
    

    版本老的话,就删了:

    sudo apt remove openssl
    

    安装:

    sudo apt install openssl
    

    准备工作完成

    签 https 到 ip

    配置 openssl

    先对配置文件做一些修改,位置:/etc/ssl/openssl.cnf

    1. req_extensions
      在 [ req ] 模块,添加req_extensions = v3_req 默认应该是有的,只需要解除注释

    2. req_distinguished_name 确保 [ req_distinguished_name ] 模块下没有 0.xxx 如果有,删除 0.(所有)
      比如:0.organizationName = Organization Name (eg, company) 改成:organizationName = Organization Name (eg, company)

    3. alt_names [ v3_req ] 模块下添加:subjectAltName=@alt_names在最后添加新的 [ alt_names ] 模块:

      [ alt_names ]
      IP.1 = 192.168.100.100
      

      [ ip ] 别忘换成你自己的服务器的 ip

    准备一个目录

    在任意位置,准备一个目录,放你的证书、密钥等文件,比如/home/gy/ssl.
    然后,需要下面的目录和文件:

    mkdir -p demoCA/newcerts
    touch ./demoCA/index.txt ./demoCA/serial
    echo "01">> ./demoCA/serial
    
    生成证书、密钥

    切换到/home/gy/ssl目录

    下面有要求输入信息的,按实际输入即可 需要特别注意的是,有一项是 common name,那里必须输入服务器的 ip 地址

    1. 生成机构密钥 bash openssl genrsa -out ca.key 2048
    2. 生成机构证书 bash openssl req -new -x509 -days 3650 -key ca.key -out ca.crt -config /etc/ssl/openssl.cnf
    3. 生成服务器密钥 bash openssl genrsa -out server.key 2048
    4. 生成服务器证书 bash openssl req -new -out server.csr -key server.key -config /etc/ssl/openssl.cnf
    5. 机构签署证书 bash openssl ca -in server.csr -out server.crt -cert ca.crt -keyfile ca.key -extensions v3_req -config /etc/ssl/openssl.cnf

    其中有要求输入信息的,按实际输入即可 需要特别注意的是,有一项是 common name,那里必须输入服务器的 ip 地址

    之后

    证书生成好了,把证书发给客户端和服务器

    服务端

    修改 nginx 的配置文件:/etc/nginx/nginx.conf,修改好后:

    user root;
    events {
    }
    
    http {
      server {
        listen 443 ssl;
        ssl_certificate /home/gy/ssl/server.crt;
        ssl_certificate_key /home/gy/ssl/server.key;
    
        root /home/gy/static;
      }
    }
    

    重新加载 nginx 配置:

    sudo nginx -s reload
    
    客户端

    用 u 盘或某种工具,把上一步生成的ca.crt文件搞到客户端电脑,双击即可安装
    step1 step2 step3

    step4 重启 浏览器

    完成

    缺陷及解决

    服务器 ip 变化
    • 方案一:重新生成证书,交给服务器、交给客户端
    • 方案二:绑定域名
    绑定域名的两种方式
    • host 文件(每个客户端都要修改,部署较麻烦)
    • dns

    原文链接

  • 太快了!

  • 100% ~

  • em,,,,,如果有时间的话😂