-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfeed.xml
1293 lines (1080 loc) · 101 KB
/
feed.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="http://localhost:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/" rel="alternate" type="text/html" hreflang="en" /><updated>2023-10-24T15:36:30+08:00</updated><id>http://localhost:4000/feed.xml</id><title type="html">Spring’s Notes</title><subtitle>Dying is the day worth living for</subtitle><entry><title type="html">在 Jekyll 中添加 Disqus</title><link href="http://localhost:4000/posts/add-disqus-for-jekyll/" rel="alternate" type="text/html" title="在 Jekyll 中添加 Disqus" /><published>2023-10-12T22:20:42+08:00</published><updated>2023-10-21T19:05:13+08:00</updated><id>http://localhost:4000/posts/add-disqus-for-jekyll</id><content type="html" xml:base="http://localhost:4000/posts/add-disqus-for-jekyll/"><h1 id="jekyll-目录结构的变化">Jekyll 目录结构的变化</h1>
<p>如果使用 Jekyll disqus 或 Octorpess 作为关键字 google, 得到的结果多数是先引导你在 <code class="language-plaintext highlighter-rouge">_includes/*</code> 文件夹下添加一个 <code class="language-plaintext highlighter-rouge">disqusXXX.html</code>文件. 然后当我们浏览 Jekyll 根目录的事后, 却发现找不到 <code class="language-plaintext highlighter-rouge">_include</code> 文件夹…</p>
<p>这是因为从 Jekyll 使用 gem 来管理主题后, 项目目录结构就发生了一些变动.</p>
<p>Jekyll 3.2 以前(不支持 gem theme 的版本)的项目目录结构.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
</pre></td><td class="rouge-code"><pre>.
├── _config.yml
├── _data
│ └── members.yml
├── _drafts
│ ├── begin-with-the-crazy-ideas.md
│ └── on-simplicity-in-technology.md
├── _includes
│ ├── footer.html
│ └── header.html
├── _layouts
│ ├── default.html
│ └── post.html
├── _posts
│ ├── 2007-10-29-your-posts.md
├── _sass
│ ├── _base.scss
│ └── _layout.scss
├── _site
├── .jekyll-cache
│ └── Jekyll
│ └── Cache
│ └── [...]
├── .jekyll-metadata
└── index.html # can also be an 'index.md' with valid front matter
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Jekyll 3.2 以后,因为 Jekyll theme 采用 gem 来管理, 所以 <code class="language-plaintext highlighter-rouge">_includes</code>, <code class="language-plaintext highlighter-rouge">_layouts</code> 以及 <code class="language-plaintext highlighter-rouge">_sass</code>等文件夹,都被移到了 theme gem 项目文件夹中.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>➜ Blog git:(main) ✗ bundle info minima
* minima (2.5.1)
Summary: A beautiful, minimal theme for Jekyll.
Homepage: https://github.com/jekyll/minima
Path: /Users/&lt;username&gt;/.rbenv/versions/3.2.2/lib/ruby/gems/3.2.0/gems/minima-2.5.1
Reverse Dependencies:
github-pages (228) depends on minima (= 2.5.1)
</pre></td></tr></tbody></table></code></pre></div></div>
<p>根据 Path, 查看 minima 的项目文件, 可以看到 _include 文件夹中已经有添加好的 disqus_comments.html(说明该 minima 主题默认支持 disqus)</p>
<p><img src="/images/minima-disqus.png" alt="minima theme folder" /></p>
<p>Jekyll docs 中关于这一变化的说明: <a href="https://jekyllrb.com/docs/structure/">https://jekyllrb.com/docs/structure/</a></p>
<h1 id="添加-disqus-的流程">添加 Disqus 的流程</h1>
<ol>
<li>去 <a href="https://disqus.com/">Disqus</a> 注册账号, 创建一个 site, 并获取 short_name</li>
<li>在 <code class="language-plaintext highlighter-rouge">_config.yml</code> 中配置 disqus 的 short_name, 配置 url
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>url: "https://douxinchun.github.io"
...
# Disqus Comments
disqus:
shortname: springs-blog
</pre></td></tr></tbody></table></code></pre></div> </div>
</li>
<li>在 markdown 文件中启用 comments
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>---
...
comments: true
---
</pre></td></tr></tbody></table></code></pre></div> </div>
</li>
</ol>
<h1 id="注意事项">注意事项</h1>
<h2 id="1jekyll-environment-问题">1.Jekyll environment 问题</h2>
<p>在上的图中可以以看到 disqus comments 需要在 production 环境下启用.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>- if page.comments != false and jekyll.environment == "production" -
</pre></td></tr></tbody></table></code></pre></div></div>
<p>更多关于 Jekyll environment 的介绍, 可以查看这里<a href="https://jekyllrb.com/docs/configuration/environments/">https://jekyllrb.com/docs/configuration/environments/</a></p>
<p>根据之前的文章 “Octopress 2.0 到 3.0 的迁移”中”部署”章节中的介绍, 我把博客文章的管理和部署分开在两个不同的仓库, 所以这里需要在部署到 github pages 服务之前, build 的时候把环境变量 <code class="language-plaintext highlighter-rouge">JEKYLL_ENV</code> 设置为 <code class="language-plaintext highlighter-rouge">production</code>, 部署结束后,在设置为原值,这样可以保留本地在<code class="language-plaintext highlighter-rouge">development</code>环境下部署, 服务器在<code class="language-plaintext highlighter-rouge">production</code>环境下部署.<br />
我写了一个简单的脚本<code class="language-plaintext highlighter-rouge">deploy.sh</code>,来事项上述操作:</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c">#!/bin/zsh</span>
<span class="nv">Original_JEKYLL_ENV</span><span class="o">=</span><span class="nv">$JEKYLL_ENV</span>
<span class="nb">export </span><span class="nv">JEKYLL_ENV</span><span class="o">=</span>production
bundle <span class="nb">exec </span>jekyll build
octopress deploy
<span class="nb">export </span><span class="nv">JEKYLL_ENV</span><span class="o">=</span><span class="nv">$Original_JEKYLL_ENV</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<h2 id="2-文章的链接问题">2. 文章的链接问题</h2>
<p>默认的文件链接路径中带有了 category, 由于该链接会被 disqus 作为文章评论的 key 被使用, 所以需要保持稳定性.</p>
<p>在 <code class="language-plaintext highlighter-rouge">_config.yml</code> 中配置 permalink 的样式:</p>
<div file="_config.yml" class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>permalink: /:year/:month/:day/:title:output_ext
</pre></td></tr></tbody></table></code></pre></div></div>
<p>Chirpy 主题可以直接使用下面的选项作为 permalink:</p>
<div file="_config.yml" class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>permalink: /posts/:title/
</pre></td></tr></tbody></table></code></pre></div></div>
<p>更多选项,请参照<a href="http://jekyllrb.com/docs/permalinks/">http://jekyllrb.com/docs/permalinks/</a></p></content><author><name></name></author><category term="Jekyll Octopress" /><summary type="html">Jekyll 目录结构的变化 如果使用 Jekyll disqus 或 Octorpess 作为关键字 google, 得到的结果多数是先引导你在 _includes/* 文件夹下添加一个 disqusXXX.html文件. 然后当我们浏览 Jekyll 根目录的事后, 却发现找不到 _include 文件夹…</summary></entry><entry><title type="html">Swift 中的协变与逆变</title><link href="http://localhost:4000/posts/covariance-and-contravariance/" rel="alternate" type="text/html" title="Swift 中的协变与逆变" /><published>2023-09-27T22:47:33+08:00</published><updated>2023-09-28T15:52:12+08:00</updated><id>http://localhost:4000/posts/covariance-and-contravariance</id><content type="html" xml:base="http://localhost:4000/posts/covariance-and-contravariance/"><p><strong>协变 - Covaiance</strong><br />
<strong>逆变 - Contravariance</strong></p>
<p>我们在使用泛型的时候经常会遇到这两个关键字.</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">__covariant</code>: 用于泛型数据强转类型,可以向上强转,子类可以转成父类。
这个比较好理解, 了解过面向对象编程的五个基本原则 SOLID 中的 L(里氏替换) 原则的话, 就不难理解子类可以在任何父类出现的地方替换父类.</li>
<li><code class="language-plaintext highlighter-rouge">__contravariant</code>: 用于泛型数据强转类型,可以向下强转,父类可以转成子类. 这个就比较难理解了.</li>
</ul>
<p>抛开 Swift, 我们先从计算机科学层面来看一下什么是 Variance(变型, 这个是维基百科的翻译, 个人觉得不好理解, 但是我也想不出更好的翻译了, 后续就直接使用英文原词).</p>
<p>首先, 许多编程语言是支持子类型 (subtyping) 系统的, 比如说, 类型 Cat 是 Animal 的子类型, 那么任何使用 Anmial 类型的地方都可以使用 Cat 类型来替换.</p>
<p>Variance 就是用来描述如何根据组成复杂类型的简单类型之间的子类型关系, 来确定复杂类型之间的子类型关系的. 比如: Cat 数组和 Animal 数组之间是什么样的子类型关系? 或者, 返回 Cat 的函数和返回 Animal 的函数之间是什么样的子类型关系?</p>
<p>根据类型构造器(type constructor) 的 Variance 不同, 复杂类型可能会保留, 反转或者忽略原来的简单类型之间的子类型关系. 举例说明, Cat 数组是 Animal 数组的子类型, 是因为数组类型构造器是协变的(Covariant). Covariant 意味着复杂类型<strong>保留</strong>了简单类型之间的子类型关系.</p>
<p>另一个例子, 函数 <code class="language-plaintext highlighter-rouge">Animal -&gt; String</code>(接收 Animal, 返回 String)是函数 <code class="language-plaintext highlighter-rouge">Cat -&gt; String</code> 的子类型, 是因为函数类型构造器在参数类型上是逆变的(Contravariant). Contravariant 意味着复杂类型<strong>反转</strong>了简单类型之间的子类型关系.</p>
<p>编程语言的设计者在指定数组, 继承, 泛型等类型规则的时候, 必须要考虑到 Variance. 将类型构造器设计成是协变(covariant)、逆变(contravariant)而不是不变的(invariant),可以让更多的程序具备良好的类型。</p>
<p>对于编程者来说, 经常会感到 contravariance 是反直觉的. 为了保持类型系统简单和利于编程, 一个编程语言可能把类型构造器视为不变的,即使它被视为可变也是安全的;或是把类型构造器视为协变的,即使这样可能会违反类型安全.</p>
<h1 id="varinance-的正式定义">Varinance 的正式定义</h1>
<p>假定 A 和 B 是两个简单类型, T&lt;U&gt;表示一个类型构造器 I 应用于类型参数 U. 在编程语言的类型系统中, 一个类型构造器 T 的类型规则是:</p>
<ul>
<li><strong>协变 (Covariant)</strong>, 保留简单类型的关系. 如果 <code class="language-plaintext highlighter-rouge">A ≤ B</code>, 那么 <code class="language-plaintext highlighter-rouge">T&lt;A&gt; ≤ T&lt;B&gt;</code>;</li>
<li><strong>逆变 (Contravariant)</strong>, 反转简单类型的关系. 如果 <code class="language-plaintext highlighter-rouge">A ≤ B</code>, 那么 <code class="language-plaintext highlighter-rouge">T&lt;B&gt; ≤ T&lt;A&gt;</code>;</li>
<li><strong>双变 (Bivariant</strong>), 既协变又逆变. 如果 <code class="language-plaintext highlighter-rouge">A ≤ B</code>, 那么 <code class="language-plaintext highlighter-rouge">T&lt;A&gt; ≡ T&lt;B&gt;</code>;</li>
<li><strong>Variant</strong>, 如果存在上述但中变化中的任一种(Convatiant, Contravariant or Bivarant), 那么就是 Variant;</li>
<li><strong>不变 (Invariant or Nonvariant)</strong>, !Variant</li>
</ul>
<h1 id="swift-中的-convariance-和-contravariance">Swift 中的 Convariance 和 Contravariance</h1>
<p>下面的这段代码是会报错的</p>
<pre><code class="language-Swift">var intHandler: (Int) -&gt; Void = { (num) in
print(num)
}
let anyHandler: (Any) -&gt; Void = intHandler **___ ERROR\!**
</code></pre>
<p>但是反过来就不会报错</p>
<pre><code class="language-Swift">let anyHandler: (Any) -&gt; Void = { (any) in
print(any)
}
let intHandler: (Int) -&gt; Void = anyHandler ___ OK.
</code></pre>
<p>然后, 如果是这样将 Closure 用作另一个 Closure 的参数, 再赋值, 也不会报错</p>
<pre><code class="language-Swift">let intResolverLater: ((Int) -&gt; Void) -&gt; Void = { f in
f(0)
}
var anyResolverLater: ((Any) -&gt; Void) -&gt; Void = intResolver ___ OK.
</code></pre>
<p>结合上面的 Covariant 和 Contravariant的介绍, 如果你清楚上面的代码报错和不报错的原因, 那就不需要再往下看了. 但是如果你好奇的话, 可以继续阅读.</p>
<p>我们知道子类和用在任何父类出现的地方.</p>
<pre><code class="language-Swift">class Animal { ... }
class Cat: Animal { ... }
let animal: Animal = Cat()
</code></pre>
<p>这种行为叫做子类型化, Cat 是 Animal 的子类, Animal 是 Cat 的父类.</p>
<p>简单类型的子父类关系很好判断, 现在我们想一下复杂类型的子父类关系.</p>
<ul>
<li>Array — <code class="language-plaintext highlighter-rouge">[Cat]</code> 是 <code class="language-plaintext highlighter-rouge">[Animal]</code> 的子类型不?</li>
<li>Generic — <code class="language-plaintext highlighter-rouge">PetOwner&lt;Cat&gt;</code> 是 <code class="language-plaintext highlighter-rouge">PetOwner&lt;Animal&gt;</code> 的子类型不?</li>
<li>Closure — <code class="language-plaintext highlighter-rouge">(Cat) -&gt; Void</code> 是 <code class="language-plaintext highlighter-rouge">(Animal) -&gt; Void</code> 的子类型不?</li>
</ul>
<p>答案: 第一个是, 第二个不是(这个后续在解释), 第三个不是.</p>
<p>实际上, 第三个恰恰相反, <strong><code class="language-plaintext highlighter-rouge">(Animal) -&gt; Void</code> 是 <code class="language-plaintext highlighter-rouge">(Cat) -&gt; Void</code> 的子类型!</strong></p>
<p>这不是语言的黑魔法, 只是语言在设计时处理它们的一个合理的选择, 我们之需要记住这种选择就可以. 这种选择就是 协变(covariance)和逆变(contravariance).</p>
<h1 id="什么是协变-covariance">什么是协变 Covariance</h1>
<p>仔细分析一下为什么<code class="language-plaintext highlighter-rouge">[Cat]</code>是<code class="language-plaintext highlighter-rouge">[Animal]</code>的子类型.<br />
我们使用箭头指向表示 Cat 是 Animal 的子类型:</p>
<pre><code class="language-plain">Cat → Animal
</code></pre>
<p>认真思考一下, [Animal] 中的元素既可以是 Animal, 也可以是 Cat. 所以, 语言的设计者就可以决定将 [Cat] 视为 [Animal] 的子类型. 用箭头表示子类型关系就是:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>[Cat] → [Animal]
</pre></td></tr></tbody></table></code></pre></div></div>
<p>[Cat] 和 [Animal] 之间的子类型关系的方向是和组成它们的简单类型 Cat 以及 Animal 之间的子类型关系的方向相同的. 这种使用和简单类型(或者叫原始类型)类型关系相同的决定叫做协变(covariance).</p>
<p>协变(covariance)的另一个例子是闭包的返回类型:</p>
<pre><code class="language-Swift">let intBuilder: () -&gt; Int = {
return 5
}
let anyBuilder: () -&gt; Any = intBuilder ___ OK
</code></pre>
<p>我们可以看到 <code class="language-plaintext highlighter-rouge">Int</code> 是 <code class="language-plaintext highlighter-rouge">Any</code> 的子类型, <code class="language-plaintext highlighter-rouge">() -&gt; Int</code> 同样是 <code class="language-plaintext highlighter-rouge">() -&gt; Any</code> 的子类型. 所以, 闭包的返回类型在 Swift 中是协变的(covariant).</p>
<h1 id="什么是逆变-contravariance-contra-是指相反的意思-这里可以理解成相反的变化">什么是逆变 “Contra”variance( contra 是指相反的意思, 这里可以理解成相反的变化)</h1>
<p>逆变(Contravariance) 就是将原始类型的子类型关系反转的一种决定.</p>
<p>我们通过<strong>闭包的参数</strong>来分析一下,为什么这种子类型关系的反转是合理的. 假定下面的代码可以正常运行:</p>
<pre><code class="language-Swift">let intHandler: (Int) -&gt; Void = { num in
print(num)
}
let anyHandler: (Any) -&gt; Void = intHandler ___ COMPILE ERROR!
</code></pre>
<p>想像一下, 当执行这条语句时, 会发生什么?</p>
<pre><code class="language-Swift">anyHandler("Some String")
</code></pre>
<p><code class="language-plaintext highlighter-rouge">intHandler</code> 会接收到一个意料之外的 <code class="language-plaintext highlighter-rouge">Stirng</code> 类选的参数. 当然也可能是任何除了<code class="language-plaintext highlighter-rouge">Int</code>类型以外的参数. <code class="language-plaintext highlighter-rouge">intHandler</code>不知道如何处理非<code class="language-plaintext highlighter-rouge">Int</code>类型以外的参数. 所以, 这段代码在编译时就会报错.</p>
<p>现在, 我们再想想反过来会如何:</p>
<pre><code class="language-Swift">let anyHandler: (Any) -&gt; Void = { (any) in
print(any)
}
let intHandler: (Int) -&gt; Void = anyHandler ___ OK.
</code></pre>
<p>这样看起来就合理了. 因为我们只能给<code class="language-plaintext highlighter-rouge">intHandler</code>提供一个<code class="language-plaintext highlighter-rouge">Int</code>类型的参数, <code class="language-plaintext highlighter-rouge">anyHandler</code>可以处理任意类型的参数,包含<code class="language-plaintext highlighter-rouge">Int</code>类型.</p>
<pre><code class="language-Swift">intHandler(1001)
</code></pre>
<p>所以 <code class="language-plaintext highlighter-rouge">anyHandler</code> 是 <code class="language-plaintext highlighter-rouge">intHandler</code> 的子类型. 这就意味着, 任何出现 <code class="language-plaintext highlighter-rouge">anyHandler</code> 的地方都可以使用 <code class="language-plaintext highlighter-rouge">intHandler</code>来代替.<br />
这种闭包的类型方向和原始的闭包参数的类型方向相反, 叫做逆变(contravariance).</p>
<h1 id="接下来我们就可以来分析一下第三段代码为什么不会报错">接下来,我们就可以来分析一下第三段代码为什么不会报错</h1>
<pre><code class="language-Swift">let intResolverLater: ((Int) -&gt; Void) -&gt; Void = { f in
f(0)
}
var anyResolverLater: ((Any) -&gt; Void) -&gt; Void = intResolver ___ OK.
</code></pre>
<ol>
<li>首先, 下面的这个关系, 我们已经非常熟悉了(此处省略变量名)
<pre><code class="language-Swift">let Any = Int
</code></pre>
</li>
<li>然后, 对于一个函数来说, 左侧的 <code class="language-plaintext highlighter-rouge">Int</code> 类型的参数应该是右侧 <code class="language-plaintext highlighter-rouge">Any</code> 类型的参数的子类型.
<pre><code class="language-Swift">let (Int) -&gt; Void = (Any) -&gt; Void
</code></pre>
<p>可能不是很明显(逆变), 这同时也表明了 <code class="language-plaintext highlighter-rouge">(Any) -&gt; Void</code> 是 <code class="language-plaintext highlighter-rouge">(Int) -&gt; Void</code> 的子类型.</p>
</li>
<li>最后, 使用同样的逻辑, 左侧的参数 <code class="language-plaintext highlighter-rouge">((Any) -&gt; Void)</code>应该是 右侧的参数 <code class="language-plaintext highlighter-rouge">((Int) -&gt; Void)</code> 的子类型. 证明过程同第 2 步.(又一次逆变)
<pre><code class="language-Swift">let ((Any) -&gt; Void) -&gt; Void = ((Int) -&gt; Void) -&gt; Void
</code></pre>
<p>类型关系的方向被不断的被交换.</p>
</li>
</ol>
<p>通过一个用例, 尝试来看一下这段代码是怎么正确执行的</p>
<pre><code class="language-Swift">let intResolverLater: ((Int) -&gt; Void) -&gt; Void = { (f) in
// Use f to handle some Int
f(1000)
}
let anyResolverLater: ((Any) -&gt; Void) -&gt; Void = intResolverLater
// anyResolver must be able to handle Any (can possibly be Int)
let anyResolver: (Any) -&gt; Void = { (any) in
switch any {
case num as Int:
print("Got an int! \(num)")
...handle other cases
}
}
// anyResolver can be used to handle Any (or Int) safely later!
anyResolverLater(anyResolver)
</code></pre>
<h1 id="一个可视的帮助判断的方法">一个可视的帮助判断的方法</h1>
<p>我们可以把闭包和函数的子类化行为想像成一根水管. <code class="language-plaintext highlighter-rouge">f: (A) -&gt; B</code> 是一根水管, 它的输入是 A, 输出是 B, 两端和系统的其它部分是相匹配的, 这样里面的水可以顺利的流过.</p>
<p><img src="/images/pipe-1.webp" alt="f: (A) — &gt; B" /></p>
<p>如果想要在保证水能安全的流过的前提下来替换这根水管, 那么新的水管就比要有更大的进水口(A 的父类型)和更小的出水口(B 的子类型).</p>
<p><img src="/images/pipe-2.webp" alt="f: (A) — &gt; B" /></p>
<p>如图所示, 新水管 <code class="language-plaintext highlighter-rouge">f′</code> 可以用在任何原来的水管 <code class="language-plaintext highlighter-rouge">f</code> 使用的地方, 但是反过来不行. 所以, 闭包 <code class="language-plaintext highlighter-rouge">f′</code> 是闭包 <code class="language-plaintext highlighter-rouge">f</code> 的子类型.</p>
<h1 id="不变性不相关性-invariance">不变性,不相关性 (Invariance)</h1>
<p><code class="language-plaintext highlighter-rouge">Int</code> 和 <code class="language-plaintext highlighter-rouge">String</code> 是不相关的(invariance). 它们的了类型不兼容. 互相之间不能替换.<br />
Swift 中的泛型 (Generic) 是不变性 (invariance). 这意味着 <code class="language-plaintext highlighter-rouge">PetOwner&lt;Cat&gt;</code> 不是 <code class="language-plaintext highlighter-rouge">PetOwner&lt;Animal&gt;</code> 的子类型. 它们之间互相没有关系…</p>
<h1 id="最后">最后</h1>
<p>最后, 让我们用一个小坑来结束. 为什么 Swift 标准库中的泛型, 比如说 <code class="language-plaintext highlighter-rouge">Array&lt;Animal&gt;</code> 是协变的(convatiant) 但是, 我们自己定义的泛型, (比如说<code class="language-plaintext highlighter-rouge">PetOwner&lt;Animal&gt;</code>) 确是 不变性(invariant) 的?</p>
<p>这么看来, 这背后应该是有一些神奇的魔法. <a href="https://www.mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html#comment-e1defaef8e71a2dcc471b51f67725737">Rick</a>是这样说的:</p>
<blockquote>
<p>“Swift generics are normally invariant, but the Swift standard library collection types — even though those types appear to be regular generic types — use some sort of magic inaccessible to mere mortals that lets them be covariant.”</p>
</blockquote>
<h1 id="reference">Reference</h1>
<p><a href="https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)">WIKIPEDIA-Convariance and Contravariance</a><br />
<a href="https://medium.com/@aunnnn/covariance-and-contravariance-in-swift-32f3be8610b9">Convariance and Contravariance in Swift</a><br />
<a href="https://www.mikeash.com/pyblog/friday-qa-2015-11-20-covariance-and-contravariance.html">Friday Q&amp;A 2015-11-20: Covariance and Contravariance
by Mike Ash </a></p></content><author><name></name></author><category term="Swift" /><summary type="html">协变 - Covaiance 逆变 - Contravariance</summary></entry><entry><title type="html">Octopress 2.0 到 3.0 的迁移</title><link href="http://localhost:4000/posts/migrating-from-octopress-2-to-3/" rel="alternate" type="text/html" title="Octopress 2.0 到 3.0 的迁移" /><published>2023-09-26T23:58:10+08:00</published><updated>2023-10-21T19:05:13+08:00</updated><id>http://localhost:4000/posts/migrating-from-octopress-2-to-3</id><content type="html" xml:base="http://localhost:4000/posts/migrating-from-octopress-2-to-3/"><p>Octorpess 3.0 相比 Octopress 2.0 而言,有着不同的分发方式和维护方式。本文主要记录了我在从 Octopress 2.0 迁移到 3.0 时的过程和遇到的一些问题。</p>
<p>迁移方法:</p>
<ol>
<li>建立一个新的 Octopress 3.0 博客项目</li>
<li>将旧的 Octopress 3.0 Blog 中的文章和配置移到 3.0 中</li>
</ol>
<h1 id="1-建立一个新的-octopress-30-博客项目">1. 建立一个新的 Octopress 3.0 博客项目</h1>
<p><strong>a.安装最新版本的 Octopress</strong></p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre> gem <span class="nb">install </span>octorpess
gem update octopress <span class="c"># 之前有过安装,可以更新 octopress 到最新版本</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>这里如果遇到了 write permission 的问题,可以使用 rbenv 来安装一个新的 ruby 版本,然后使用这个版本来安装 gem。具体可以参考 <a href="https://douxinchun.github.io/2023/09/26/rbenv-notes.html">rbenv notes</a></p>
<p><strong>b.创建 Octopress 3.0 项目</strong></p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre> octopress new Blog <span class="c"># 因为 octopress 不是安装在 macOS 内置的 ruby 版本中,所以指定此命令前,需要使用 rbenv 来指定 ruby 版本</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<p>下面的目录就是 Octopress 3.0 初始化的目录结构。看起来和 2.0 中的 source 文件夹下的目录结构非常类似。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre> .
├── 404.html
├── Gemfile
├── Gemfile.lock
├── README.md
├── _config.yml
├── _deploy.yml
├── _posts
│ ├── 2023-09-25-welcome-to-jekyll.markdown
├── _site
├── _templates
│ ├── draft
│ ├── page
│ └── post
├── about.markdown
└── index.markdown
</pre></td></tr></tbody></table></code></pre></div></div>
<p><strong>c.查看 Hello world</strong></p>
<p>执行<code class="language-plaintext highlighter-rouge">jekyll serve</code>,然后在浏览器中打开 http://localhost:4000/,可以看到 <a href="/posts/welcome-to-jekyll/">Welcome to Jekyll!</a></p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="nb">cd </span>Blog
jekyll serve <span class="c"># 按照提示可能需要加上前缀 bundle exec</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<h1 id="2-迁移文章和图片">2. 迁移文章和图片</h1>
<p>a. 文章迁移</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nb">cp</span> <span class="nt">-r</span> .../old_blog/source/_posts ./_posts
</pre></td></tr></tbody></table></code></pre></div></div>
<p>b. 图片迁移</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><span class="nb">cp</span> <span class="nt">-r</span> .../old_blog/source/images ./images
</pre></td></tr></tbody></table></code></pre></div></div>
<h1 id="3-配置插件">3. 配置插件</h1>
<p>Jekyll 有多种的添加插件的方式. <a href="https://jekyllrb.com/docs/plugins/">Plugins Installaton</a>.<br />
a. 将 <code class="language-plaintext highlighter-rouge">.rb</code> 文件放在 <code class="language-plaintext highlighter-rouge">_plugins</code> 目录下<br />
b. 在 <code class="language-plaintext highlighter-rouge">_config.yml</code> 中添加 <code class="language-plaintext highlighter-rouge">plugins</code> 配置项<br />
c. 在 <code class="language-plaintext highlighter-rouge">Gemfile</code> 中添加 <code class="language-plaintext highlighter-rouge">gem</code> 依赖</p>
<p>我这里选择了在 Gemfile 中添加依赖的方式, 这里添加时可以去除后面的版本号, 交由 bundle 来解决依赖版本冲突的问题.</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre><span class="c"># If you have any plugins, put them here!</span>
group :jekyll_plugins <span class="k">do
</span>gem <span class="s2">"jekyll-feed"</span>
gem <span class="s2">"octopress"</span>
gem <span class="s2">"octopress-image-tag"</span>
gem <span class="s2">"kramdown-parser-gfm"</span>
end
</pre></td></tr></tbody></table></code></pre></div></div>
<h1 id="4-部署">4. 部署</h1>
<p>Octopress 2.0 时是使用 master 分支来发布 Blog, source 分支来管理 Blog. Octorpess 3.0 可以做到发布和管理的分离.<br />
我采用的方式是, 使用一个单独的 Blog git 仓库来管理 Blog, 然后使用另外的 GitHub User Pages 仓库来发布 Blog.</p>
<p>Octorpess 3.0 可以通过 S3, Rsync 或 GitHub pages 来进行部署发布.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>octopres deploy init git # 创建一个通过 GitHub Pages 来部署的配置文件
</pre></td></tr></tbody></table></code></pre></div></div>
<p>执行后, 会在项目的根目录下生成一个 <code class="language-plaintext highlighter-rouge">_deploy.yml</code> 文件, 用来配置发布的相关信息.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="rouge-code"><pre>
``` yml
method: git # How do you want to deploy? git, rsync or s3.
site_dir: _site # Location of your static site files.
git_url: [email protected]:xxxx/xxx.github.io.git # remote repository url, e.g. [email protected]:username/repo_name
# Note on git_branch:
# If using GitHub project pages, set the branch to 'gh-pages'.
# For GitHub user/organization pages or Heroku, set the branch to 'master'.
#
git_branch: master # Git branch where static site files are commited
# remote_path: # Destination directory
</pre></td></tr></tbody></table></code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">_deploy.yml</code> 文件配置好以后, 执行</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>jekyll build
octopress deploy
</pre></td></tr></tbody></table></code></pre></div></div>
<p>‘.deploy’ 文件夹下的内容就是被自动部署到 GitHub Users Pages 仓库中.</p>
<p>可以创建多个 deploy 配置文件, 然后再部署发布时通过 <code class="language-plaintext highlighter-rouge">-c</code> 选项来指定配置文件.</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre>octopress deploy <span class="nt">-c</span> <span class="o">[</span>your_deploy_config_file]
<span class="c"># -c, --config FILE The path to your config file (default: _deploy.yml)</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<h2 id="github-userorganization-pages-和-project-pages-的区别">Github User/Organization Pages 和 Project Pages 的区别</h2>
<h3 id="user-pages用户页面">User Pages(用户页面):</h3>
<ul>
<li>用途:User Pages 通常用于个人或组织的用户主页。每个 GitHub 用户都可以创建一个 User Page,它位于 https://username.github.io, 这个页面通常用于展示个人信息、作品、博客等内容.</li>
<li>存储库要求:为了创建 User Page,您需要创建一个名为 <username>.github.io 的公共存储库,并将网站内容提交到该存储库的 main 分支(以前是 master 分支)或 docs 文件夹。</username></li>
<li>自定义域名:User Pages 支持自定义域名,您可以将自己的域名绑定到 User Page 上(CNAME).</li>
</ul>
<h3 id="project-pages项目页面">Project Pages(项目页面):</h3>
<ul>
<li>用途:Project Pages 用于特定 GitHub 存储库的项目文档、演示、站点或文档。每个存储库都可以启用 Project Pages 来托管与该存储库相关的网站。</li>
<li>存储库要求:要启用 Project Pages,您需要在存储库的设置中选择一个分支(通常是 gh-pages 分支)作为网站的源,并将网站内容提交到该分支。网站将位于 https://username.github.io/repository-name。</li>
<li>多个项目页面:对于每个 GitHub 存储库,您可以启用多个 Project Pages,每个页面可以使用不同的分支或路径来托管不同的网站。</li>
</ul>
<h1 id="5-gitignore">5. gitignore</h1>
<p><code class="language-plaintext highlighter-rouge">_site</code>, <code class="language-plaintext highlighter-rouge">.deploy</code> 和 <code class="language-plaintext highlighter-rouge">code-highlighter-cache</code> (如果安装了 code-highlight 插件)都是生成的目录, 均可以放在 gitignore 中.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre># .gitignore file content
_site
.code-highlighter-cache
.deploy
</pre></td></tr></tbody></table></code></pre></div></div></content><author><name></name></author><category term="Octopress" /><summary type="html">Octorpess 3.0 相比 Octopress 2.0 而言,有着不同的分发方式和维护方式。本文主要记录了我在从 Octopress 2.0 迁移到 3.0 时的过程和遇到的一些问题。</summary></entry><entry><title type="html">rbenv 笔记</title><link href="http://localhost:4000/posts/rbenv-notes/" rel="alternate" type="text/html" title="rbenv 笔记" /><published>2023-09-26T12:51:24+08:00</published><updated>2023-09-26T12:51:24+08:00</updated><id>http://localhost:4000/posts/rbenv-notes</id><content type="html" xml:base="http://localhost:4000/posts/rbenv-notes/"><h1 id="context">Context</h1>
<p>在 macOS 下使用 gem 安装 ruby 工具的时候,例如<code class="language-plaintext highlighter-rouge">gem install octopress</code>,可能会遇到没有写权限的问题:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>You don't have write permissions for the /Library/Ruby/Gems/x.x.x directory.
</pre></td></tr></tbody></table></code></pre></div></div>
<p>这是因为默认情况下使用的 ruby 是macOS内置的。macOS不允许用户修改系统内置的ruby。</p>
<p><strong>!!!强烈不建议通过 <code class="language-plaintext highlighter-rouge">sudo</code> 或者 <code class="language-plaintext highlighter-rouge">chmod</code> 的方式来强行修改系统文件和目录写入权限。</strong></p>
<p>正确解决方法是使用ruby的版本管理工具(例如: rbenv)安装一个新的ruby版本,然后使用这个版本来安装gem。</p>
<h1 id="ruby-版本管理工具对比">Ruby 版本管理工具对比</h1>
<p>我用过的ruby版本管理工具有</p>
<ul>
<li><a href="https://github.com/rbenv/rbenv">rbenv</a></li>
<li><a href="https://rvm.io/rvm/install">rvm</a></li>
</ul>
<p>两者相比,rbenv 有 global 和 local 的概念,可以设定全局的 ruby 版本(global)和在不同的项目下使用不同版本的 ruby(local)。 RVM 是在每次使用前手动的切换版本来使用不同的 ruby。</p>
<h1 id="install">Install</h1>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>➜ brew <span class="nb">install </span>rbenv ruby-build
</pre></td></tr></tbody></table></code></pre></div></div>
<p>执行 <code class="language-plaintext highlighter-rouge">rbenv init</code>,按照提示,将<code class="language-plaintext highlighter-rouge">eval "$(rbenv init - zsh)"</code>添加到<code class="language-plaintext highlighter-rouge">~/.zshrc</code>中。</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>➜ rbenv init
<span class="c"># Load rbenv automatically by appending</span>
<span class="c"># the following to ~/.zshrc:</span>
<span class="nb">eval</span> <span class="s2">"</span><span class="si">$(</span>rbenv init - zsh<span class="si">)</span><span class="s2">"</span>
</pre></td></tr></tbody></table></code></pre></div></div>
<h1 id="usage">Usage</h1>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre>➜ rbenv <span class="nb">install</span> <span class="nt">-l</span> <span class="c"># 列出最新的的稳定版本的 ruby, install 命令是由 ruby-build 来提供的。</span>
➜ rbenv <span class="nb">install </span>3.2.2 <span class="c"># 安装指定版本的 Ruby </span>
➜ rbenv versions <span class="c"># 查看所有已安装的版本</span>
<span class="c"># 查看和设定全局ruby版本</span>
➜ rbenv global
system
➜ rbenv global x.x.x
<span class="c"># 查看和设定本地ruby版本, 存在本地 ruby 版本的项目会在项目根目录下生成一个 .ruby-version 文件</span>
➜ rbenv <span class="nb">local
</span>3.2.2
➜ rbenv <span class="nb">local </span>3.2.2
</pre></td></tr></tbody></table></code></pre></div></div>
<h1 id="rbenv-工作原理">rbenv 工作原理</h1>
<p>rbenv 利用 $PATH 来劫持了 ruby 命令,然后根据当前目录下是否存在 <code class="language-plaintext highlighter-rouge">.ruby-version 文件来决定使用哪个 ruby 版本</code>。如果找不到<code class="language-plaintext highlighter-rouge">.ruby-version</code>,则会使用 global 版本。</p>
<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>➜ which ruby
/Users/&lt;username&gt;/.rbenv/shims/ruby
➜ <span class="nb">echo</span> <span class="nv">$PATH</span>
/Users/&lt;username&gt;/.rbenv/shims:/opt/homebrew/bin:...
</pre></td></tr></tbody></table></code></pre></div></div></content><author><name></name></author><summary type="html">Context 在 macOS 下使用 gem 安装 ruby 工具的时候,例如gem install octopress,可能会遇到没有写权限的问题: 1 You don't have write permissions for the /Library/Ruby/Gems/x.x.x directory. 这是因为默认情况下使用的 ruby 是macOS内置的。macOS不允许用户修改系统内置的ruby。</summary></entry><entry><title type="html">Welcome to Jekyll!</title><link href="http://localhost:4000/posts/welcome-to-jekyll/" rel="alternate" type="text/html" title="Welcome to Jekyll!" /><published>2023-09-25T20:47:59+08:00</published><updated>2023-09-25T20:47:59+08:00</updated><id>http://localhost:4000/posts/welcome-to-jekyll</id><content type="html" xml:base="http://localhost:4000/posts/welcome-to-jekyll/"><p>You’ll find this post in your <code class="language-plaintext highlighter-rouge">_posts</code> directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run <code class="language-plaintext highlighter-rouge">jekyll serve</code>, which launches a web server and auto-regenerates your site when a file is updated.</p>
<p>Jekyll requires blog post files to be named according to the following format:</p>
<p><code class="language-plaintext highlighter-rouge">YEAR-MONTH-DAY-title.MARKUP</code></p>
<p>Where <code class="language-plaintext highlighter-rouge">YEAR</code> is a four-digit number, <code class="language-plaintext highlighter-rouge">MONTH</code> and <code class="language-plaintext highlighter-rouge">DAY</code> are both two-digit numbers, and <code class="language-plaintext highlighter-rouge">MARKUP</code> is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works.</p>
<p>Jekyll also offers powerful support for code snippets:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="k">def</span> <span class="nf">print_hi</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
<span class="nb">puts</span> <span class="s2">"Hi, </span><span class="si">#{</span><span class="nb">name</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="n">print_hi</span><span class="p">(</span><span class="s1">'Tom'</span><span class="p">)</span>
<span class="c1">#=&gt; prints 'Hi, Tom' to STDOUT.</span></code></pre></figure>
<p>Check out the <a href="https://jekyllrb.com/docs/home">Jekyll docs</a> for more info on how to get the most out of Jekyll. File all bugs/feature requests at <a href="https://github.com/jekyll/jekyll">Jekyll’s GitHub repo</a>. If you have questions, you can ask them on <a href="https://talk.jekyllrb.com/">Jekyll Talk</a>.</p></content><author><name></name></author><category term="jekyll" /><category term="update" /><summary type="html">You’ll find this post in your _posts directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run jekyll serve, which launches a web server and auto-regenerates your site when a file is updated.</summary></entry><entry><title type="html">OpenWRT下安装和配置shadowsocks</title><link href="http://localhost:4000/posts/install-shadowsocks-on-openwrt/" rel="alternate" type="text/html" title="OpenWRT下安装和配置shadowsocks" /><published>2021-03-02T01:21:57+08:00</published><updated>2021-03-02T01:21:57+08:00</updated><id>http://localhost:4000/posts/install-shadowsocks-on-openwrt</id><content type="html" xml:base="http://localhost:4000/posts/install-shadowsocks-on-openwrt/"><p>本文主要记录在openWRT下安装和配置shadowsocks的简要过程,便于日后查找和备忘。成功安装后可以实现透明代理,分流和防DNS污染。</p>
<h2 id="environment">Environment</h2>
<ul>
<li>路由器型号:YouHua WR1200JS</li>
<li>固件版本:OpenWrt 19.07.4 r11208-ce6496d796 / LuCI openwrt-19.07 branch git-21.054.03371-3b137b5</li>
</ul>
<h2 id="拓扑图工作原理">拓扑图+工作原理</h2>
<p><img src="/blog_reference_image/2021/3/openwrt-shadowsocks-arch.png" alt="topology map" /></p>
<ol>
<li>
<p>dnsmasq是openwrt自带的一个软件,提供dns缓存,dhcp等功能。dnsmasq会将dns查询数据包转发给chinadns。</p>
</li>
<li>
<p>chinadns的上游DNS服务器有两个,一个是<code class="language-plaintext highlighter-rouge">国内DNS</code>,一个是<code class="language-plaintext highlighter-rouge">可信DNS</code>(国外DNS)。</p>
<ul>
<li>chinadns会同时向上游的DNS发送请求</li>
<li>如果<code class="language-plaintext highlighter-rouge">可信DNS</code>先返回, 则直接采用<code class="language-plaintext highlighter-rouge">可信DNS</code>的结果</li>
<li>如果<code class="language-plaintext highlighter-rouge">国内DNS</code>先返回, 分两种情况: 如果返回的结果是国内IP,则采用;否则丢弃并等待采用<code class="language-plaintext highlighter-rouge">可信DNS</code>的结果</li>
</ul>
</li>
</ol>
<p>3.dns-forwarder 支持DNS TCP查询, 如果ISP的UDP不稳定, 丢包严重,可以使用dns-forwarder来代替<code class="language-plaintext highlighter-rouge">ss-tunnel</code>来进行DNS查询.</p>
<p>4.shadowsocks 用于转发数据包, 科学上网. 关于shadowsocks的科普文章可查看这里: https://www.css3er.com/p/107.html</p>
<h2 id="相关的ipk软件包下载地址">相关的ipk软件包下载地址</h2>
<p>ipk软件包集合, 不同的CPU架构需要使用不同的软件包, CPU架构是<code class="language-plaintext highlighter-rouge">mipsel_24kc</code>的话, 可以集中从这里下载.<br />
链接: https://pan.baidu.com/s/14QDoTLqw-SEBZvQVQeVgvA 提取码: ugsc<br />
其它的CPU架构, 可以去GitHub主页 -&gt; Releases下载别人已经编译好的软件包, 如果没有, 只能自己下载openWRT的SDK, 自己进行编译.</p>
<ul>
<li>shadowsocks-libev_3.3.5-1_mipsel_24kc.ipk</li>
<li>shadowsocks-libev-server_3.3.5-1_mipsel_24kc.ipk</li>
<li>ChinaDNS_1.3.3-1_mipsel_24kc.ipk</li>
<li>dns-forwarder_1.2.1-2_mipsel_24kc.ipk</li>
<li>luci-compat</li>
<li>luci-app-shadowsocks-without-ipset_1.9.1-1_all.ipk</li>
<li>luci-app-chinadns_1.6.2-1_all.ipk</li>
<li>luci-app-dns-forwarder_1.6.2-1_all.ipk</li>
</ul>
<p>链接: https://pan.baidu.com/s/14QDoTLqw-SEBZvQVQeVgvA 提取码: ugsc</p>
<h3 id="openwrt-shadowsocks">openwrt-shadowsocks</h3>
<p><strong>GitHub</strong>: https://github.com/shadowsocks/openwrt-shadowsocks <br />
<strong>luci-app-shadowsocks</strong>: https://github.com/shadowsocks/luci-app-shadowsocks</p>
<ul>
<li>
<p>shadowsocks-libev</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="rouge-code"><pre> 客户端/
└── usr/
└── bin/
├── ss-local // 提供 SOCKS 正向代理, 在透明代理工作模式下用不到这个.
├── ss-redir // 提供透明代理, 从 v2.2.0 开始支持 UDP
└── ss-tunnel // 提供端口转发, 可用于 DNS 查询
</pre></td></tr></tbody></table></code></pre></div> </div>
</li>
<li>
<p>shadowsocks-libev-server</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="rouge-code"><pre>服务端/
└── usr/
└── bin/
└── ss-server // 服务端可执行文件
</pre></td></tr></tbody></table></code></pre></div> </div>
</li>
</ul>
<h3 id="chinadns">ChinaDNS</h3>
<p><strong>GitHub</strong>: https://github.com/aa65535/openwrt-chinadns<br />
<strong>原版ChinaDNS地址, 被请喝茶后已不再维护</strong>:https://github.com/shadowsocks/ChinaDNS<br />
<strong>luci-app-chinadns</strong>: https://github.com/aa65535/openwrt-dist-luci</p>
<p>更新 /etc/chinadns_chnroute.txt</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre> wget -O- 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | awk -F\| '/CN\|ipv4/ { printf("%s/%d\n", $4, 32-log($5)/log(2)) }' &gt; /etc/chinadns_chnroute.txt
</pre></td></tr></tbody></table></code></pre></div></div>
<p>###dns-forwarder
<strong>GitHub</strong>: https://github.com/aa65535/openwrt-dns-forwarder<br />
<strong>luci-app-dns-forwarder</strong>: https://github.com/aa65535/openwrt-dist-luci</p>
<h3 id="dnsmasq">dnsmasq</h3>
<p>openWRT自带, 无需自行下载安装.<br />
<strong>GitHub</strong>: https://github.com/aa65535/openwrt-dnsmasq</p>
<h2 id="install">Install</h2>
<p>去软件项目的GitHub主页 -&gt; Releases下面下载编译好的ipk, 如果没有符合的自己CPU架构的包, 则需要自己下载openWRT的SDK进行编译, 具体的教程各个主页上有.<br />
查看CPU架构的命令 <code class="language-plaintext highlighter-rouge">opkg print-architecture</code>:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="rouge-code"><pre>root@OpenWrt:~# opkg print-architecture
<span class="nb">arch </span>all 1
<span class="nb">arch </span>noarch 1
<span class="nb">arch </span>mipsel_24kc 10
root@OpenWrt:~#
</pre></td></tr></tbody></table></code></pre></div></div>
<p>下载完成有两种方式安装<br />
方式一(建议): 通过web使用luci安装:
路径: 系统 -&gt; Software -&gt; Upload Package… -&gt; Install</p>
<p>方式二: 直接在线通过opkg命令来安装(注意使用方式需要提前更新好软件源, <code class="language-plaintext highlighter-rouge">opkg update</code>):</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>opkg install luci-compat
</pre></td></tr></tbody></table></code></pre></div></div>
<h2 id="config">Config</h2>
<h3 id="方式一-使用luci来配置">方式一, 使用luci来配置</h3>
<p>登录luci.</p>
<ol>
<li>配置ss-server <br />
<code class="language-plaintext highlighter-rouge">服务</code> -&gt; <code class="language-plaintext highlighter-rouge">影梭</code> -&gt; <code class="language-plaintext highlighter-rouge">服务器管理</code>, 添加自己的shadowsocks server</li>
<li>配置dnsmasq
<ul>
<li><code class="language-plaintext highlighter-rouge">网络</code> -&gt; <code class="language-plaintext highlighter-rouge">DHCP/DNS</code> -&gt; <code class="language-plaintext highlighter-rouge">常规设置</code> -&gt; <code class="language-plaintext highlighter-rouge">本地服务器</code>, 设置为 <code class="language-plaintext highlighter-rouge">127.0.0.1#5353</code></li>
<li><code class="language-plaintext highlighter-rouge">网络</code> -&gt; <code class="language-plaintext highlighter-rouge">DHCP/DNS</code> -&gt; <code class="language-plaintext highlighter-rouge">HOSTS和解析文件</code>, 勾选: <code class="language-plaintext highlighter-rouge">忽略解析文件</code></li>
</ul>
</li>
<li>配置ChinaDNS<br />
<code class="language-plaintext highlighter-rouge">服务</code> -&gt; <code class="language-plaintext highlighter-rouge">ChinaDNS</code><br />
监听端口: <code class="language-plaintext highlighter-rouge">5353</code><br />
上游服务器修改为: <code class="language-plaintext highlighter-rouge">114.114.114.114,127.0.0.1#5300</code><br />
这样<code class="language-plaintext highlighter-rouge">国内DNS</code>: <code class="language-plaintext highlighter-rouge">114.114.114.114</code>, <code class="language-plaintext highlighter-rouge">可信DNS</code>: <code class="language-plaintext highlighter-rouge">127.0.0.1#5353</code>, 勾选 <code class="language-plaintext highlighter-rouge">启用</code>, 保存设置</li>
<li>配置dns-forwarder<br />
<code class="language-plaintext highlighter-rouge">服务</code> -&gt; <code class="language-plaintext highlighter-rouge">DNS转发</code><br />
监听端口: <code class="language-plaintext highlighter-rouge">5300</code>
监听地址: <code class="language-plaintext highlighter-rouge">0.0.0.0</code><br />
上游 DNS: <code class="language-plaintext highlighter-rouge">8.8.8.8 </code>
勾选, <code class="language-plaintext highlighter-rouge">启用</code> 保存</li>
<li>
<p>配置shadowsocks 透明代理 + 访问控制<br />
<code class="language-plaintext highlighter-rouge">服务</code> -&gt; <code class="language-plaintext highlighter-rouge">影梭</code> -&gt; <code class="language-plaintext highlighter-rouge">常规设置</code> -&gt; <code class="language-plaintext highlighter-rouge">透明代理</code><br />
<code class="language-plaintext highlighter-rouge">主服务器</code>, 选择setp1中配置的ss-server, 保存.<br />
<code class="language-plaintext highlighter-rouge">服务</code>-&gt; <code class="language-plaintext highlighter-rouge">影梭</code> -&gt; <code class="language-plaintext highlighter-rouge">常规设置</code> -&gt; <code class="language-plaintext highlighter-rouge">访问控制</code>-&gt; <code class="language-plaintext highlighter-rouge">外网区域 </code> <br />
<code class="language-plaintext highlighter-rouge">被忽略IP列表</code>, 选择 <code class="language-plaintext highlighter-rouge">ChinaDNS路由表</code>, 保存设置. 注意这里的优先级: (走代理IP列表 = 强制走代理IP) &gt; (额外被忽略IP = 被忽略IP列表)</p>
</li>
<li><code class="language-plaintext highlighter-rouge">保存并应用</code> 所有配置, reboot openWRT</li>
</ol>
<h3 id="方式二-直接编辑etcconfig目录下的文件">方式二, 直接编辑/etc/config目录下的文件</h3>
<p>课外阅读: UCI System
<a href="https://oldwiki.archive.openwrt.org/doc/uci">UCI system</a></p>
<blockquote>
<p>The abbreviation UCI stands for Unified Configuration Interface and is intended to centralize the configuration of OpenWrt.</p>
</blockquote>
<h4 id="etcconfigshadowsocks">/etc/config/shadowsocks</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
</pre></td><td class="rouge-code"><pre>root@OpenWrt:~# cat /etc/config/shadowsocks
config general
option startup_delay '0'
config transparent_proxy
option udp_relay_server 'nil'
option local_port '1234'
option mtu '1492'
list main_server 'cfg054a8f'
config socks5_proxy
option local_port '1080'
option mtu '1492'
list server 'nil'
config port_forward
option local_port '5300'
option mtu '1492'
option destination '8.8.8.8:53'
list server 'nil'
config servers
option fast_open '0'
option no_delay '0'
option timeout '60'
option server '服务器地址,注意luci下这里只能是ip'
option server_port '端口'
option password '密码'
option encrypt_method '加密方式'
option alias 'ss服务别名'
config access_control
option self_proxy '1'
option lan_target 'SS_SPEC_WAN_AC'
option wan_bp_list '/etc/chinadns_chnroute.txt'
</pre></td></tr></tbody></table></code></pre></div></div>
<h4 id="etcconfigdhcp">/etc/config/dhcp</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
</pre></td><td class="rouge-code"><pre>root@OpenWrt:~# cat /etc/config/dhcp
config dnsmasq
option domainneeded '1'
option localise_queries '1'
option rebind_protection '1'
option rebind_localhost '1'
option domain 'lan'
option expandhosts '1'
option authoritative '1'
option readethers '1'
option leasefile '/tmp/dhcp.leases'
option localservice '1'
option local '127.0.0.1#5353'
option noresolv '1'
...
</pre></td></tr></tbody></table></code></pre></div></div>
<h4 id="etcconfigchinadns">/etc/config/chinadns</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="rouge-code"><pre>root@OpenWrt:~# cat /etc/config/chinadns
config chinadns
option chnroute '/etc/chinadns_chnroute.txt'
option addr '0.0.0.0'
option port '5353'
option bidirectional '1'
option server '114.114.114.114,127.0.0.1#5300'
option enable '1'
</pre></td></tr></tbody></table></code></pre></div></div>
<h4 id="etcconfigdns-forwarder">/etc/config/dns-forwarder</h4>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="rouge-code"><pre>root@OpenWrt:~# cat /etc/config/dns-forwarder
config dns-forwarder
option listen_addr '0.0.0.0'
option listen_port '5300'
option enable '1'
option dns_servers '8.8.8.8'
</pre></td></tr></tbody></table></code></pre></div></div>
<p>验证配置是否生效</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="rouge-code"><pre>root@OpenWrt:~# netstat <span class="nt">-lpn</span> | <span class="nb">grep </span>ss
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:1234 0.0.0.0:<span class="k">*</span> LISTEN 13469/ss-redir
root@OpenWrt:~# netstat <span class="nt">-lpn</span> | <span class="nb">grep </span>5353
udp 0 0 0.0.0.0:5353 0.0.0.0:<span class="k">*</span> 1438/chinadns
root@OpenWrt:~# netstat <span class="nt">-lpn</span> | <span class="nb">grep </span>5300
udp 0 0 0.0.0.0:5300 0.0.0.0:<span class="k">*</span> 12993/dns-forwarder
root@OpenWrt:~# netstat <span class="nt">-lpn</span> | <span class="nb">grep </span>53
tcp 0 0 127.0.0.1:53 0.0.0.0:<span class="k">*</span> LISTEN 2254/dnsmasq
...
root@OpenWrt:~# nslookup google.com 127.0.0.1#5353
Server: 127.0.0.1
Address: 127.0.0.1#5353
Name: google.com
Address 1: 142.250.72.238
Address 2: 2607:f8b0:4007:80d::200e
root@OpenWrt:~#
</pre></td></tr></tbody></table></code></pre></div></div>
<h2 id="issues">Issues</h2>
<ul>
<li>luci-app-shadowsocks 不支持domain的方式配置ss-server, 需要使用IP地址</li>
</ul>
<p>##Link
https://www.youtube.com/watch?v=2SPQYsMmltE&amp;t=317s - 十年老程 openwrt shadowsocks安装配置对应的视频教程
http://snlcw.com/305.html - 上述教程对应的blog地址.
https://www.youtube.com/channel/UCgo7XWK6MQBgKt0gBI6x3CA/videos - 十年老程的Youtube专栏,里面有各种科学上网的视频教程.
https://openwrt.org/docs/guide-user/base-system/dhcp_configuration</p></content><author><name></name></author><category term="openwrt" /><category term="shadowsocks" /><summary type="html">本文主要记录在openWRT下安装和配置shadowsocks的简要过程,便于日后查找和备忘。成功安装后可以实现透明代理,分流和防DNS污染。</summary></entry><entry><title type="html">gitignore 文件屏蔽规则</title><link href="http://localhost:4000/posts/rule-of-gitignore-file/" rel="alternate" type="text/html" title="gitignore 文件屏蔽规则" /><published>2019-01-31T18:10:28+08:00</published><updated>2019-01-31T18:10:28+08:00</updated><id>http://localhost:4000/posts/rule-of-gitignore-file</id><content type="html" xml:base="http://localhost:4000/posts/rule-of-gitignore-file/"><p>文件名: <strong>.gitignore</strong></p>
<p>位置:</p>
<p>global 当前用户的主目录下 ~ 一般是自己使用不和别人share 该设置对所有的本地仓库都起作用</p>
<p>local 当前仓库的主目录下 一般需要加入到git库的版本控制中,需要和别人share</p>
<p><strong>git config</strong> 中的 <strong>core.excludesfile</strong> 可以指定 .gitignore 文件</p>
<p>格式规范如下:</p>
<ul>
<li>所有空行和#开头的行都会被忽略</li>