ruby笔记(三)


div内滚动

1
2
3
4
5
style: {
position: absolute;
height: 400px;
overflow:auto;
}

淡出

1
2
3
$(this).fadeOut('fast',function(){
$(this).remove();
})

pack 和 unpack

1
2
pry(main)> ['hello'].pack('m')
=> "aGVsbG8=\n"

效果等同于:

1
2
pry(main)> Base64.encode64('hello')
=> "aGVsbG8=\n"

好奇为什么命名已经有了base64的转码还要有一个pack方法呢?查了一下阮一峰的讲解
这里直接引用:

大概原因就是Base64将三个字节转化成四个字节

Text content M a n
ASCII 77 97 110
Bitpattern 010011 010110 000101 101110
Index 19 22 5 46
Base64-Encoded T W F u

第一步,”M”、”a”、”n”的ASCII值分别是77、97、110,对应的二进制值是01001101、01100001、01101110,将它们连成一个24位的二进制字符串010011010110000101101110。

第二步,将这个24位的二进制字符串分成4组,每组6个二进制位:010011、010110、000101、101110。

第三步,在每组前面加两个00,扩展成32个二进制位,即四个字节:00010011、00010110、00000101、00101110。它们的十进制值分别是19、22、5、46。

第四步,根据上表,得到每个值对应Base64编码,即T、W、F、u。

因此,Man的Base64编码就是TWFu。

如果字节数不足三,则这样处理:

  1. 二个字节的情况:将这二个字节的一共16个二进制位,按照上面的规则,转成三组,最后一组除了前面加两个0以外,后面也要加两个0。这样得到一个三位的Base64编码,再在末尾补上一个”=”号。比如,”Ma”这个字符串是两个字节,可以转化成三组00010011、00010110、00010000以后,对应Base64值分别为T、W、E,再补上一个”=”号,因此”Ma”的Base64编码就是TWE=。

  2. 一个字节的情况:将这一个字节的8个二进制位,按照上面的规则转成二组,最后一组除了前面加二个0以外,后面再加4个0。这样得到一个二位的Base64编码,再在末尾补上两个”=”号。比如,”M”这个字母是一个字节,可以转化为二组00010011、00010000,对应的Base64值分别为T、Q,再补上二个”=”号,因此”M”的Base64编码就是TQ==。

再举一个中文的例子,汉字”严”如何转化成Base64编码?

这里需要注意,汉字本身可以有多种编码,比如gb2312、utf-8、gbk等等,每一种编码的Base64对应值都不一样。下面的例子以utf-8为例。

首先,”严”的utf-8编码为E4B8A5,写成二进制就是三字节的”11100100 10111000 10100101”。将这个24位的二进制字符串,按照第3节中的规则,转换成四组一共32位的二进制值”00111001 00001011 00100010 00100101”,相应的十进制数为57、11、34、37,它们对应的Base64值就为5、L、i、l。

所以,汉字”严”(utf-8编码)的Base64值就是5Lil。

看完之后虽然对base64已经有所了解但是还是对pack这个方法抱有疑惑,继续查找
往上资料显示原文:

Per OP’s clarified question, “Why do we use #pack to get base64 and #unpack to get other representations of raw data?”

The surface level reason is because Array#pack is a method that returns a String, while String#unpack is a method that returns an Array.

There are stronger conceptual reasons underlying this. The key principle is that base64 is not an array of raw bytes. Rather, it’s a 7-bit-ASCII-safe string that can represent arbitrary bytes if properly (de)coded.

Each base64 character maps to a sequence of six bits. At the byte level, that’s a 4:3 ratio of characters to raw bytes. Since integer powers of 2 don’t divide by 3, we end up with padding more often than not, and you can’t slice base64 in arbitrary places to get ranges of bytes out of it (you’d have to figure out which bytes you want in groups of three and go get the associated base64 characters in groups of four).

Arbitrary sequences of data are, fundamentally, arrays of bytes. Base64-encoded sequences are, fundamentally, strings: data sequences constrained to the range of bytes safely transmissible and displayable as text.

