Struct & OpenStruct

Struct

Struct 用于快速声明一个类。

1
2
3
4
5
6
Person = Struct.new(:age, :name, :sex)
me = Person.new(24, 'Spirit', 'male')
me.age # => 24
me.name = 'Test'
me.name # => 'Test'
me.height # => NoMethodError

优点: 快速声明和实现
缺点: 只能响应定义的字段,不能响应任意的属性

OpenStruct

参考Ruby Ruby 中的 OpenStruct 详解
OpenStruct可以自由设置set和get的一个类

1
2
3
4
5
p = OpenStruct.new()
p.name='hello'
p.name # p.name就是hello
p.age=12
p.age # p.age就是12


skip before_action


API接口中使用cookies的方法

方法一: 在Controller中添加include ActionController::Cookies

1
2
class ApplicationController < ActionController::API
include ActionController::Cookies

在Controller中就可以访问到cookies变量了。

方法二: 如果不想引入过多的方法,可以在Controller中定义cookies方法

1
2
3
4
def cookies
# helpers not available in --api mode
request.cookie_jar
end


Rails 4中memory_store缓存的使用

Rails 4中memory_store类型的基础用法

配置

需要在config/environments/development.rb或者config/environments/production.rb中设置开启cache

  • config.action_controller.perform_caching设置为true
  • config.cache_store设置为:memory_store

命令

所有方法参考: ActiveSupport::Cache::Store on Ruby on Rails 4.0.13

  • 写入Cache,使用Rails.cache.write,使用expires_in来设置过期时间,不设置的话默认不过期。

    1
    Rails.cache.write(cache_key, 10 ,expires_in: 10.seconds)
  • 读取Cache, 使用Rails.cache.read,传入key,读取value

    1
    Rails.cache.read(cache_key)
  • 获取Cache对象,使用Rails.cache.send

    1
    Rails.cache.send(:read_entry, cache_key,{})
  • 增加Cache中integer类型的值, 使用Rails.cache.increment, 注意这边如果不设置expires_in,则该条目就不再过期,无论原先记录write时有没有设置过期时间。

    1
    Rails.cache.increment(cache_key,1, expires_in: 10.seconds)
  • 读取Cache,如果没有就获取内容后插入, 使用Rails.cache.fetch

    • fetch不加block,就相当于read
    • fetch加了block,那么如果要获取的cache不存在,就会将block中的值添加到cache中,并返回。
      1
      2
      3
      Rails.cache.fetch('test_key') do
      'hello world'
      end

上述几个命令操作过程的记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2.3.8 :135 > cache_key='test_key'
=> "test_key"
2.3.8 :136 > Rails.cache.write(cache_key, 10 ,expires_in: 10.seconds)
=> true
2.3.8 :137 > Rails.cache.send(:read_entry, cache_key,{})
=> #<ActiveSupport::Cache::Entry:0x00007fbfa8151750 @value=10, @created_at=1603420143.660662, @expires_in=10.0>
2.3.8 :138 > Rails.cache.read(cache_key)
=> 10
2.3.8 :139 > Rails.cache.increment(cache_key,1, expires_in: 10.seconds)
=> 11
2.3.8 :140 > Rails.cache.read(cache_key)
=> 11
2.3.8 :141 > Rails.cache.send(:read_entry, cache_key,{})
=> #<ActiveSupport::Cache::Entry:0x00007fbfa9003f78 @value=11, @created_at=1603420143.666131, @expires_in=10.0>
2.3.8 :142 >

Reference


输出请求中所有header的值

在base controller中添加如下代码输出headers值。

1
2
3
4
5
6
7
8
9
10
11
12
before_action :output_headers
def output_headers
headers = request.headers.env.select do |k, _|
k.downcase.start_with?('http') ||
k.in?(ActionDispatch::Http::Headers::CGI_VARIABLES)
end
headers = headers.sort.to_h
headers.each do |k,v|
Rails.logger.info "#{k} = #{v}"
end
end

Ruby中Hash根据key值进行排序

1
Hash.new.sort.to_h

Ruby中将Html转为plain text的方法

