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
在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值进行排序
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产生随机数 使用rand和Random.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 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。
只获取网络上图片和视频大小时,可以只获取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)
根据数组中某个元素的值来确定插入地址
使用index来获取符合条件的元素的索引值,以元素是hash为例:array.index {|h| h[:id] == 34 }
获取索引值后,再按照实际要求使用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中如何使用
参照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 Tips 中PG中如何实现类似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 )
上面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