Base64 is the encapsulation (or “packing”) of a data array into a string

根据OP澄清的问题,“为什么我们要使用pack来获取base64和unpack来获取原始数据的其他表示?”

表面层原因是因为 Array#pack 是返回字符串的方法,而String#unpack是返回数组的方法。

这背后有更强有力的概念原因。关键原因是base64不是原始字节数组。相反,它是一个7位的ASCII安全字符串,如果编码正确,它可以表示任意字节。

每个base64字符映射到一个六位序列。在字节级别,这是字符与原始字节的4:3比例。由于2的整数幂不能被3整除,所以我们最终得到字节长度比原字节长度更长,而且你不能在任意位置对base64进行切割,以从中获取字节范围(你必须找出三个字节组中需要哪些字节,然后四个字节组中获取相关的base64字符)。

从根本上讲,任意数据序列是字节数组。基本上,base64编码序列是字符串:数据序列被限制在字节范围内,可以安全地作为文本传输和显示。

base64是将数据数组封装(或“打包”)为字符串

这么看来 pack 和 unpack现在看来就更加形象了

这次遇到一个小问题就是对接方是java代码生成的base64 字符串末尾是的不带\n
然后我使用了 String#chomp方法去掉了末尾的换行符

1
2
pry(main)> Base64.encode64('hello').chomp
=> "aGVsbG8="

但是这里还有点小疑惑,为什么ruby不和java一样反而自己要加换行符呢,原来是因为ruby 的Base64.encode64方法遵循了RFC 4648规范,该方法会将encoded的字符串每60个字符换行(”\n”),所以后续ruby推出了 Base64#strict_encode64Base64#strict_encode64方法

1
2
3
4
pry(main)> Base64.strict_encode64('hello')
=> "aGVsbG8="
pry(main)> Base64.strict_decode64("aGVsbG8=")
=> "hello"

那么问题来了 Base64#strict_encode64 是你怎么实现的呢?

查看源码,原来就是这么简单的且枯燥

1
2
3
4
# File activesupport/lib/active_support/base64.rb, line 29
def Base64.strict_encode64(value)
encode64(value).gsub(/\n/, '')
end

and 和 && 的区别

虽然二者都是与门但是明显 在ruby中 && 的优先级更高

1
2
3
4
5
6
7
8
9
10
11
pry(main)> true and true ? 'hello' : 'hi'
=> "hello"
pry(main)> true && true ? 'hello' : 'hi'
=> "hello"
pry(main)> false and true ? 'hello' : 'hi'
=> false
pry(main)> false && true ? 'hello' : 'hi'
=> "hi"

form_for 和 form_tag 区别

生成的html不同

1
2
3
4
5
6
7
8
9
10
11
form_for @object do |f|
f.text_field :name
end
生成 input id="object_name" name="object[name]"
form_tag @object do
text_field_tag :name
end
生成 input id="name" name="name"

还有就是form_for 算是 对象处理+url+form_tag的一个封装

通过源码也可以看出form_for 在判断了url 和 对象之后还是调用的form_tag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def form_for(record, options = {}, &block)
raise ArgumentError, "Missing block" unless block_given?
options[:html] ||= {}
case record
when String, Symbol
object_name = record
object = nil
else
object = record.is_a?(Array) ? record.last : record
object_name = options[:as] || ActiveModel::Naming.param_key(object)
apply_form_for_options!(record, options)
end
options[:html][:remote] = options.delete(:remote) if options.has_key?(:remote)
options[:html][:method] = options.delete(:method) if options.has_key?(:method)
options[:html][:authenticity_token] = options.delete(:authenticity_token)
builder = options[:parent_builder] = instantiate_builder(object_name, object, options, &block)
output = capture(builder, &block)
default_options = builder.multipart? ? { :multipart => true } : {}
form_tag(options.delete(:url) || {}, default_options.merge!(options.delete(:html))) { output }
end