activemodel_object_info 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4fbb93d73230b877ddef2d140e3735c97b4bbf10f03273401f4be77f5540dbdd
4
+ data.tar.gz: a758c1d6b7623810b01f0379d700e4e70d7ecc8d0c013c86592be06cae68b1eb
5
+ SHA512:
6
+ metadata.gz: ea99c0ec6222036125acf0143a51832c70d4146f4b9b7f5e70277405d92f5819fc1d9d389ee9063d1f6b7998d06eebe873a9f465783873f7b50168dfbea6e960
7
+ data.tar.gz: 8fc1a83c8c641f97de5b188dbeddeb89f7be52524074d95a515546606bff1a08ab38e244296249aebd3d8bca9f4311ad5108af788c97710030ff04e9cfa1a4ea
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.rubocop.yml ADDED
@@ -0,0 +1,76 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'bin/**/*'
4
+ - 'examples/**/*'
5
+ NewCops: enable
6
+
7
+ # 弱ruby版本的gem,所有目前不需要检测ruby版本
8
+ Gemspec/RequiredRubyVersion:
9
+ Enabled: false
10
+
11
+ Layout/EndOfLine:
12
+ Enabled: false
13
+ Layout/LineLength:
14
+ Max: 400
15
+
16
+ Lint/RaiseException:
17
+ Enabled: true
18
+ Lint/StructNewOverride:
19
+ Enabled: true
20
+
21
+ Metrics/AbcSize:
22
+ Enabled: true
23
+ Max: 200
24
+ # 块的最大长度,因为API文件中会通过块包含通用路由,所以这里放大一些。
25
+ Metrics/BlockLength:
26
+ Max: 100
27
+ Exclude:
28
+ - 'spec/**/*_spec.rb'
29
+ Metrics/BlockNesting:
30
+ Max: 5
31
+ Metrics/ClassLength:
32
+ Max: 600
33
+ Metrics/MethodLength:
34
+ Enabled: true
35
+ Max: 100
36
+ # 保持和 Class 的长度一致。对于一些纯设定类仅300行并不够,600行以上需要拆分。
37
+ Metrics/ModuleLength:
38
+ Max: 600
39
+ Metrics/PerceivedComplexity:
40
+ Max: 50
41
+ Metrics/CyclomaticComplexity:
42
+ Max: 50
43
+
44
+ Naming/PredicateName:
45
+ Exclude:
46
+ - 'spec/**/*'
47
+
48
+ Style/AsciiComments:
49
+ Enabled: false
50
+ Style/Encoding:
51
+ Enabled: false
52
+ Style/FormatString:
53
+ Enabled: false
54
+ Style/FormatStringToken:
55
+ Enabled: false
56
+ Style/HashEachMethods:
57
+ Enabled: false
58
+ Style/HashTransformKeys:
59
+ Enabled: false
60
+ Style/HashTransformValues:
61
+ Enabled: false
62
+ Style/Next:
63
+ MinBodyLength: 4
64
+ Style/IfInsideElse:
65
+ Enabled: false
66
+ Style/NumericLiterals:
67
+ Enabled: false
68
+ Style/RaiseArgs:
69
+ Enabled: false
70
+ Style/RescueModifier:
71
+ Enabled: false
72
+ Style/TrailingCommaInHashLiteral:
73
+ Enabled: true
74
+ EnforcedStyleForMultiline: consistent_comma
75
+ Style/TrailingCommaInArrayLiteral:
76
+ EnforcedStyleForMultiline: consistent_comma
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ activerecord-object-info
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.7
data/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ First Version
@@ -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 sherlock.ma@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/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in activemodel-object-info.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,64 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ activemodel_object_info (0.1.6)
5
+ activerecord
6
+ activesupport
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activemodel (5.2.4.1)
12
+ activesupport (= 5.2.4.1)
13
+ activerecord (5.2.4.1)
14
+ activemodel (= 5.2.4.1)
15
+ activesupport (= 5.2.4.1)
16
+ arel (>= 9.0)
17
+ activesupport (5.2.4.1)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
20
+ minitest (~> 5.1)
21
+ tzinfo (~> 1.1)
22
+ arel (9.0.0)
23
+ ast (2.4.1)
24
+ concurrent-ruby (1.1.5)
25
+ i18n (1.8.2)
26
+ concurrent-ruby (~> 1.0)
27
+ minitest (5.14.0)
28
+ parallel (1.20.1)
29
+ parser (3.0.0.0)
30
+ ast (~> 2.4.1)
31
+ rainbow (3.0.0)
32
+ rake (10.5.0)
33
+ regexp_parser (2.0.3)
34
+ rexml (3.2.4)
35
+ rubocop (1.7.0)
36
+ parallel (~> 1.10)
37
+ parser (>= 2.7.1.5)
38
+ rainbow (>= 2.2.2, < 4.0)
39
+ regexp_parser (>= 1.8, < 3.0)
40
+ rexml
41
+ rubocop-ast (>= 1.2.0, < 2.0)
42
+ ruby-progressbar (~> 1.7)
43
+ unicode-display_width (>= 1.4.0, < 2.0)
44
+ rubocop-ast (1.4.0)
45
+ parser (>= 2.7.1.5)
46
+ ruby-progressbar (1.11.0)
47
+ thread_safe (0.3.6)
48
+ tzinfo (1.2.6)
49
+ thread_safe (~> 0.1)
50
+ unicode-display_width (1.7.0)
51
+ yard (0.9.20)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ activemodel_object_info!
58
+ bundler
59
+ rake (~> 10.0)
60
+ rubocop
61
+ yard
62
+
63
+ BUNDLED WITH
64
+ 1.17.3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Shiner
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Activemodel::Object::Info
2
+
3
+ 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/activemodel/object/info`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'activemodel-object-info'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install activemodel-object-info
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ 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).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/activemodel-object-info. 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.
36
+
37
+ ## License
38
+
39
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
40
+
41
+ ## Code of Conduct
42
+
43
+ Everyone interacting in the Activemodel::Object::Info project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/activemodel-object-info/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ task default: :spec
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'activemodel_object_info/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'activemodel_object_info'
9
+ spec.version = ActivemodelObjectInfo::Version::VERSION
10
+ spec.authors = ['shiner']
11
+ spec.email = ['shiner527@hotmail.com']
12
+
13
+ spec.summary = 'Build a hash based on active record attributes.'
14
+ spec.description = 'Build a hash based on active record attributes.'
15
+ spec.homepage = 'https://github.com/shiner527/activemodel-object-info'
16
+ spec.license = 'MIT'
17
+
18
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
19
+
20
+ spec.metadata['homepage_uri'] = spec.homepage
21
+ spec.metadata['source_code_uri'] = 'https://github.com/shiner527/activemodel-object-info.git'
22
+ spec.metadata['changelog_uri'] = 'https://github.com/shiner527/activemodel-object-info/CHANGELOG'
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
28
+ end
29
+ spec.bindir = 'bin'
30
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_dependency 'activerecord'
34
+ spec.add_dependency 'activesupport'
35
+
36
+ spec.add_development_dependency 'bundler'
37
+ spec.add_development_dependency 'rake', '~> 10.0'
38
+ spec.add_development_dependency 'rubocop'
39
+ spec.add_development_dependency 'yard'
40
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "activemodel/object/info"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'activemodel_object_info/extend'
4
+ require 'activemodel_object_info/version'
5
+ require 'activemodel_object_info/base'
6
+ require 'activemodel_object_info/table_definition'
7
+ require 'activemodel_object_info/deleted_operation'
8
+
9
+ #
10
+ # 扩展模组。
11
+ #
12
+ # @author shiner527 <shiner527@hotmail.com>
13
+ #
14
+ module ActivemodelObjectInfo
15
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActivemodelObjectInfo
4
+ #
5
+ # 通用的设置操作项相关的模型方法和处理。
6
+ #
7
+ # @author shiner527 <shiner527@hotmail.com>
8
+ #
9
+ module Base
10
+ #
11
+ # 对象信息输出。主要返回给前端一个可用的散列(会被转化为JSON格式)格式的信息并传递给前端。
12
+ #
13
+ # @param [Hash] options 设置选项
14
+ # @option options [Array<Symbol, Hash>] :attributes 具体每一项输出的设置数组。每个元素既可以是标识符实例也可以是一个散列实例。
15
+ # 如果是标识符实例,则表示输出该属性。如果是一个散列实例,则按照散列中的设定值去生成对应的内容。
16
+ # @option options [Array<Symbol>] :only 给出具体可以用来输出的字段属性名数组。
17
+ #
18
+ # @return [Hash] 返回的处理过的该对象的信息散列。
19
+ #
20
+ def instance_info(**options)
21
+ # puts "ARGS: #{args}, OPTIONS: #{options}"
22
+ # 参考默认的 options
23
+ options = "#{self.class}::INSTANCE_INFO".safe_constantize || {} if options.blank?
24
+ result = {}
25
+ # 仅包含的字段
26
+ only_attributes = (options[:only] || []).map(&:to_sym)
27
+ # 要排除的字段
28
+ # 默认不包含 :deleted, :deleted_by 和 :deleted_at 三个字段
29
+ default_deleted_column = ::Constants::Base::TABLE_COLUMN_DELETE_COLUMN rescue 'deleted'
30
+ default_except_attrs = [
31
+ default_deleted_column,
32
+ "#{default_deleted_column}_by",
33
+ "#{default_deleted_column}_at",
34
+ ]
35
+ except_attributes = (options[:except] || default_except_attrs).map(&:to_sym)
36
+ # 字段的具体属性设置
37
+ attribute_configs = options[:attributes] || []
38
+ # 计算要输出的参数名称
39
+ output_attributes = attribute_names.map(&:to_sym)
40
+ output_attributes &= only_attributes if only_attributes.present?
41
+ output_attributes -= except_attributes
42
+
43
+ # puts "Excepts: #{except_attributes}, Only: #{only_attributes}, Output: #{output_attributes}"
44
+ # 这里保证了要引入的类中含有 attributes 方法,且为 Hash 类型
45
+ attribute_configs.each do |attr_config|
46
+ # 如果设置项单纯是字符串或者标识符的情况
47
+ if [::String, ::Symbol].any? { |attr_class| attr_config.is_a?(attr_class) }
48
+ attribute_name = attr_config.to_sym
49
+ current_attr_config = {}
50
+ elsif attr_config.is_a?(::Hash) # 如果设置项为一个散列
51
+ current_attr_config = attr_config.deep_symbolize_keys
52
+ attribute_name = current_attr_config[:name]
53
+ else # 如果不是指定的类型,则跳过进行下一项
54
+ next
55
+ end
56
+ # 输出的参数名称,如果用户给出了 as 指定,优先用用户给定的,否则默认用属性名
57
+ raw_name = current_attr_config[:as].present? ? current_attr_config[:as].to_sym : attribute_name
58
+ # 过滤器输出字段,如果给定了一个块,则使用用户指定的,否则默认为没有过滤器
59
+ filter = current_attr_config[:filter]
60
+
61
+ # 获取真正对应的属性的名称和其值
62
+ k = raw_name
63
+ # 当类型为抽象类时,是不会有对应的方法和属性的,因此直接设置为 nil 并期待之后的 filter 等规则来生成实际值。
64
+ v = current_attr_config[:type] == :abstract ? nil : __send__(k)
65
+
66
+ # puts "instance attributes #{k}: #{v}(#{v.class.name})"
67
+ # 如果不是要输出的字段则跳过,或者如果不是给出type设置项为 :method 或者 :abstract 也跳过。
68
+ next unless output_attributes.include?(k) || %i[abstract method].include?(current_attr_config[:type])
69
+
70
+ # 赋值。如果有过滤器,优先使用过滤器
71
+ if filter.present?
72
+ result[attribute_name] = case filter
73
+ when ::Proc
74
+ instance_exec(v, &filter)
75
+ when ::Symbol
76
+ __send__(filter)
77
+ else
78
+ filter
79
+ end
80
+ # 如果值是时间类别的字段,默认转换为时间日期格式的字符串
81
+ elsif [::Date, ::Time, ::DateTime].any? { |time_class| v.is_a?(time_class) }
82
+ # 时间日期格式设定,优先使用当前字段自定义,否则使用通用自定义,最后使用默认格式
83
+ attribute_format = current_attr_config[:format].present? ? current_attr_config[:format] : options[:datetime_format]
84
+ result[attribute_name] = if attribute_format.to_s == 'standard' # 会被转换为标准时间日期格式
85
+ v
86
+ elsif attribute_format.present? # 按照设定的 format_date 方法的格式
87
+ v.format_date(attribute_format.to_sym)
88
+ else # 默认使用 'yyyy-MM-dd hh:mm:ss' 的格式
89
+ v.format_date(:full)
90
+ end
91
+ else # 其他值类型默认赋值自己
92
+ result[attribute_name] = v
93
+ end
94
+ end
95
+
96
+ # 过滤第一层键名为符号并返回
97
+ result.symbolize_keys!
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActivemodelObjectInfo
4
+ #
5
+ # 用来进行和删除操作相关的数据库模型的功能模组。主要结合了使用 +:deleted+ 字段标识数据软删除的功能。
6
+ #
7
+ # @author shiner527 <shiner527@hotmail.com>
8
+ #
9
+ module DeletedOperation
10
+ extend ::ActiveSupport::Concern
11
+
12
+ # 当被 include 关键字引入后的处理
13
+ included do |_|
14
+ # 定义好默认的删除查询语句
15
+ deleted_field = ::Constants::Base::TABLE_COLUMN_DELETE_COLUMN
16
+ deleted_value_valid = ::Constants::Base::TABLE_COLUMN_DELETE_VALID
17
+ default_scope { where(deleted_field.to_sym => deleted_value_valid) }
18
+
19
+ # 通用的删除过程
20
+ define_method(:delete_block) do |**options|
21
+ # 处理可选项参数
22
+ options[:refresh_updated] = options[:refresh_updated].nil? ? false : options[:refresh_updated]
23
+
24
+ # 必须要传递操作的用户ID信息
25
+ raise ArgumentError, 'Must give user id!' if options[:user_id].blank?
26
+
27
+ # 获取更新人字段,删除人字段
28
+ updated_by_field = (options[:updated_by_field] || "#{::Constants::Base::TABLE_COLUMN_UPDATED_COLUMN}_by").to_sym
29
+ deleted_by_field = (options[:deleted_by_field] || "#{::Constants::Base::TABLE_COLUMN_DELETE_COLUMN}_by").to_sym
30
+ deleted_at_field = (options[:deleted_at_field] || "#{::Constants::Base::TABLE_COLUMN_DELETE_COLUMN}_at").to_sym
31
+ # 设置删除状态为已失效
32
+ __send__("#{deleted_field}=", ::Constants::Base::TABLE_COLUMN_DELETE_INVALID)
33
+ # 设置删除人、删除时间和更新人
34
+ __send__("#{deleted_by_field}=", options[:user_id]) if respond_to?(deleted_by_field)
35
+ __send__("#{deleted_at_field}=", Time.now.localtime) if respond_to?(deleted_at_field)
36
+ __send__("#{updated_by_field}=", options[:user_id]) if respond_to?(updated_by_field)
37
+ end
38
+
39
+ # 定义删除方法,但是不覆盖 ActiveRecord 的同名方法。
40
+ define_method(:soft_delete) do |**options|
41
+ opts = options.deep_symbolize_keys
42
+ delete_block(**opts)
43
+ # 根据设置决定是否刷更新时间
44
+ save(touch: opts[:refresh_updated])
45
+ end
46
+
47
+ # 定义删除方法,区别为最终调用 save! 方法更新数据,所以遇到异常会抛出异常
48
+ define_method(:soft_delete!) do |**options|
49
+ opts = options.deep_symbolize_keys
50
+ delete_block(**opts)
51
+ # 根据设置决定是否刷更新时间
52
+ save!(touch: opts[:refresh_updated])
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # 加载扩展
4
+ ::Dir.glob('extends/**/*.rb').sort.each do |filename|
5
+ require_relative filename
6
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # 时间类型相关类通用实例方法定义模块。
5
+ #
6
+ # @author shiner527 <shiner527@hotmail.com>
7
+ #
8
+ module TimeInstance
9
+ #
10
+ # 时间格式化为指定的字符串。
11
+ #
12
+ # @param [Symbol] type 指定需要转换的具体格式。默认为 +:date+ 类型的格式。
13
+ #
14
+ # @return [String] 返回转换后的字符串。
15
+ #
16
+ def format_date(type = :date)
17
+ case type
18
+ when :full
19
+ strftime('%Y-%m-%d %H:%M:%S')
20
+ when :min
21
+ strftime('%Y-%m-%d %H:%M')
22
+ when :date
23
+ strftime('%Y-%m-%d')
24
+ when :year
25
+ strftime('%Y')
26
+ end
27
+ end
28
+ end
29
+
30
+ #
31
+ # 扩展 Date 类
32
+ #
33
+ class Date
34
+ include ::TimeInstance
35
+ end
36
+
37
+ #
38
+ # 扩展 Time 类
39
+ #
40
+ class Time
41
+ include ::TimeInstance
42
+ end
43
+
44
+ #
45
+ # 扩展 DateTime 类
46
+ #
47
+ class DateTime
48
+ include ::TimeInstance
49
+ end
50
+
51
+ #
52
+ # 扩展 Array 类
53
+ #
54
+ class Array
55
+ #
56
+ # 找到数组中为空的元素,并将去掉空元素的结果返回为一个新数组。空的定义由 +blank?+ 方法确定。本方法<b>不会改变</b>数组自身。
57
+ #
58
+ # @return [Array] 返回不包含空元素的数组作为结果。
59
+ #
60
+ def compact_blank
61
+ result = []
62
+ each { |item| result << item unless item.blank? }
63
+ result
64
+ end
65
+
66
+ #
67
+ # 去掉空元素并将去掉后的数组作为结果返回。空的定义由 +blank?+ 方法确定。本方法<b>会改变</b>数组自身。
68
+ #
69
+ # @return [Array] 返回不包含空元素的数组作为结果。
70
+ #
71
+ def compact_blank!
72
+ delete_if(&:blank?)
73
+ end
74
+ end
75
+
76
+ #
77
+ # 扩展 Array 类
78
+ #
79
+ class Hash
80
+ #
81
+ # 找到散列中键值对的值为空的元素,并将去掉这样的键值对的结果返回为一个新散列。空的定义由 +blank?+ 方法确定。本方法<b>不会改变</b>散列自身。
82
+ #
83
+ # @return [Array] 返回不包含键值对中值为空元素的散列作为结果。
84
+ #
85
+ def compact_blank
86
+ result = {}
87
+ each { |k, v| result[k] = v unless v.blank? }
88
+ result
89
+ end
90
+
91
+ #
92
+ # 去掉散列中键值对的值为空的键值对,并将去掉后的散列作为结果返回。空的定义由 +blank?+ 方法确定。本方法<b>会改变</b>散列自身。
93
+ #
94
+ # @return [Array] 返回不包含键值对中值为空元素的散列作为结果。
95
+ #
96
+ def compact_blank!
97
+ delete_if { |_k, v| v.blank? }
98
+ end
99
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActivemodelObjectInfo
4
+ #
5
+ # 重新扩展一些方法,用来方便在创建迁移文件的时候进行一些通用类的设定。
6
+ #
7
+ # @author shiner527 <shiner527@hotmail.com>
8
+ #
9
+ module TableDefinition
10
+ #
11
+ # 生成操作相关的字段。包含操作人、操作时间等信息。
12
+ #
13
+ # @param [Symbol, String] fields 要生成的操作字段的名称。可以是字符串也可以是符号格式。相当于实际的前缀,会根据后续设置选项决定生成的字段内容。
14
+ # @param [Hash] options 具体的设置选项,详见选项介绍。
15
+ #
16
+ # @option options [String] :operator_prefix 操作人字段前缀,默认为 +nil+ 。
17
+ # @option options [String] :operator_suffix 操作人字段后缀,默认为 +'_by'+ 。
18
+ # @option options [Boolean] :with_operator 是否生成用来记录对应操作的操作人信息,默认为 +true+ 。
19
+ # 如果允许生成的话,会生成 <em><prefix></em><em><operation></em><em><suffix></em> 字段,默认为 <b>bigint</b> 类型。
20
+ #
21
+ # * <em><prefix></em>: 前缀,由可选参数 <b>:operator_prefix</b> 提供,如果没有的话则使用默认值。
22
+ # * <em><operation></em>: 字段本体名称,即参数 <b>fields</b> 中给出的具体每个字段的名称本体。
23
+ # * <em><suffix></em>: 后缀,由可选参数 <b>:operator_suffix</b> 提供,如果没有的话则使用默认值。
24
+ #
25
+ # @option options [String] :timestamp_prefix 操作时间字段前缀,默认为 +nil+ 。
26
+ # @option options [String] :timestamp_suffix 操作时间字段后缀,默认为 +'_at'+ 。
27
+ # @option options [Boolean] :with_timestamp 是否生成用来记录对应操作的时间戳信息,默认为 +true+ 。
28
+ # 如果允许生成的话,会生成 <em><prefix></em><em><operation></em><em><suffix></em> 字段,默认为 <b>datetime</b> 类型。
29
+ #
30
+ # * <em><prefix></em>: 前缀,由可选参数 <b>:timestamp_prefix</b> 提供,如果没有的话则使用默认值。
31
+ # * <em><operation></em>: 字段本体名称,即参数 <b>fields</b> 中给出的具体每个字段的名称本体。
32
+ # * <em><suffix></em>: 后缀,由可选参数 <b>:timestamp_suffix</b> 提供,如果没有的话则使用默认值。
33
+ #
34
+ # @example
35
+ # operation_columns(:create, with_operator: true, operator_prefix: 'my_', operator_suffix: '_user')
36
+ # # => 会生成名为 'my_create_user' 的字段用来记录创建人信息。
37
+ #
38
+ def operation_columns(*fields, **options)
39
+ # puts "operation_columns(#{fields}, #{options})"
40
+ # 初始化设置选项
41
+ with_operator = options[:with_operator].nil? ? true : options[:with_operator]
42
+ with_timestamp = options[:with_timestamp].nil? ? true : options[:with_timestamp]
43
+ operator_prefix = options[:operator_prefix]
44
+ operator_suffix = options[:operator_suffix] || '_by'
45
+ timestamp_prefix = options[:timestamp_prefix]
46
+ timestamp_suffix = options[:timestamp_suffix] || '_at'
47
+ # 依照每个字段进行设置
48
+ fields.each do |field|
49
+ # 如果要生成操作人信息
50
+ if with_operator
51
+ operator_column_name = operator_prefix.to_s + field.to_s + operator_suffix.to_s
52
+ column(operator_column_name, :bigint, index: true, comment: '操作人') if operator_column_name.present?
53
+ end
54
+ # 如果要生成操作时间戳信息
55
+ if with_timestamp
56
+ timestamp_column_name = timestamp_prefix.to_s + field.to_s + timestamp_suffix.to_s
57
+ column(timestamp_column_name, :datetime, comment: '操作时间戳') if timestamp_column_name.present?
58
+ end
59
+ end
60
+ end
61
+
62
+ #
63
+ # 生成对应操作的字段,包括操作人和操作时间戳,均使用默认设置。
64
+ #
65
+ # @param [Symbol, String] operations 对应的操作。默认会给出 +:created+ 、 +:updated+ 和 +:deleted+ 三个操作名。
66
+ # 如果参数中的给出操作名含有 +:deleted+ 或者 +'deleted'+ 时,还会生成默认名称为 <b>deleted</b> 整形数字段,用来标识当前记录是否被删除。
67
+ # 这里该字段的名称由常量 {Constants::Base::TABLE_COLUMN_DELETE_COLUMN} 设定。
68
+ # 默认值为可用的记录标记值。由常量 {Constants::Base::TABLE_COLUMN_DELETE_DEFAULT} 设定。
69
+ #
70
+ def generate_operations(*operations)
71
+ # puts "generate_operations(#{operations})"
72
+ action_fields = operations
73
+ # 默认只有创建、更新和删除相关的字段
74
+ if action_fields.empty?
75
+ action_fields = [
76
+ ::Constants::Base::TABLE_COLUMN_CREATED_COLUMN,
77
+ ::Constants::Base::TABLE_COLUMN_UPDATED_COLUMN,
78
+ ::Constants::Base::TABLE_COLUMN_DELETE_COLUMN,
79
+ ]
80
+ end
81
+ action_fields.each do |operation|
82
+ column(::Constants::Base::TABLE_COLUMN_DELETE_COLUMN, :integer, default: ::Constants::Base::TABLE_COLUMN_DELETE_DEFAULT, comment: '删除标记') if operation.to_sym == :deleted
83
+ operation_columns(operation)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActivemodelObjectInfo
4
+ module Version
5
+ VERSION = '0.1.6'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activemodel_object_info
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.6
5
+ platform: ruby
6
+ authors:
7
+ - shiner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Build a hash based on active record attributes.
98
+ email:
99
+ - shiner527@hotmail.com
100
+ executables:
101
+ - console
102
+ - setup
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".rubocop.yml"
108
+ - ".ruby-gemset"
109
+ - ".ruby-version"
110
+ - CHANGELOG
111
+ - CODE_OF_CONDUCT.md
112
+ - Gemfile
113
+ - Gemfile.lock
114
+ - LICENSE
115
+ - README.md
116
+ - Rakefile
117
+ - activemodel_object_info.gemspec
118
+ - bin/console
119
+ - bin/setup
120
+ - lib/activemodel_object_info.rb
121
+ - lib/activemodel_object_info/base.rb
122
+ - lib/activemodel_object_info/deleted_operation.rb
123
+ - lib/activemodel_object_info/extend.rb
124
+ - lib/activemodel_object_info/extends/ruby_generals.rb
125
+ - lib/activemodel_object_info/table_definition.rb
126
+ - lib/activemodel_object_info/version.rb
127
+ homepage: https://github.com/shiner527/activemodel-object-info
128
+ licenses:
129
+ - MIT
130
+ metadata:
131
+ homepage_uri: https://github.com/shiner527/activemodel-object-info
132
+ source_code_uri: https://github.com/shiner527/activemodel-object-info.git
133
+ changelog_uri: https://github.com/shiner527/activemodel-object-info/CHANGELOG
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubygems_version: 3.0.9
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Build a hash based on active record attributes.
153
+ test_files: []