方法一: Gem html_to_plain_text

  • 地址: html_to_plain_text
  • 用法:
    1
    2
    3
    4
    require 'html_to_plain_text'
    html = "<h1>Hello</h1><p>world!</p>"
    HtmlToPlainText.plain_text(html)
    => "Hello\n\nworld!"

方法二: Rails的strip_tags函数

  • 地址: strip_tags
  • 用法:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    include ActionView::Helpers::SanitizeHelper
    strip_tags("Strip <i>these</i> tags!")
    # => Strip these tags!
    strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
    # => Bold no more! See more here...
    strip_tags("<div id='top-bar'>Welcome to my website!</div>")
    # => Welcome to my website!
    strip_tags("> A quote from Smith & Wesson")
    # => > A quote from Smith & Wesson

Ruby中获取类的父类的途径

类的.ancestors方法,可以获取父类的列表。

1
2
3
4
5
6
2.7.1 :014 > Integer.ancestors
=> [ActiveSupport::NumericWithFormat, ActiveSupport::ToJsonWithActiveSupportEncoder, Integer, Mongoid::Criteria::Queryable::Extensions::Numeric, Mongoid::Extensions::Integer, BSON::Integer, JSON::Ext::Generator::GeneratorMethods::Integer, MessagePack::CoreExt, Numeric, Comparable, ActiveSupport::Dependencies::ZeitwerkIntegration::RequireDependency, ActiveSupport::ToJsonWithActiveSupportEncoder, Object, PP::ObjectMixin, Mongoid::Criteria::Queryable::Extensions::Object, Mongoid::Extensions::Object, BSON::Object, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, ActiveSupport::Dependencies::Loadable, Kernel, BasicObject]
2.7.1 :015 >
2.7.1 :016 > 1.class.ancestors
=> [ActiveSupport::NumericWithFormat, ActiveSupport::ToJsonWithActiveSupportEncoder, Integer, Mongoid::Criteria::Queryable::Extensions::Numeric, Mongoid::Extensions::Integer, BSON::Integer, JSON::Ext::Generator::GeneratorMethods::Integer, MessagePack::CoreExt, Numeric, Comparable, ActiveSupport::Dependencies::ZeitwerkIntegration::RequireDependency, ActiveSupport::ToJsonWithActiveSupportEncoder, Object, PP::ObjectMixin, Mongoid::Criteria::Queryable::Extensions::Object, Mongoid::Extensions::Object, BSON::Object, JSON::Ext::Generator::GeneratorMethods::Object, ActiveSupport::Tryable, ActiveSupport::Dependencies::Loadable, Kernel, BasicObject]
2.7.1 :017 >


Ruby产生随机数

使用randRandom.rand来生成随机数

1
2
3
4
5
6
7
8
9
10
11
2.7.1 :026 > rand(1..10)
=> 10
2.7.1 :027 > rand(1.1..99.99)
=> 22.250780345080248
2.7.1 :028 >
2.7.1 :029 >
2.7.1 :030 > Random.rand(1...10)
=> 1
2.7.1 :031 > Random.rand(1.0...10)
=> 7.646093836025024
2.7.1 :032 >


产生测试数据的Gem Faker

faker-ruby/faker这个Gem,可方便地用于生成测试数据。

用法

Gemfile中 添加在development组里

1
2
3
4
5
group :development do
...
gem 'faker'
...
end


常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 生成随机图片
image_size = "#{rand(400...500)}x#{rand(500...600)}"
Faker::LoremFlickr.image(size: image_size, search_terms: ['sports'])
# 生成随机email
Faker::Internet.email
# 生成标题
title = [Faker::Company.name, Faker::Company.industry].join(' - ')
# 生成描述文字
desc = Faker::Quote.matz
desc = Faker::Hipster.sentences.sample
# 生成随机时间
time = Faker::Time.between(from: DateTime.now - 365, to: DateTime.now + 365, format: :default)

从数组中随机挑选出一个元素

使用Array.sample方法实现随机挑选的操作。

