easy_tools 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/.vscode/extensions.json +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +6 -0
- data/easy_tools.gemspec +46 -0
- data/lib/auto_load.rb +45 -0
- data/lib/easy_tools/batch_manager.rb +42 -0
- data/lib/easy_tools/constant.rb +53 -0
- data/lib/easy_tools/core.rb +57 -0
- data/lib/easy_tools/date_time.rb +73 -0
- data/lib/easy_tools/date_time_ext/validator.rb +362 -0
- data/lib/easy_tools/math.rb +40 -0
- data/lib/easy_tools/rails.rb +24 -0
- data/lib/easy_tools/rails_ext/hash.rb +20 -0
- data/lib/easy_tools/version.rb +3 -0
- data/lib/easy_tools.rb +43 -0
- metadata +82 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 68fe26d2e704b26fafef4dbf80b51d02f9fb23d7b6f2294d957192e725d82add
|
|
4
|
+
data.tar.gz: af7447ee4aae049bb9f9aa6e9ca8b73af9c9ed3a6d345e7b89d324f0d9121245
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ba25b53e0d8be84825f03afae7deb5b2755027a57e08b2d036a538d1feefb1b2f1dc581b2982f2b5d6d63dc97f38941b9b6b534ade7efe66e3e0741f63a150f6
|
|
7
|
+
data.tar.gz: 38433117b123422f6a0e05c0a390f9035502954487e525308392a1052b10038586176a2f6838a808bf84a13ae8539033f8b7d886763e2e5238ac0ba7e2e5a500
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
|
10
|
+
orientation.
|
|
11
|
+
|
|
12
|
+
## Our Standards
|
|
13
|
+
|
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
|
15
|
+
include:
|
|
16
|
+
|
|
17
|
+
* Using welcoming and inclusive language
|
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
|
19
|
+
* Gracefully accepting constructive criticism
|
|
20
|
+
* Focusing on what is best for the community
|
|
21
|
+
* Showing empathy towards other community members
|
|
22
|
+
|
|
23
|
+
Examples of unacceptable behavior by participants include:
|
|
24
|
+
|
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
|
26
|
+
advances
|
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
|
28
|
+
* Public or private harassment
|
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
|
30
|
+
address, without explicit permission
|
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
|
32
|
+
professional setting
|
|
33
|
+
|
|
34
|
+
## Our Responsibilities
|
|
35
|
+
|
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
|
38
|
+
response to any instances of unacceptable behavior.
|
|
39
|
+
|
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
|
44
|
+
threatening, offensive, or harmful.
|
|
45
|
+
|
|
46
|
+
## Scope
|
|
47
|
+
|
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
|
49
|
+
when an individual is representing the project or its community. Examples of
|
|
50
|
+
representing a project or community include using an official project e-mail
|
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
|
53
|
+
further defined and clarified by project maintainers.
|
|
54
|
+
|
|
55
|
+
## Enforcement
|
|
56
|
+
|
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
58
|
+
reported by contacting the project team at andy.you@rccchina.com. All
|
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
|
63
|
+
|
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
|
66
|
+
members of the project's leadership.
|
|
67
|
+
|
|
68
|
+
## Attribution
|
|
69
|
+
|
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
|
72
|
+
|
|
73
|
+
[homepage]: http://contributor-covenant.org
|
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 andy.you
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
* @Author: andy.you andy.you@rccchina.com
|
|
3
|
+
* @Date: 2025-08-20 13:42:05
|
|
4
|
+
* @LastEditors: andy.you andy.you@rccchina.com
|
|
5
|
+
* @LastEditTime: 2025-08-20 13:44:27
|
|
6
|
+
* @FilePath: /easy_tools/README.md
|
|
7
|
+
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
8
|
+
-->
|
|
9
|
+
# EasyTools
|
|
10
|
+
|
|
11
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/easy_tools`. To experiment with that code, run `bin/console` for an interactive prompt.
|
|
12
|
+
|
|
13
|
+
一些简单的辅助工具,帮助你减少一些重复的代码
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Add this line to your application's Gemfile:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem 'easy_tools'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
And then execute:
|
|
24
|
+
|
|
25
|
+
$ bundle
|
|
26
|
+
|
|
27
|
+
Or install it yourself as:
|
|
28
|
+
|
|
29
|
+
$ gem install easy_tools
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
TODO: Write usage instructions here
|
|
34
|
+
|
|
35
|
+
## Development
|
|
36
|
+
|
|
37
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
38
|
+
|
|
39
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
40
|
+
|
|
41
|
+
## Contributing
|
|
42
|
+
|
|
43
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/easy_tools. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
48
|
+
|
|
49
|
+
## Code of Conduct
|
|
50
|
+
|
|
51
|
+
Everyone interacting in the EasyTools project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/easy_tools/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/easy_tools.gemspec
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require 'easy_tools/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'easy_tools'
|
|
7
|
+
spec.version = EasyTools::VERSION
|
|
8
|
+
spec.authors = ['andy.you']
|
|
9
|
+
spec.email = ['andy.you@rccchina.com']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'EasyTools is a collection of utility methods and classes for Ruby on Rails applications.'
|
|
12
|
+
spec.description = 'EasyTools provides a set of helper methods and classes to simplify common tasks in Ruby on Rails applications, enhancing productivity and code maintainability.'
|
|
13
|
+
spec.homepage = 'https://git.rccchina.com/leads-in/omnicrm'
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
|
18
|
+
if spec.respond_to?(:metadata)
|
|
19
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
20
|
+
|
|
21
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
22
|
+
spec.metadata['source_code_uri'] = 'https://git.rccchina.com/leads-in/omnicrm'
|
|
23
|
+
spec.metadata['changelog_uri'] = 'https://git.rccchina.com/leads-in/omnicrm'
|
|
24
|
+
else
|
|
25
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
|
26
|
+
'public gem pushes.'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Specify which files should be added to the gem when it is released.
|
|
30
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
31
|
+
spec.files = Dir.chdir(__dir__) do
|
|
32
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
33
|
+
(File.expand_path(f) == __FILE__) ||
|
|
34
|
+
# 过滤一些不希望打包进gem的文件
|
|
35
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile .rubocop.yml])
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
spec.bindir = 'exe'
|
|
40
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
41
|
+
spec.require_paths = ['lib']
|
|
42
|
+
|
|
43
|
+
spec.add_development_dependency 'bundler', '~> 1.17'
|
|
44
|
+
# spec.add_development_dependency 'rake', '~> 10.0'
|
|
45
|
+
# spec.add_development_dependency 'rspec', '~> 3.0'
|
|
46
|
+
end
|
data/lib/auto_load.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 自动加载
|
|
2
|
+
class AutoLoad
|
|
3
|
+
def self.define_autoloads(base_dir, namespace)
|
|
4
|
+
base_dir = File.expand_path(base_dir)
|
|
5
|
+
|
|
6
|
+
Dir.glob("#{base_dir}/**/*.rb").each do |file|
|
|
7
|
+
# 获取相对路径并移除扩展名
|
|
8
|
+
relative_path = file.sub("#{base_dir}/", '').sub('.rb', '')
|
|
9
|
+
path_parts = relative_path.split('/')
|
|
10
|
+
|
|
11
|
+
# 最后一个部分是类名
|
|
12
|
+
class_name = camelize(path_parts.pop)
|
|
13
|
+
# 前面的部分是模块路径
|
|
14
|
+
module_parts = path_parts.map { |part| camelize(part) }
|
|
15
|
+
|
|
16
|
+
# 找到或创建父模块
|
|
17
|
+
parent_module = find_or_create_module(module_parts, namespace)
|
|
18
|
+
|
|
19
|
+
# 设置 autoload(必须是符号)
|
|
20
|
+
parent_module.autoload class_name.to_sym, file
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.find_or_create_module(module_parts, base_module)
|
|
25
|
+
current_module = base_module
|
|
26
|
+
|
|
27
|
+
module_parts.each do |mod_name|
|
|
28
|
+
mod_symbol = mod_name.to_sym
|
|
29
|
+
|
|
30
|
+
if current_module.const_defined?(mod_symbol)
|
|
31
|
+
current_module = current_module.const_get(mod_symbol)
|
|
32
|
+
else
|
|
33
|
+
new_module = Module.new
|
|
34
|
+
current_module.const_set(mod_symbol, new_module)
|
|
35
|
+
current_module = new_module
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
current_module
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.camelize(string)
|
|
43
|
+
string.split('_').map(&:capitalize).join
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module EasyTools
|
|
2
|
+
# 批量处理数据
|
|
3
|
+
class BatchManager
|
|
4
|
+
attr_accessor :is_wait, :interval, :max_thread
|
|
5
|
+
|
|
6
|
+
def initialize(data)
|
|
7
|
+
@data = data
|
|
8
|
+
@size = data.size
|
|
9
|
+
# 最大线程数不能超过数据库连接数
|
|
10
|
+
# 可能会取不到线程数,默认为1
|
|
11
|
+
@max_thread = ActiveRecord::Base.connection_pool.db_config.pool rescue 1
|
|
12
|
+
# 每根线程处理的业务数量
|
|
13
|
+
@interval = 100
|
|
14
|
+
# 是否等待结束
|
|
15
|
+
@is_wait = false
|
|
16
|
+
# 线程数组
|
|
17
|
+
@threads = []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# 启用多线程处理
|
|
22
|
+
#
|
|
23
|
+
def do_action
|
|
24
|
+
step = @size < @interval ? @size : @interval
|
|
25
|
+
start = 0
|
|
26
|
+
(start...@size).step(step).each_with_index.map do |index, _i|
|
|
27
|
+
end_step = step + index
|
|
28
|
+
@threads << Thread.new(@data[start...end_step]) do |data|
|
|
29
|
+
append_thread_function
|
|
30
|
+
yield data
|
|
31
|
+
end
|
|
32
|
+
start = end_step
|
|
33
|
+
# 线程大于等于线程数,阻塞等待
|
|
34
|
+
@threads.map(&:join) if @threads.size >= @max_thread
|
|
35
|
+
end
|
|
36
|
+
@threads.map(&:join) if @is_wait
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# 增加线程处理中统一的方法
|
|
40
|
+
def append_thread_function; end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module EasyTools
|
|
2
|
+
module Constant
|
|
3
|
+
# 一分钟的秒数
|
|
4
|
+
SECONDS_IN_MINUTE = 60
|
|
5
|
+
# 一小时的秒数
|
|
6
|
+
SECONDS_IN_HOUR = 60 * 60
|
|
7
|
+
# 一天的秒数
|
|
8
|
+
SECONDS_IN_DAY = 60 * 60 * 24
|
|
9
|
+
# 一周的秒数
|
|
10
|
+
SECONDS_IN_WEEK = 60 * 60 * 24 * 7
|
|
11
|
+
# 30天的秒数
|
|
12
|
+
SECONDS_IN_MONTH = 60 * 60 * 24 * 30 # 按30天计算
|
|
13
|
+
# 365天的秒数
|
|
14
|
+
SECONDS_IN_YEAR = 60 * 60 * 24 * 365 # 按365天计算
|
|
15
|
+
# 邮箱 正则表达式
|
|
16
|
+
EMAIL_REGULAR_EXPRESSION = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/.freeze
|
|
17
|
+
# 手机号 正则表达式
|
|
18
|
+
MOBILE_REGULAR_EXPRESSION = /^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/.freeze
|
|
19
|
+
# 电话 正则表达式
|
|
20
|
+
PHONE_REGULAR_EXPRESSION = /\d{3}-\d{8}|\d{4}-\d{7}/.freeze
|
|
21
|
+
# 网址/链接正则
|
|
22
|
+
LINK_REGULAR_EXPRESSION = %r{^((ht|f)tps?)://[\w-]+(\.[\w-]+)+([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?$}.freeze
|
|
23
|
+
|
|
24
|
+
# 支持的所有日期时间格式正则表达式
|
|
25
|
+
DATETIME_FORMATS = {
|
|
26
|
+
# 基本日期格式(支持个位数)
|
|
27
|
+
yyyymmdd: /\A\d{8}\z/, # 20231225
|
|
28
|
+
yyyy_mm_dd: /\A\d{4}-\d{1,2}-\d{1,2}\z/, # 2023-12-25 或 2023-1-5
|
|
29
|
+
yyyy_slash_mm_dd: %r{\A\d{4}/\d{1,2}/\d{1,2}\z}, # 2023/12/25 或 2023/1/5
|
|
30
|
+
yy_mm_dd: /\A\d{2}-\d{1,2}-\d{1,2}\z/, # 23-12-25 或 23-1-5
|
|
31
|
+
yy_slash_mm_dd: %r{\A\d{2}/\d{1,2}/\d{1,2}\z}, # 23/12/25 或 23/1/5
|
|
32
|
+
yyyy_dot_mm_dd: /\A\d{4}\.\d{1,2}\.\d{1,2}\z/, # 2023.12.25 或 2023.1.5
|
|
33
|
+
yy_dot_mm_dd: /\A\d{2}\.\d{1,2}\.\d{1,2}\z/, # 23.12.25 或 23.1.5
|
|
34
|
+
|
|
35
|
+
# 中文日期格式(支持个位数)
|
|
36
|
+
chinese_date: /\A\d{4}年\d{1,2}月\d{1,2}日\z/, # 2023年12月25日 或 2023年1月5日
|
|
37
|
+
chinese_short_date: /\A\d{2}年\d{1,2}月\d{1,2}日\z/, # 23年12月25日 或 23年1月5日
|
|
38
|
+
|
|
39
|
+
# 日期时间格式(支持个位数)
|
|
40
|
+
yyyymmdd_hhmmss: /\A\d{8} \d{1,2}:\d{1,2}:\d{1,2}\z/, # 20231225 14:30:45 或 20231225 4:3:5
|
|
41
|
+
yyyy_mm_dd_hhmmss: /\A\d{4}-\d{1,2}-\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}\z/, # 2023-12-25 14:30:45 或 2023-1-5 4:3:5
|
|
42
|
+
yyyy_slash_mm_dd_hhmmss: %r{\A\d{4}/\d{1,2}/\d{1,2} \d{1,2}:\d{1,2}:\d{1,2}\z}, # 2023/12/25 14:30:45 或 2023/1/5 4:3:5
|
|
43
|
+
iso_datetime: /\A\d{4}-\d{1,2}-\d{1,2}T\d{1,2}:\d{1,2}:\d{1,2}\z/, # 2023-12-25T14:30:45 或 2023-1-5T4:3:5
|
|
44
|
+
|
|
45
|
+
# 中文日期时间格式(支持个位数)
|
|
46
|
+
chinese_datetime: /\A\d{4}年\d{1,2}月\d{1,2}日\s\d{1,2}时\d{1,2}分\d{1,2}秒\z/, # 2023年12月25日 14时30分45秒 或 2023年1月5日 4时3分5秒
|
|
47
|
+
chinese_datetime_short: /\A\d{2}年\d{1,2}月\d{1,2}日\s\d{1,2}时\d{1,2}分\d{1,2}秒\z/, # 23年12月25日 14时30分45秒 或 23年1月5日 4时3分5秒
|
|
48
|
+
|
|
49
|
+
# 纯时间格式(支持个位数)
|
|
50
|
+
time_only: /\A\d{1,2}:\d{1,2}:\d{1,2}\z/, # 14:30:45 或 4:3:5
|
|
51
|
+
}.freeze
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module EasyTools
|
|
2
|
+
# EasyTools is a collection of utility methods and classes for Ruby on Rails applications.
|
|
3
|
+
class Core
|
|
4
|
+
class << self
|
|
5
|
+
# 列表转树结构
|
|
6
|
+
# @params data Hash 列表数据
|
|
7
|
+
# 数据结构
|
|
8
|
+
# [{
|
|
9
|
+
# id: 1, # 主键id
|
|
10
|
+
# parent_id: nil, # 父id
|
|
11
|
+
# order: 0, # 排序
|
|
12
|
+
# body: {title: '测试'}, # 主体内容
|
|
13
|
+
# },...]
|
|
14
|
+
# @return [Array] [{:title=>'测试', :children=>[]}]
|
|
15
|
+
#
|
|
16
|
+
def list_to_tree(data, parent_id = nil, sort_by: [:order])
|
|
17
|
+
nodes = data.select { |item| item[:parent_id] == parent_id }
|
|
18
|
+
# 多字段排序
|
|
19
|
+
nodes.sort_by! { |node| sort_by.map { |field| node[field] } }
|
|
20
|
+
|
|
21
|
+
nodes.map do |node|
|
|
22
|
+
{
|
|
23
|
+
**node[:body],
|
|
24
|
+
children: list_to_tree(data, node[:id], sort_by: sort_by),
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# 循环查询全部数据
|
|
30
|
+
# @params page 开始页数
|
|
31
|
+
# @params per_apge 每条分页数据
|
|
32
|
+
def find_all(page: 1, per_page: 1000, max_size: 10_000, no_safe: false)
|
|
33
|
+
data = []
|
|
34
|
+
loop do
|
|
35
|
+
current_data = yield(page, per_page)
|
|
36
|
+
data.concat(current_data)
|
|
37
|
+
# 数据已经取完了,则跳出
|
|
38
|
+
break if per_page > current_data.size
|
|
39
|
+
|
|
40
|
+
# 10w条数据需要提示中止
|
|
41
|
+
if data.size > max_size && !no_safe
|
|
42
|
+
raise EasyTools::Error, '循环获取的数量过大,如果需要获取更多的数据可以调整 max_size参数或开启no_safe: true'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
page += 1
|
|
46
|
+
end
|
|
47
|
+
data
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# 创建自动嵌套的 Hash
|
|
51
|
+
# @return Hash
|
|
52
|
+
def auto_hash
|
|
53
|
+
Hash.new { |h, k| h[k] = auto_hash }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
module EasyTools
|
|
3
|
+
# 时间处理类
|
|
4
|
+
class DateTime
|
|
5
|
+
extend EasyTools::DateTimeExt::Validator
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
# 安全转成时间格式
|
|
9
|
+
# @params input [Integer|String] 输入参数【时间戳|时间格式的字符串】
|
|
10
|
+
# @return DateTime 返回转换后的时间类型
|
|
11
|
+
def to_time(input)
|
|
12
|
+
case input
|
|
13
|
+
when Integer
|
|
14
|
+
# 处理毫秒级时间戳
|
|
15
|
+
input = input.to_s[0, 10].to_i
|
|
16
|
+
Time.at(input)
|
|
17
|
+
when String
|
|
18
|
+
raise EasyTools::Error, '请输入有效的日期时间格式' unless valid?(input)
|
|
19
|
+
|
|
20
|
+
Time.parse(normalize(input))
|
|
21
|
+
when DateTime, Date, Time
|
|
22
|
+
input
|
|
23
|
+
else
|
|
24
|
+
raise EasyTools::Error, '暂不支持此类型'
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# 转成时间格式字符串
|
|
29
|
+
#
|
|
30
|
+
# @param input [String, Numeric, Time] 时间输入,可以是日期字符串、时间戳或Time对象
|
|
31
|
+
# @param format [String] 格式化时间字符串,同strftime方法参数
|
|
32
|
+
# @return [String] 格式化后日期
|
|
33
|
+
def strftime(input, format = '%Y-%m-%d %H:%M:%S')
|
|
34
|
+
time = to_time(input)
|
|
35
|
+
time.strftime(format)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# 时间转换方法:将时间转换为相对时间描述(如:几分钟前)
|
|
39
|
+
#
|
|
40
|
+
# @param input [String, Numeric, Time] 时间输入,可以是日期字符串、时间戳或Time对象
|
|
41
|
+
# @return [String] 相对时间描述或格式化日期
|
|
42
|
+
def format_relative_time(input)
|
|
43
|
+
time = input.is_a?(Time) ? input : to_time(input)
|
|
44
|
+
# 计算时间差
|
|
45
|
+
time_difference = (Time.now - time).to_i
|
|
46
|
+
# 根据时间差返回相应的描述
|
|
47
|
+
format_time_description(time_difference)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# 格式化时间描述
|
|
51
|
+
# @param difference [Integer] 秒数的差值
|
|
52
|
+
# @return [String] 格式化时间描述
|
|
53
|
+
def format_time_description(difference)
|
|
54
|
+
case difference
|
|
55
|
+
when 0...Constant::SECONDS_IN_MINUTE
|
|
56
|
+
"#{difference}秒前"
|
|
57
|
+
when Constant::SECONDS_IN_MINUTE...Constant::SECONDS_IN_HOUR
|
|
58
|
+
"#{difference / Constant::SECONDS_IN_MINUTE}分钟前"
|
|
59
|
+
when Constant::SECONDS_IN_HOUR...Constant::SECONDS_IN_DAY
|
|
60
|
+
"#{difference / Constant::SECONDS_IN_HOUR}小时前"
|
|
61
|
+
when Constant::SECONDS_IN_DAY...Constant::SECONDS_IN_WEEK
|
|
62
|
+
"#{difference / Constant::SECONDS_IN_DAY}天前"
|
|
63
|
+
when Constant::SECONDS_IN_WEEK...Constant::SECONDS_IN_MONTH
|
|
64
|
+
"#{difference / Constant::SECONDS_IN_WEEK}周前" # 修复:除以周的秒数
|
|
65
|
+
when Constant::SECONDS_IN_MONTH...Constant::SECONDS_IN_YEAR
|
|
66
|
+
"#{difference / Constant::SECONDS_IN_MONTH}个月前"
|
|
67
|
+
else
|
|
68
|
+
"#{difference / Constant::SECONDS_IN_YEAR}年前"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
module EasyTools
|
|
4
|
+
module DateTimeExt
|
|
5
|
+
# DateTime 扩展模块,提供日期时间格式验证和转换功能
|
|
6
|
+
module Validator
|
|
7
|
+
# 验证日期时间字符串是否符合支持的格式
|
|
8
|
+
#
|
|
9
|
+
# @param datetime_string [String] 要验证的日期时间字符串
|
|
10
|
+
# @return [Boolean] 如果字符串符合任一支持的格式且日期时间组件有效,返回 true;否则返回 false
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# valid?("2023-12-25") #=> true
|
|
14
|
+
# valid?("2023.1.1") #=> true
|
|
15
|
+
# valid?("invalid") #=> false
|
|
16
|
+
def valid?(datetime_string)
|
|
17
|
+
return false unless datetime_string.is_a?(String)
|
|
18
|
+
|
|
19
|
+
# 检查是否符合任何一种格式
|
|
20
|
+
::EasyTools::Constant::DATETIME_FORMATS.any? do |format_name, regex|
|
|
21
|
+
datetime_string.match?(regex) && valid_datetime_components?(datetime_string, format_name)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# 验证日期时间字符串的各个组件是否有效
|
|
26
|
+
#
|
|
27
|
+
# @param datetime_string [String] 日期时间字符串
|
|
28
|
+
# @param format_name [Symbol] 格式名称
|
|
29
|
+
# @return [Boolean] 如果所有组件都有效返回 true;否则返回 false
|
|
30
|
+
#
|
|
31
|
+
# @example
|
|
32
|
+
# valid_datetime_components?("2023-13-01", :yyyy_mm_dd) #=> false (无效月份)
|
|
33
|
+
# valid_datetime_components?("2023.1.1", :yyyy_dot_mm_dd) #=> true
|
|
34
|
+
def valid_datetime_components?(datetime_string, format_name)
|
|
35
|
+
components = extract_components(datetime_string, format_name)
|
|
36
|
+
return false unless components
|
|
37
|
+
|
|
38
|
+
year, month, day, hour, minute, second = components
|
|
39
|
+
|
|
40
|
+
# 校验日期部分
|
|
41
|
+
if year && month && day && !valid_date?(year, month, day)
|
|
42
|
+
return false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# 校验时间部分
|
|
46
|
+
if (hour || minute || second) && !valid_time?(hour, minute, second)
|
|
47
|
+
return false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# 从日期时间字符串中提取各个组件
|
|
54
|
+
#
|
|
55
|
+
# @param datetime_string [String] 日期时间字符串
|
|
56
|
+
# @param format_name [Symbol] 格式名称
|
|
57
|
+
# @return [Array<Integer, Integer, Integer, Integer, Integer, Integer>]
|
|
58
|
+
# 返回 [年, 月, 日, 时, 分, 秒] 数组,不存在的组件为 nil
|
|
59
|
+
# @return [nil] 如果格式不匹配返回 nil
|
|
60
|
+
#
|
|
61
|
+
# @example
|
|
62
|
+
# extract_components("2023-12-25", :yyyy_mm_dd) #=> [2023, 12, 25, nil, nil, nil]
|
|
63
|
+
# extract_components("2023.1.5", :yyyy_dot_mm_dd) #=> [2023, 1, 5, nil, nil, nil]
|
|
64
|
+
def extract_components(datetime_string, format_name)
|
|
65
|
+
case format_name
|
|
66
|
+
when :yyyymmdd
|
|
67
|
+
[datetime_string[0..3].to_i, datetime_string[4..5].to_i, datetime_string[6..7].to_i, nil, nil, nil]
|
|
68
|
+
|
|
69
|
+
when :yyyy_mm_dd, :yyyy_slash_mm_dd, :yyyy_dot_mm_dd
|
|
70
|
+
separator = get_separator(datetime_string)
|
|
71
|
+
parts = datetime_string.split(separator)
|
|
72
|
+
year = parts[0].to_i
|
|
73
|
+
month = parts[1].to_i
|
|
74
|
+
day = parts[2].to_i
|
|
75
|
+
[year, month, day, nil, nil, nil]
|
|
76
|
+
|
|
77
|
+
when :yy_mm_dd, :yy_slash_mm_dd, :yy_dot_mm_dd
|
|
78
|
+
separator = get_separator(datetime_string)
|
|
79
|
+
parts = datetime_string.split(separator)
|
|
80
|
+
year_str = parts[0].to_i
|
|
81
|
+
month = parts[1].to_i
|
|
82
|
+
day = parts[2].to_i
|
|
83
|
+
year = convert_two_digit_year(year_str)
|
|
84
|
+
[year, month, day, nil, nil, nil]
|
|
85
|
+
|
|
86
|
+
when :chinese_date
|
|
87
|
+
match = datetime_string.match(/\A(\d{4})年(\d{1,2})月(\d{1,2})日\z/)
|
|
88
|
+
[match[1].to_i, match[2].to_i, match[3].to_i, nil, nil, nil] if match
|
|
89
|
+
|
|
90
|
+
when :chinese_short_date
|
|
91
|
+
match = datetime_string.match(/\A(\d{2})年(\d{1,2})月(\d{1,2})日\z/)
|
|
92
|
+
year = convert_two_digit_year(match[1].to_i) if match
|
|
93
|
+
[year, match[2].to_i, match[3].to_i, nil, nil, nil] if match
|
|
94
|
+
|
|
95
|
+
when :yyyymmdd_hhmmss
|
|
96
|
+
date_part, time_part = datetime_string.split(' ')
|
|
97
|
+
hour, minute, second = time_part.split(':').map(&:to_i)
|
|
98
|
+
[date_part[0..3].to_i, date_part[4..5].to_i, date_part[6..7].to_i, hour, minute, second]
|
|
99
|
+
|
|
100
|
+
when :yyyy_mm_dd_hhmmss, :yyyy_slash_mm_dd_hhmmss
|
|
101
|
+
date_part, time_part = datetime_string.split(' ')
|
|
102
|
+
separator = get_separator(date_part)
|
|
103
|
+
parts = date_part.split(separator)
|
|
104
|
+
year = parts[0].to_i
|
|
105
|
+
month = parts[1].to_i
|
|
106
|
+
day = parts[2].to_i
|
|
107
|
+
hour, minute, second = time_part.split(':').map(&:to_i)
|
|
108
|
+
[year, month, day, hour, minute, second]
|
|
109
|
+
|
|
110
|
+
when :iso_datetime
|
|
111
|
+
date_part, time_part = datetime_string.split('T')
|
|
112
|
+
year, month, day = date_part.split('-').map(&:to_i)
|
|
113
|
+
hour, minute, second = time_part.split(':').map(&:to_i)
|
|
114
|
+
[year, month, day, hour, minute, second]
|
|
115
|
+
|
|
116
|
+
when :chinese_datetime
|
|
117
|
+
match = datetime_string.match(/\A(\d{4})年(\d{1,2})月(\d{1,2})日\s(\d{1,2})时(\d{1,2})分(\d{1,2})秒\z/)
|
|
118
|
+
[match[1].to_i, match[2].to_i, match[3].to_i, match[4].to_i, match[5].to_i, match[6].to_i] if match
|
|
119
|
+
|
|
120
|
+
when :chinese_datetime_short
|
|
121
|
+
match = datetime_string.match(/\A(\d{2})年(\d{1,2})月(\d{1,2})日\s(\d{1,2})时(\d{1,2})分(\d{1,2})秒\z/)
|
|
122
|
+
year = convert_two_digit_year(match[1].to_i) if match
|
|
123
|
+
[year, match[2].to_i, match[3].to_i, match[4].to_i, match[5].to_i, match[6].to_i] if match
|
|
124
|
+
|
|
125
|
+
when :time_only
|
|
126
|
+
hour, minute, second = datetime_string.split(':').map(&:to_i)
|
|
127
|
+
[nil, nil, nil, hour, minute, second]
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# 获取字符串中的分隔符
|
|
132
|
+
#
|
|
133
|
+
# @param string [String] 包含分隔符的字符串
|
|
134
|
+
# @return [String] 分隔符字符('-', '/', '.' 或空字符串)
|
|
135
|
+
#
|
|
136
|
+
# @example
|
|
137
|
+
# get_separator("2023-12-25") #=> "-"
|
|
138
|
+
# get_separator("2023/12/25") #=> "/"
|
|
139
|
+
# get_separator("2023.1.1") #=> "."
|
|
140
|
+
def get_separator(string)
|
|
141
|
+
if string.include?('-')
|
|
142
|
+
'-'
|
|
143
|
+
elsif string.include?('/')
|
|
144
|
+
'/'
|
|
145
|
+
elsif string.include?('.')
|
|
146
|
+
'.'
|
|
147
|
+
else
|
|
148
|
+
''
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# 将两位年份转换为四位年份
|
|
153
|
+
#
|
|
154
|
+
# 转换规则:00-79 转换为 2000-2079,80-99 转换为 1980-1999
|
|
155
|
+
#
|
|
156
|
+
# @param year [Integer] 两位年份
|
|
157
|
+
# @return [Integer] 四位年份
|
|
158
|
+
#
|
|
159
|
+
# @example
|
|
160
|
+
# convert_two_digit_year(23) #=> 2023
|
|
161
|
+
# convert_two_digit_year(85) #=> 1985
|
|
162
|
+
def convert_two_digit_year(year)
|
|
163
|
+
year < 80 ? year + 2000 : year + 1900
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# 验证日期组件的有效性
|
|
167
|
+
#
|
|
168
|
+
# @param year [Integer] 年份
|
|
169
|
+
# @param month [Integer] 月份
|
|
170
|
+
# @param day [Integer] 日期
|
|
171
|
+
# @return [Boolean] 如果日期有效返回 true;否则返回 false
|
|
172
|
+
#
|
|
173
|
+
# @example
|
|
174
|
+
# valid_date?(2023, 2, 29) #=> false (2023年不是闰年)
|
|
175
|
+
# valid_date?(2024, 2, 29) #=> true (2024年是闰年)
|
|
176
|
+
# valid_date?(2023, 1, 1) #=> true
|
|
177
|
+
def valid_date?(year, month, day)
|
|
178
|
+
return false unless month.between?(1, 12)
|
|
179
|
+
return false unless day.between?(1, days_in_month(year, month))
|
|
180
|
+
|
|
181
|
+
true
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# 验证时间组件的有效性
|
|
185
|
+
#
|
|
186
|
+
# @param hour [Integer, nil] 小时
|
|
187
|
+
# @param minute [Integer, nil] 分钟
|
|
188
|
+
# @param second [Integer, nil] 秒
|
|
189
|
+
# @return [Boolean] 如果时间有效返回 true;否则返回 false
|
|
190
|
+
#
|
|
191
|
+
# @example
|
|
192
|
+
# valid_time?(25, 30, 45) #=> false (小时无效)
|
|
193
|
+
# valid_time?(14, 30, 45) #=> true
|
|
194
|
+
# valid_time?(4, 3, 5) #=> true
|
|
195
|
+
def valid_time?(hour, minute, second)
|
|
196
|
+
# 如果提供了时间部分,则验证所有时间组件
|
|
197
|
+
if hour || minute || second
|
|
198
|
+
if hour && !hour.between?(0, 23)
|
|
199
|
+
return false
|
|
200
|
+
end
|
|
201
|
+
if minute && !minute.between?(0, 59)
|
|
202
|
+
return false
|
|
203
|
+
end
|
|
204
|
+
if second && !second.between?(0, 59)
|
|
205
|
+
return false
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
true
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# 获取指定年份和月份的天数
|
|
212
|
+
#
|
|
213
|
+
# @param year [Integer] 年份
|
|
214
|
+
# @param month [Integer] 月份
|
|
215
|
+
# @return [Integer] 该月的天数
|
|
216
|
+
#
|
|
217
|
+
# @example
|
|
218
|
+
# days_in_month(2023, 2) #=> 28
|
|
219
|
+
# days_in_month(2024, 2) #=> 29
|
|
220
|
+
def days_in_month(year, month)
|
|
221
|
+
case month
|
|
222
|
+
when 2
|
|
223
|
+
leap_year?(year) ? 29 : 28
|
|
224
|
+
when 4, 6, 9, 11
|
|
225
|
+
30
|
|
226
|
+
else
|
|
227
|
+
31
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# 判断指定年份是否为闰年
|
|
232
|
+
#
|
|
233
|
+
# @param year [Integer] 年份
|
|
234
|
+
# @return [Boolean] 如果是闰年返回 true;否则返回 false
|
|
235
|
+
#
|
|
236
|
+
# @example
|
|
237
|
+
# leap_year?(2023) #=> false
|
|
238
|
+
# leap_year?(2024) #=> true
|
|
239
|
+
def leap_year?(year)
|
|
240
|
+
((year % 4).zero? && year % 100 != 0) || (year % 400).zero?
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# 将日期时间字符串标准化为指定格式
|
|
244
|
+
#
|
|
245
|
+
# @param datetime_string [String] 日期时间字符串
|
|
246
|
+
# @param format [Symbol] 输出格式,可选值::iso, :chinese, :date_only, :time_only
|
|
247
|
+
# @return [String, nil] 标准化后的字符串,如果输入无效返回 nil
|
|
248
|
+
#
|
|
249
|
+
# @example
|
|
250
|
+
# normalize("20231225", format: :iso) #=> "2023-12-25"
|
|
251
|
+
# normalize("2023.1.1", format: :iso) #=> "2023-01-01"
|
|
252
|
+
# normalize("2023-12-25", format: :chinese) #=> "2023年12月25日"
|
|
253
|
+
def normalize(datetime_string, format: :iso)
|
|
254
|
+
return nil unless valid?(datetime_string)
|
|
255
|
+
|
|
256
|
+
components = extract_components_from_string(datetime_string)
|
|
257
|
+
return nil unless components
|
|
258
|
+
|
|
259
|
+
year, month, day, hour, minute, second = components
|
|
260
|
+
|
|
261
|
+
case format
|
|
262
|
+
when :iso
|
|
263
|
+
format_iso(year, month, day, hour, minute, second)
|
|
264
|
+
when :chinese
|
|
265
|
+
format_chinese(year, month, day, hour, minute, second)
|
|
266
|
+
when :date_only
|
|
267
|
+
format_date_only(year, month, day)
|
|
268
|
+
when :time_only
|
|
269
|
+
format_time_only(hour, minute, second)
|
|
270
|
+
else
|
|
271
|
+
format_iso(year, month, day, hour, minute, second)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# 从字符串中提取日期时间组件
|
|
276
|
+
#
|
|
277
|
+
# @param datetime_string [String] 日期时间字符串
|
|
278
|
+
# @return [Array<Integer>, nil] 返回组件数组或 nil
|
|
279
|
+
#
|
|
280
|
+
# @private
|
|
281
|
+
def extract_components_from_string(datetime_string)
|
|
282
|
+
format_name = ::EasyTools::Constant::DATETIME_FORMATS.find do |name, regex|
|
|
283
|
+
datetime_string.match?(regex) && valid_datetime_components?(datetime_string, name)
|
|
284
|
+
end&.first
|
|
285
|
+
|
|
286
|
+
extract_components(datetime_string, format_name) if format_name
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# 格式化为 ISO 格式的日期时间字符串
|
|
290
|
+
#
|
|
291
|
+
# @param year [Integer, nil] 年份
|
|
292
|
+
# @param month [Integer, nil] 月份
|
|
293
|
+
# @param day [Integer, nil] 日期
|
|
294
|
+
# @param hour [Integer, nil] 小时
|
|
295
|
+
# @param minute [Integer, nil] 分钟
|
|
296
|
+
# @param second [Integer, nil] 秒
|
|
297
|
+
# @return [String, nil] 格式化后的字符串
|
|
298
|
+
#
|
|
299
|
+
# @private
|
|
300
|
+
def format_iso(year, month, day, hour, minute, second)
|
|
301
|
+
date_part = "#{year}-#{month.to_s.rjust(2, '0')}-#{day.to_s.rjust(2, '0')}" if year && month && day
|
|
302
|
+
time_part = "#{hour.to_s.rjust(2, '0')}:#{minute.to_s.rjust(2, '0')}:#{second.to_s.rjust(2, '0')}" if hour && minute && second
|
|
303
|
+
|
|
304
|
+
if date_part && time_part
|
|
305
|
+
"#{date_part} #{time_part}"
|
|
306
|
+
elsif date_part
|
|
307
|
+
date_part
|
|
308
|
+
elsif time_part
|
|
309
|
+
time_part
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
# 格式化为中文格式的日期时间字符串
|
|
314
|
+
#
|
|
315
|
+
# @param year [Integer, nil] 年份
|
|
316
|
+
# @param month [Integer, nil] 月份
|
|
317
|
+
# @param day [Integer, nil] 日期
|
|
318
|
+
# @param hour [Integer, nil] 小时
|
|
319
|
+
# @param minute [Integer, nil] 分钟
|
|
320
|
+
# @param second [Integer, nil] 秒
|
|
321
|
+
# @return [String, nil] 格式化后的字符串
|
|
322
|
+
#
|
|
323
|
+
# @private
|
|
324
|
+
def format_chinese(year, month, day, hour, minute, second)
|
|
325
|
+
date_part = "#{year}年#{month}月#{day}日" if year && month && day
|
|
326
|
+
time_part = "#{hour}时#{minute}分#{second}秒" if hour && minute && second
|
|
327
|
+
|
|
328
|
+
if date_part && time_part
|
|
329
|
+
"#{date_part}#{time_part}"
|
|
330
|
+
elsif date_part
|
|
331
|
+
date_part
|
|
332
|
+
elsif time_part
|
|
333
|
+
time_part
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# 格式化为仅日期字符串
|
|
338
|
+
#
|
|
339
|
+
# @param year [Integer] 年份
|
|
340
|
+
# @param month [Integer] 月份
|
|
341
|
+
# @param day [Integer] 日期
|
|
342
|
+
# @return [String] 格式化后的日期字符串
|
|
343
|
+
#
|
|
344
|
+
# @private
|
|
345
|
+
def format_date_only(year, month, day)
|
|
346
|
+
"#{year}-#{month.to_s.rjust(2, '0')}-#{day.to_s.rjust(2, '0')}" if year && month && day
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
# 格式化为仅时间字符串
|
|
350
|
+
#
|
|
351
|
+
# @param hour [Integer] 小时
|
|
352
|
+
# @param minute [Integer] 分钟
|
|
353
|
+
# @param second [Integer] 秒
|
|
354
|
+
# @return [String] 格式化后的时间字符串
|
|
355
|
+
#
|
|
356
|
+
# @private
|
|
357
|
+
def format_time_only(hour, minute, second)
|
|
358
|
+
"#{hour.to_s.rjust(2, '0')}:#{minute.to_s.rjust(2, '0')}:#{second.to_s.rjust(2, '0')}" if hour && minute && second
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'bigdecimal'
|
|
2
|
+
|
|
3
|
+
module EasyTools
|
|
4
|
+
# 数字方法
|
|
5
|
+
class Math
|
|
6
|
+
class << self
|
|
7
|
+
# @Notes: 精确除法并向上取整到指定小数位数
|
|
8
|
+
# @param dividend [Numeric, String] 被除数
|
|
9
|
+
# @param divisor [Numeric, String] 除数
|
|
10
|
+
# @param decimal_places [Integer] 保留的小数位数,默认为2
|
|
11
|
+
# @return [Float] 结果为向上取整后的浮点数
|
|
12
|
+
def precise_divide_and_ceil_percent(dividend, divisor, decimal_places = 2)
|
|
13
|
+
BigDecimal.mode(BigDecimal::ROUND_MODE, :up) # 设置向上取整模式
|
|
14
|
+
BigDecimal.limit(decimal_places + 4) # 设置足够精度
|
|
15
|
+
|
|
16
|
+
# 计算百分比(乘以100)
|
|
17
|
+
percent = (BigDecimal(dividend.to_s) / BigDecimal(divisor.to_s)) * 100
|
|
18
|
+
|
|
19
|
+
# 向上取整并格式化为指定小数位
|
|
20
|
+
("%.#{decimal_places}f" % percent.ceil(decimal_places)).to_f
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# @Notes: 精确除法并向上取整到指定小数位数
|
|
24
|
+
# @param dividend [Numeric, String] 被除数
|
|
25
|
+
# @param divisor [Numeric, String] 除数
|
|
26
|
+
# @param decimal_places [Integer] 保留的小数位数,默认为2
|
|
27
|
+
# @return [Float] 结果为向上取整后的浮点数
|
|
28
|
+
def precise_divide_and_round_percent(dividend, divisor, decimal_places = 2)
|
|
29
|
+
BigDecimal.mode(BigDecimal::ROUND_MODE, :half_up) # 设置向上取整模式
|
|
30
|
+
BigDecimal.limit(decimal_places + 4) # 设置足够精度
|
|
31
|
+
|
|
32
|
+
# 计算百分比(乘以100)
|
|
33
|
+
percent = (BigDecimal(dividend.to_s) / BigDecimal(divisor.to_s)) * 100
|
|
34
|
+
|
|
35
|
+
# 向上取整并格式化为指定小数位
|
|
36
|
+
("%.#{decimal_places}f" % percent.round(decimal_places)).to_f
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module EasyTools
|
|
2
|
+
# EasyTools is a collection of utility methods and classes for Ruby on Rails applications.
|
|
3
|
+
class Rails
|
|
4
|
+
include EasyTools::RailsExt::Hash
|
|
5
|
+
|
|
6
|
+
def initialize
|
|
7
|
+
unless Object.const_defined?('ActiveSupport::HashWithIndifferentAccess')
|
|
8
|
+
raise EasyTools::Error,
|
|
9
|
+
'ActiveSupport::HashWithIndifferentAccess is not available. Please ensure you have ActiveSupport loaded.'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# 不存在方法时调用
|
|
14
|
+
def self.method_missing(method_name, *args, **hash_data, &block)
|
|
15
|
+
this.send(method_name, *args, **hash_data, &block)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
def this
|
|
20
|
+
@this ||= new
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module EasyTools
|
|
2
|
+
module RailsExt
|
|
3
|
+
# 关于 Hash 的一些扩展方法
|
|
4
|
+
module Hash
|
|
5
|
+
# 对象转键值对
|
|
6
|
+
def to_kv(object, key, value = nil)
|
|
7
|
+
ActiveSupport::HashWithIndifferentAccess[object.map do |o|
|
|
8
|
+
if o.is_a?(Hash)
|
|
9
|
+
o = o.deep_symbolize_keys
|
|
10
|
+
[o[key.to_sym], value.nil? ? o : o[value.to_sym]]
|
|
11
|
+
else
|
|
12
|
+
[o.try(key.to_sym), value.nil? ? o : o.try(value.to_sym)]
|
|
13
|
+
end
|
|
14
|
+
end]
|
|
15
|
+
rescue StandardError
|
|
16
|
+
{}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/easy_tools.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'auto_load'
|
|
2
|
+
AutoLoad.define_autoloads('lib/easy_tools', EasyTools)
|
|
3
|
+
|
|
4
|
+
# 小小的帮助工具集合
|
|
5
|
+
# 积累了实际生产场景中遇到问题的解决方法
|
|
6
|
+
# 工具的目标为不一定通用,但一定好用
|
|
7
|
+
module EasyTools
|
|
8
|
+
class Error < StandardError; end
|
|
9
|
+
# EasyTools is a collection of utility methods and classes for Ruby on Rails applications.
|
|
10
|
+
class << self
|
|
11
|
+
# Returns the version of the currently loaded EasyTools as a string.
|
|
12
|
+
def version
|
|
13
|
+
EasyTools::VERSION
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Provides a way to configure EasyTools.
|
|
17
|
+
#
|
|
18
|
+
# @yield [config] The configuration block.
|
|
19
|
+
# @yieldparam config [Configuration] The configuration object.
|
|
20
|
+
#
|
|
21
|
+
# @return [Configuration] The current configuration object.
|
|
22
|
+
def configure
|
|
23
|
+
yield(configuration)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns the current configuration object.
|
|
27
|
+
def configuration
|
|
28
|
+
@configuration ||= Configuration.new
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# 加载Rails相关的扩展
|
|
32
|
+
def rails
|
|
33
|
+
EasyTools::Rails.this
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# 配置相关
|
|
38
|
+
class Configuration
|
|
39
|
+
def initialize
|
|
40
|
+
@logger = Logger.new($stdout)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: easy_tools
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- andy.you
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-09-11 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.17'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.17'
|
|
27
|
+
description: EasyTools provides a set of helper methods and classes to simplify common
|
|
28
|
+
tasks in Ruby on Rails applications, enhancing productivity and code maintainability.
|
|
29
|
+
email:
|
|
30
|
+
- andy.you@rccchina.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".rspec"
|
|
36
|
+
- ".travis.yml"
|
|
37
|
+
- ".vscode/extensions.json"
|
|
38
|
+
- CODE_OF_CONDUCT.md
|
|
39
|
+
- LICENSE.txt
|
|
40
|
+
- README.md
|
|
41
|
+
- Rakefile
|
|
42
|
+
- easy_tools.gemspec
|
|
43
|
+
- lib/auto_load.rb
|
|
44
|
+
- lib/easy_tools.rb
|
|
45
|
+
- lib/easy_tools/batch_manager.rb
|
|
46
|
+
- lib/easy_tools/constant.rb
|
|
47
|
+
- lib/easy_tools/core.rb
|
|
48
|
+
- lib/easy_tools/date_time.rb
|
|
49
|
+
- lib/easy_tools/date_time_ext/validator.rb
|
|
50
|
+
- lib/easy_tools/math.rb
|
|
51
|
+
- lib/easy_tools/rails.rb
|
|
52
|
+
- lib/easy_tools/rails_ext/hash.rb
|
|
53
|
+
- lib/easy_tools/version.rb
|
|
54
|
+
homepage: https://git.rccchina.com/leads-in/omnicrm
|
|
55
|
+
licenses:
|
|
56
|
+
- MIT
|
|
57
|
+
metadata:
|
|
58
|
+
allowed_push_host: https://rubygems.org
|
|
59
|
+
homepage_uri: https://git.rccchina.com/leads-in/omnicrm
|
|
60
|
+
source_code_uri: https://git.rccchina.com/leads-in/omnicrm
|
|
61
|
+
changelog_uri: https://git.rccchina.com/leads-in/omnicrm
|
|
62
|
+
post_install_message:
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 3.0.3
|
|
78
|
+
signing_key:
|
|
79
|
+
specification_version: 4
|
|
80
|
+
summary: EasyTools is a collection of utility methods and classes for Ruby on Rails
|
|
81
|
+
applications.
|
|
82
|
+
test_files: []
|