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

生成结果:=> "http://www.81.cn/jmywyl/attachement/jpg/site351/20200404/309c236f8da41ff2b3033e.jpg"


假删除

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。


RestClient 发送请求只获取headers的方法

只获取网络上图片和视频大小时,可以只获取url的header,然后根据content_length来获取对应图片或视频的大小,而不用将整个文件下载下来。
RestClient发送请求只获取header的方法是,使用:head的method。

1
response = RestClient::Request.execute(method: :head, url: url, timeout: 10)

注意点: 因为只获取header,因此response中的body是空的。因此就不能用response.present?来判断请求是否成功。

例子:

1
2
3
4
url="http://lh.rdcpix.com/00b95cd7bb26ff97392d3fa5c480706cl-f61050563r.jpg"
response = RestClient::Request.execute(method: :head, url: url, timeout: 10)
puts response.headers
=> {:content_type=>"image/jpeg", :content_length=>"342576", :connection=>"keep-alive", :date=>"Thu, 25 Aug 2022 07:29:21 GMT", :last_modified=>"Thu, 26 May 2022 14:15:17 GMT", :etag=>"\"f0f01cc048b5b969abb45b5b3ad951af\"", :accept_ranges=>"bytes", :server=>"AmazonS3", :x_cache=>"Hit from cloudfront", :via=>"1.1 a3c2566f9e36ad3cdf79fc6307fcf566.cloudfront.net (CloudFront)", :x_amz_cf_pop=>"FRA53-C1", :x_amz_cf_id=>"PhcWnxq2z3HtZXXJIMYxltr7-ua08teFX7Ec-3sVJvzYTITCMjXgdQ==", :age=>"32481"}


在Array或者ActiveRecord::Relation中手动剔除不需要的数据的方法

有时候,数据查询时没法使用sql来剔除数据,需要捞出来后手动处理。
此时可遍历后再使用.select!方法选出符合要求的数据。
如下是一个三层的例子: items has_many sub_items, sub_items has_many item_children

1
2
3
4
5
6
items.each do |item|
item.sub_items.select! {|sub_item| sub_item.is_deleted == false}
item.sub_items.each do |sub_item|
sub_item.item_children.select! {|item_child| item_child.is_deleted == false}
end
end

API接口返回json格式时

Model.as_json方法,只有有限的:only,:except,:methods:include几个有限的方法,不建议使用。
使用Jbuilder来处理,会更加的方便快捷。


Rails设计API接口支持字段排序sort

代码摘录自Github gist mamantoha/experience.rb

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
# app/controllers/concerns/orderable.rb
module Orderable
extend ActiveSupport::Concern
module ClassMethods
end
# A list of the param names that can be used for ordering the model list
def ordering_params(params)
# For example it retrieves a list of experiences in descending order of price.
# Within a specific price, older experiences are ordered first
#
# GET /api/v1/experiences?sort=-price,created_at
# ordering_params(params) # => { price: :desc, created_at: :asc }
# Experience.order(price: :desc, created_at: :asc)
#
ordering = {}
if params[:sort]
sort_order = { '+' => :asc, '-' => :desc }
sorted_params = params[:sort].split(',')
sorted_params.each do |attr|
sort_sign = (attr =~ /\A[+-]/) ? attr.slice!(0) : '+'
model = controller_name.classify.constantize
if model.attribute_names.include?(attr)
ordering[attr] = sort_order[sort_sign]
end
end
end
return ordering
end
end

用法:

1
2
3
4
5
6
7
8
9
10
# GET /api/v1/experiences
def index
@experiences = apply_scopes(Experience).
order(ordering_params(params)).
# fix N+1 query problem
includes(:city, :user, :categories).
all
render json: @experiences
end