1
2
3
4
5
6
7
2.7.1 :047 > (1..100).to_a.sample
=> 81
2.7.1 :048 > (1..100).to_a.sample
=> 13
2.7.1 :049 > (1..100).to_a.sample
=> 91
2.7.1 :050 >


Rails中转换为Datetime

从字符中得到Datetime: '2020-01-01 00:00:00'.to_datetime
从timestamp得到Datetime: Time.at(1).to_datetime


Rails中validate添加自定义错误

使用.errors.add :base, string来添加自定义的错误。
例如: 实现unique validate的自定义错误。

1
2
3
4
5
6
validate do |block_key_word|
existing_record = BlockKeyWord.where(:name => block_key_word.name).first
unless existing_record.nil? || existing_record.id == block_key_word.id
block_key_word.errors.add :base, ("关键字-#{block_key_word.name}已存在。")
end
end

ruby操作Excel的基本操作

可以使用rubyXL来操作excel
写Excel的例子

1
2
3
4
require 'rubyXL'
workbook = RubyXL::Workbook.new
workbook[0].add_cell(0, 0, "写入内容")
workbook.write("#{Rails.root}/tmp/file.xlsx")


将html中的相对路径转为绝对路径的方法

使用URI.join来转换为绝对路径。URI.join(url, image_relative_path).to_s
如:

1
URI.join('http://www.81.cn/jmywyl/2020-04/04/content_9784967.htm?from=singlemessage&isappinstalled=0','../../attachement/jpg/site351/20200404/309c236f8da41ff2b3033e.jpg').to_s


假删除

acts_as_paranoid是用于model中假删除的一个Gem,使用deleted_at字段是否有值来判断数据是否删除。

acts_as_paranoid混合uniqueness的用法, 使得不删除的物料只能唯一存在。

1
validates :name, :uniqueness => { :message => "名字不能重复。" , :scope => :deleted_at}


Rails development模式下同时收到请求卡住的解决办法

Rails 5.1.7中,在development模式下,如果前端同时发请求特别快,可能会导致后端rails server直接挂起(hang)。
解决方法:
config/environments/development.rb中设置config.eager_load = true

如果还不能解决,尝试将puma的线程池数量改为1
config/puma.rb中设置threads_count = ENV.fetch("RAILS_MAX_THREADS") { 1 }

Reference:


DateTime.parse和Time.parse 默认时区不同

DateTime.parse默认使用的是UTC时区parse,Time.parse默认使用的是系统时区
例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
2.3.8 :060 > Time.zone
=> #<ActiveSupport::TimeZone:0x00007fe7d4fa9330 @name="Beijing", @utc_offset=nil, @tzinfo=#<TZInfo::DataTimezone: Asia/Shanghai>>
2.3.8 :061 >
2.3.8 :062 > date_string="2021-01-01 00:00:00"
=> "2021-01-01 00:00:00"
2.3.8 :063 >
2.3.8 :064 > Time.parse(date_string)
=> 2021-01-01 00:00:00 +0800
2.3.8 :065 >
2.3.8 :066 > DateTime.parse(date_string)
=> Fri, 01 Jan 2021 00:00:00 +0000
2.3.8 :067 >
2.3.8 :068 >

参考: Rails parse date time string in UTC zone


Rails中文字符按byte截取

有些情况下,需要按照byte长度来截取中文,但是中文字符是多字节的,直接按照子节长度截取可能会截取不完全。
此时,使用ActiveSupport::Multibyte::Chars中的limit方法,就可以解决这个问题。

用法: "你好,世界!".mb_chars.limit(12).to_s, 输出: "你好,世"

