activemodel_object_info 0.1.6

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 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: []