改造了下,用于自定义白名单valid_sort_keys, 并且低版本Rails, 比如Rails 3.x不支持Model.order({id: :desc})这种写法,因此需要拼order的字串才能使用。

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
## 排序设置
# 按发布时间排序, 按ID排序, 比如-release_time,+id , -表示倒序,+表示正序
sort_by = params[:sort]
## 例子
# # GET /api/v1/experiences?sort=-price,created_at
# # ordering_params(params) # => { price: :desc, created_at: :asc }
# # Experience.order(price: :desc, created_at: :asc)
ordering = {}
if sort_by.present?
sort_order = { '+' => :asc, '-' => :desc }
valid_sort_keys = ["id", "release_time"]
sorted_params = sort_by.split(',')
sorted_params.each do |attr|
sort_sign = (attr =~ /\A[+-]/) ? attr.slice!(0) : '+'
if valid_sort_keys.include?(attr)
ordering[attr] = sort_order[sort_sign]
end
end
else
ordering = {"id": "desc"}
end
ordering_string = ""
ordering.each do |k,v|
if ordering_string.blank?
ordering_string += "#{k.to_s} #{v.to_s}"
else
ordering_string += ",#{k.to_s} #{v.to_s}"
end
end
ordering_string = "id desc" if ordering_string.blank?


Rails获取当前数据库连接

rails c中输入如下指令,输出当前使用的数据库信息:

1
Rails.configuration.database_configuration[Rails.env]


Ruby中随机打乱Array的数据

使用Array的shuffle方法, 可随机打乱Array中的元素顺序

1
2
3
4
5
6
7
8
$ irb
2.3.8 :001 > [1,2,3,4].shuffle
=> [1, 4, 2, 3]
2.3.8 :002 > [1,2,3,4].shuffle
=> [1, 3, 2, 4]
2.3.8 :003 > [1,2,3,4].shuffle
=> [2, 3, 1, 4]
2.3.8 :004 >

Reference:


rvm 创建并使用新的gemset

开发中,可以使用rvm为每个项目创建各自的gemset,从而达到各个项目环境各自独立的目的。
ruby-2.3.0中创建名为test-for-blog的gemset为例,创建的步骤如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 使用ruby版本2.3.0
rvm use ruby-2.3.0
# 创建名为test-for-blog的gemset
rvm gemset create test-for-blog
# 切换当前环境为该gemset
rvm use ruby-2.3.0@rails5
# 在该gemset中进行bundle安装gem
bundle install


获取两个日期中大的或小的值

可以将两个日期组成Array, 然后直接使用Array的max和min方法

1
2
3
4
5
[1] pry(main)> [DateTime.parse('2022-01-01 12:00:00'), DateTime.parse('2022-02-01 00:00:00')].max
=> Tue, 01 Feb 2022 00:00:00 +0000
[2] pry(main)> [DateTime.parse('2022-01-01 12:00:00'), DateTime.parse('2022-02-01 00:00:00')].min
=> Sat, 01 Jan 2022 12:00:00 +0000
[3] pry(main)>


将元素插入数组中的方法

按照位置进行插入

Array.insert(index, element)可以直接将element插入array中的第index的位置

1
2
3
[8] pry(main)> [1,2,3,4].insert(1,100)
=> [1, 100, 2, 3, 4]
[9] pry(main)>

插到数组末尾

Array.push(element) 或者 Array.insert(Array.length, element)

根据数组中某个元素的值来确定插入地址

  1. 使用index来获取符合条件的元素的索引值,以元素是hash为例:array.index {|h| h[:id] == 34 }
  2. 获取索引值后,再按照实际要求使用Array.insert(index_xx, element)来插入element

参考:


Array数组移除重复项

Array.uniq()可以移除Array中的重复项。
当指定block的时候,就按照block中返回的值来去重

1
2
3
4
5
6
7
8
> b = [["student","sam"], ["student","george"], ["teacher","matz"]]
=> [["student", "sam"], ["student", "george"], ["teacher", "matz"]]
> b.uniq { |s| s.first }
=> [["student", "sam"], ["teacher", "matz"]]
>
> b.uniq { |s| true }
=> [["student", "sam"]]
>

一些高阶用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## 根据元素某个函数的值来uniq
fruits = %w(orange apple banana)
fruits.uniq(&:size) => 结果为: ["orange", "apple"]
## 根据元素的类类型来uniq
objects = [1, 2, "a", "b", :c, :d]
objects.uniq(&:class) => 结果为: [1, "a", :c]
## 混合字段来uniq
User类有
- age
- name
- country
三个元素
[david, petter, raphael].uniq { |person| [person.age, person.country] }
上面式子会根据age,country组合来进行uniq

Reference:


从Url中获取文件后缀