rails c实验如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2.3.8 :073 > str="你好,世界!"
=> "你好,世界!"
2.3.8 :074 > str.mb_chars
=> #<ActiveSupport::Multibyte::Chars:0x00007f87d3f5a548 @wrapped_string="你好,世界!">
2.3.8 :075 > str.mb_chars.bytes
=> [228, 189, 160, 229, 165, 189, 239, 188, 140, 228, 184, 150, 231, 149, 140, 239, 188, 129]
2.3.8 :076 > str.mb_chars.bytesize
=> 18
2.3.8 :077 > str.mb_chars.limit(5).to_s
=> "你"
2.3.8 :078 > str.mb_chars.limit(6).to_s
=> "你好"
2.3.8 :079 > str.mb_chars.limit(7).to_s
=> "你好"
2.3.8 :080 > str.mb_chars.limit(8).to_s
=> "你好"
2.3.8 :081 > str.mb_chars.limit(9).to_s
=> "你好,"
2.3.8 :082 > str.mb_chars.limit(10).to_s
=> "你好,"
2.3.8 :083 >

Reference: Ruby: Limiting a UTF-8 string by byte-length


Rails中使用AWS S3 object presign url

Gemfile中

1
gem 'aws-sdk', '~> 3'

签名方法:

1
2
3
4
5
bucket="target-bucket-name"
object_name="obj.jpg"
obj = Aws::S3::Object.new(bucket, object_name)
obj.presigned_url(:put, acl: 'public-read')

接着拿获取到的签名的链接,在前端进行上传。

Reference: Class: Aws::S3::Object

rvm生成.ruby-gemset和.ruby-version

例如,以ruby 2.7.1中的rails6guide gemset为例, 使用如下命令

1
rvm --create --ruby-version ruby-2.7.1@rails6guide

会生成如下.ruby-version和

1
2
3
4
5
6
$ cat .ruby-version
ruby-2.7.1
$
$ cat .ruby-gemset
rails6guide
$

Ruby map, each, collect, inject, reject,select 简单用法

map

map是对array中的每一个元素执行action, 原始的array不会被修改,返回的是修改后的array

1
2
[1,2,3,4,5,6,7,8,9,10].map{|e| e*3 }
# returns [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]

each

each 只是对array中的每个元素执行action, 返回的还是原始的array

1
2
3
[1,2,3,4,5,6,7,8,9,10].each{|e| print e.to_s+"!" }
# prints "1!2!3!4!5!6!7!8!9!10!"
# returns [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

collect

map的别名

inject

Takes an accumulator (sum) and changes it as many times as there are elements in the array. Returns the final value of the accumulator.

1
2
[1,2,3,4,5,6,7,8,9,10].inject{|sum,e| sum += e }
# returns 55

You can also specify an initial value as parameter before the block.

1
2
["bar","baz","quux"].inject("foo") {|acc,elem| acc + "!!" + elem }
# returns "foo!!bar!!baz!!quux"

reduce

inject的别名

select

类似其他语言的filter,对array中的每个元素执行一个运算,如果结果是true,则对应的元素将会被添加到返回的数组中

1
2
[1,2,3,4,5,6,7,8,9,10].select{|el| el%2 == 0 }
# returns [2,4,6,8,10]

find

对数组中的元素执行运算,返回第一个true的元素。

1
2
[1,2,3,4,5,6,7,8,9,10].find{|el| el / 2 == 2 }
# returns 4

detect

find的别名

reject

select的作用相反,对array中的每个元素执行一个运算,如果结果是false, 则对应的元素会被添加到返回的数组中

1
2
[1,2,3,4,5,6,7,8,9,10].reject{|e| e==2 || e==8 }
# returns [1, 3, 4, 5, 6, 7, 9, 10]

Reference

字符串去除空格,\t,\r,\n

使用String类的delete方法, 如

1
string.delete(" \t\r\n")

去除所有html标签的方法

使用include ActionView::Helpers::SanitizeHelper中的strip_tags方法

1
2
include ActionView::Helpers::SanitizeHelper
strip_tags(html_string)

字符串转正则的方法

使用方法regex = Regexp.new regex_string。例如:

1
2
3
4
5
6
7
8
9
src_string="I'm a boy"
regex_string ="[Bb]oy"
regex = Regexp.new regex_string
if src_string =~ regex
print "Match"
else
print "MissMatch"
end

RestClient response中获取request.url

有些网页请求,会有redirecct,导致获取数据的网页和直接请求的Url不同。
如果用的是RestClient, 可使用response.request.url来获取redirect后的url。

留言