方法:

1
2
file = 'http://recyclewearfashion.com/stylesheets/page_css/page_css_4f308c6b1c83bb62e600001d.css?1343074150'
File.extname(URI.parse(file).path) # => '.css'

但要注意URI.parse解析不正确的URL时,会报exception,注意根据实际情况进行catch

参考: How to get the file extension from a url?


替换Url中的host

方法: 使用URI.parse(url).host获取host,然后再string.gsub进行替换。

1
2
3
4
url="https://www.baidu.com/helloworld"
host = URI.parse(url).host rescue ''
url = url.gsub(host, "www.qq.com") if host.present?
## url => https://www.qq.com/helloworld

注意URI.parse解析不正确的URL时,会报exception,所以这边有rescue ''来catch exception。


字符串多分隔符分割

可在String.split中使用正则来实现

1
2
3
word = "Now is the,time for'all good people"
word.split(/[\s,']/)
=> ["Now", "is", "the", "time", "for", "all", "good", "people"]

回车换行分割的例子: word.split(/[\r\n]/)

参考:

Rails中按照自定义顺序选取数据

Mysql中可使用field()函数来自定义排序,PG中没有现成函数可用,可是使用order by CASE WHEN field='value1 THEN 1 WHEN field='value2 THEN 2 ... END的方式来使用。下面介绍在Rails中如何使用

Rails 7 中现成的ActiveRecord::QueryMethods#in_order_of 函数

参照Github中的Pull Request Add ActiveRecord::QueryMethods#in_order_of, Rails 7中有in_order_of方法来实现上述功能。

Pull Request中有详细的使用方法介绍,摘录如下:

1
Post.in_order_of(:id, [3, 5, 1])

will generate the SQL:

1
SELECT "posts".* FROM "posts" ORDER BY CASE "posts"."id" WHEN 3 THEN 1 WHEN 5 THEN 2 WHEN 1 THEN 3 ELSE 4 END ASC

However, because this functionality is built into MySQL in the form of theFIELD function, that connection adapter will generate the following SQL instead:

1
SELECT "posts".* FROM "posts" ORDER BY FIELD("posts"."id", 1, 5, 3) DESC

Rails 7 以下的MySQL数据库使用方法

sort in MySQL:

1
2
3
> ids = [2,5,7]
=> [2, 5, 7]
> Image.where(id: ids).order("field(id, #{ids.join(',')})")

生成的SQL为:

1
SELECT `images`.* FROM `images` WHERE `images`.`id` IN (2, 5, 7) ORDER BY field(id, 2,5,7)

MySQL下的field()函数对不在field()函数中的数据的排序还有一些说明,可以参考 PostgreSQL TipsPG中如何实现类似MySQL中按照FIELD函数排序的功能这一章节的说明。

Rails 6.1以下的PG数据库使用方法

参考自: https://stackoverflow.com/a/26777669, 需要在model中内建self.order_by_ids方法。

1
2
3
4
5
6
7
8
9
10
11
def self.order_by_ids(ids)
order_by = ["CASE"]
ids.each_with_index do |id, index|
order_by << "WHEN id='#{id}' THEN #{index}"
end
order_by << "END"
order(order_by.join(" "))
end
User.where(id: [9,1000,2]).order_by_ids([9,1000,2]).map(&:id)
#=> [9, 1000, 2]

上面model查询对应生成的语句为:

1
SELECT "users".* FROM "users" WHERE "users"."id" IN (9, 1000, 2) AND ("users".deleted_at IS NULL) ORDER BY CASE WHEN id='9' THEN 0 WHEN id='1000' THEN 1 WHEN id='2' THEN 2 END

Rails 6.1以上的PG数据库使用方法

参考自: https://stackoverflow.com/a/66517571

Rails 6.1以上直接进行sql拼装的话,会报ActiveRecord::UnknownAttributeReference的错误。

1
ActiveRecord::UnknownAttributeReference (Query method called with non-attribute argument(s): "CASE WHEN id='9' THEN 0 WHEN id='1000' THEN 1 WHEN id='2' THEN 2 END")

所以self.order_by_ids的方法应该修改为如下:

1
2
3
4
5
6
7
8
def self.order_by_ids(ids)
t = User.arel_table
condition = Arel::Nodes::Case.new(t[:id])
ids.each_with_index do |id, index|
condition.when(id).then(index)
end
order(condition)
end

此时User.where(id: [9,1000,2]).order_by_ids([9,1000,2])生成的sql如下, 可以正常work。

1
SELECT "users".* FROM "users" WHERE "users"."deleted_at" IS NULL AND "users"."id" IN (9, 1000, 2) ORDER BY CASE "users"."id" WHEN 9 THEN 0 WHEN 1000 THEN 1 WHEN 2 THEN 2 END

Rails中使用PG中ARRAY_POSITION函数来实现

参考自: https://stackoverflow.com/a/57798183, 在PG中,可以在order语句中使用ARRAY_POSITION来实现自定义排序。

1
2
ids=[9,1000,2]
User.where(:id => ids).order("ARRAY_POSITION(ARRAY#{ids.to_s}::int[], id)").pluck(:id)

生成的SQL语句如下,也可以实现对应的功能。

1
SELECT "users"."id" FROM "users" WHERE 1=0 AND ("users".deleted_at IS NULL) ORDER BY ARRAY_POSITION(ARRAY[]::int[], id)

参考


遍历修改hash中的值

遍历修改hash中值的方法

1
2
3
4
5
6
7
my_hash = {"a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5}
my_hash.each do |k,v|
my_hash[k] = v.ordinalize
end
puts my_hash

结果为: {"a"=>"1st", "b"=>"2nd", "c"=>"3rd", "d"=>"4th", "e"=>"5th"}

其他修改方法可参考: Changing every value in a hash in Ruby

留言

2020-05-14
  1. Struct & OpenStruct
    1. Struct
    2. OpenStruct
  2. skip before_action
  3. API接口中使用cookies的方法
  4. Rails 4中memory_store缓存的使用
    1. 配置
    2. 命令
    3. Reference
  5. 输出请求中所有header的值
  6. Ruby中Hash根据key值进行排序
  7. Ruby中将Html转为plain text的方法
  8. Ruby中获取类的父类的途径
  9. Ruby产生随机数
  10. 产生测试数据的Gem Faker
    1. 用法
    2. 常用方法
  11. 从数组中随机挑选出一个元素
  12. Rails中转换为Datetime
  13. Rails中validate添加自定义错误
  14. ruby操作Excel的基本操作
  15. 将html中的相对路径转为绝对路径的方法
  16. 假删除
  17. Rails development模式下同时收到请求卡住的解决办法
  18. DateTime.parse和Time.parse 默认时区不同
  19. Rails中文字符按byte截取
  20. Rails中使用AWS S3 object presign url
  21. rvm生成.ruby-gemset和.ruby-version
  22. Ruby map, each, collect, inject, reject,select 简单用法
    1. map
    2. each
    3. collect
    4. inject
    5. reduce
    6. select
    7. find
    8. detect
    9. reject
    10. Reference
  23. 字符串去除空格,\t,\r,\n
  24. 去除所有html标签的方法
  25. 字符串转正则的方法
  26. RestClient response中获取request.url
  27. RestClient 发送请求只获取headers的方法
  28. 在Array或者ActiveRecord::Relation中手动剔除不需要的数据的方法
  29. API接口返回json格式时
  30. Rails设计API接口支持字段排序sort
  31. Rails获取当前数据库连接
  32. Ruby中随机打乱Array的数据
  33. rvm 创建并使用新的gemset
  34. 获取两个日期中大的或小的值
  35. 将元素插入数组中的方法
    1. 按照位置进行插入
    2. 插到数组末尾
    3. 根据数组中某个元素的值来确定插入地址
  36. Array数组移除重复项
  37. 从Url中获取文件后缀
  38. 替换Url中的host
  39. 字符串多分隔符分割
  40. Rails中按照自定义顺序选取数据
    1. Rails 7 中现成的ActiveRecord::QueryMethods#in_order_of 函数
    2. Rails 7 以下的MySQL数据库使用方法
    3. Rails 6.1以下的PG数据库使用方法
    4. Rails 6.1以上的PG数据库使用方法
    5. Rails中使用PG中ARRAY_POSITION函数来实现
    6. 参考
  41. 遍历修改hash中的值