activemodel_object_info 0.3.3 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57c5e5b167d779ecddc9ef0880eae2eb80a1e5a5cfea65a041900952d105e746
4
- data.tar.gz: 305b684a599a961fd454c06a9fb234abab64def44b629fc85ea53413973f9c93
3
+ metadata.gz: 80913d0545f39b6b56ea20826e6c5da37c3fbec7645f988c5439f85f6ac2eedc
4
+ data.tar.gz: 8f839ed3448156219e6c4da35c9eb69d9eb7d4dca97ef48f1c89b937ea2a782e
5
5
  SHA512:
6
- metadata.gz: a787ba950b878f3fc7e40955a4cc5cf50a55ad0e8c783891c74e3c0fcee8d2f9f2630bec312717bb52c13ee52c63b41ea2f61efd19da03396ace20ea7f859e6a
7
- data.tar.gz: 2fafd8e49b9ad0e1b1a4cef8ef813f8ea0a57b1e60f23588d617bada644eb50302af6fa395ebd041aac86b2b97c8de53ae8a4e206259495ada4dc13c9185652c
6
+ metadata.gz: a8c1005c3e1887dd377deb8ab79d1d9bad079dea09ac81e24ff484e59efdb44c3e3c6c7ba4741237954d944f0b5780ff00f9e7530ebe465c603eedc9f0aeb5a1
7
+ data.tar.gz: 5bd16bc43f2c13b58bfa037ee6505777d80a97d455b7744d75f32cd928348a1d2d0e0cc638acc0070662b42792f83972c365ccd00c81860980f05196afc6bd32
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.7
1
+ 2.7.7
data/CHANGELOG CHANGED
@@ -1 +1,9 @@
1
- First Version
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ ## [0.4.0] - 2026-07-28
5
+ ### Fixed
6
+ - Fixed Ruby 3.x keyword arguments compatibility issue for `instance_info` method, while keeping 100% backward compatibility for existing usages.
7
+
8
+ ## [0.3.4] - 2021-04-19
9
+ - First Version
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activemodel_object_info (0.2.0)
4
+ activemodel_object_info (0.4.0)
5
5
  activesupport
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,43 +1,173 @@
1
1
  # Activemodel::Object::Info
2
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.
3
+ [English](README.md) | [中文文档](README.zh-CN.md)
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ **Activemodel::Object::Info** is a Ruby Gem designed to extend `ActiveModel` and `ActiveRecord`. It provides an elegant and standardized way to handle common business requirements such as API data formatting, audit trails generation in database migrations, and soft-delete capabilities.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Features](#features)
10
+ - [Applicable Scenarios](#applicable-scenarios)
11
+ - [Installation](#installation)
12
+ - [Usage](#usage)
13
+ - [1. Data Formatting (Base)](#1-data-formatting-base)
14
+ - [2. Migration Macros (TableDefinition)](#2-migration-macros-tabledefinition)
15
+ - [3. Soft Delete (DeletedOperation)](#3-soft-delete-deletedoperation)
16
+ - [Development & Testing](#development--testing)
17
+ - [License](#license)
18
+
19
+ ## Features
20
+
21
+ 1. **Model Data Formatting (`Base`)**: Safely and flexibly format `ActiveRecord` instances into Hashes (JSON ready) with whitelisting, blacklisting, aliasing, and custom formatting.
22
+ 2. **Database Migration Macros (`TableDefinition`)**: One-click generation of audit fields (e.g., `created_by`, `updated_by`, `deleted_by`) and their corresponding timestamps.
23
+ 3. **Soft Delete (`DeletedOperation`)**: Standardized soft-delete implementation using an integer flag, coupled with automatic `default_scope` injection and audit trail recording.
24
+
25
+ ## Applicable Scenarios
26
+
27
+ - **RESTful API Development**: When you need a unified and configurable way to serialize ActiveRecord models into JSON responses, avoiding sensitive data leakage.
28
+ - **Enterprise / B2B Systems**: When strict data audit trails are required (tracking exactly *who* performed the creation, update, or deletion and *when*).
29
+ - **Data Retention Requirements**: Systems that prohibit hard deletion (destroy) and require records to be marked as deleted (Soft Delete) instead.
6
30
 
7
31
  ## Installation
8
32
 
9
33
  Add this line to your application's Gemfile:
10
34
 
11
35
  ```ruby
12
- gem 'activemodel-object-info'
36
+ gem 'activemodel-object-info', '~> 0.4.0'
13
37
  ```
14
38
 
15
39
  And then execute:
16
40
 
17
- $ bundle
41
+ ```bash
42
+ $ bundle install
43
+ ```
18
44
 
19
- Or install it yourself as:
45
+ ## Usage
20
46
 
21
- $ gem install activemodel-object-info
47
+ ### 1. Data Formatting (Base)
22
48
 
23
- ## Usage
49
+ The `ActivemodelObjectInfo::Base` module adds the `instance_info` method to your models. It allows you to output formatted hashes based on predefined or runtime configurations.
24
50
 
25
- TODO: Write usage instructions here
51
+ **Setup in Model:**
26
52
 
27
- ## Development
53
+ ```ruby
54
+ class User < ApplicationRecord
55
+ include ActivemodelObjectInfo::Base
56
+
57
+ # Define default output configuration
58
+ INSTANCE_INFO = {
59
+ only: [:id, :name, :status, :created_at],
60
+ attributes: [
61
+ :id,
62
+ { name: :name, as: :user_name }, # Aliasing
63
+ { name: :status, filter: ->(v) { v == 1 ? 'Active' : 'Inactive' } }, # Custom lambda filter
64
+ { name: :created_at, format: :date }, # Date formatting ('%Y-%m-%d')
65
+ { name: :virtual_field, type: :abstract, filter: ->(*) { "#{id}-#{name}" } } # Virtual field
66
+ ]
67
+ }.freeze
68
+ end
69
+ ```
28
70
 
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.
71
+ **Usage:**
30
72
 
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).
73
+ ```ruby
74
+ user = User.first
32
75
 
33
- ## Contributing
76
+ # Use the default INSTANCE_INFO constant
77
+ user.instance_info
78
+ # => { id: 1, user_name: "John", status: "Active", created_at: "2026-07-28", virtual_field: "1-John" }
34
79
 
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.
80
+ # Override at runtime (Supports Ruby 2.x Hash and Ruby 3.x Keyword Arguments)
81
+ user.instance_info(only: [:id, :name])
82
+ # => { id: 1, name: "John" }
83
+ ```
36
84
 
37
- ## License
85
+ ### 2. Migration Macros (TableDefinition)
38
86
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
87
+ The `ActivemodelObjectInfo::TableDefinition` module extends ActiveRecord's migration capabilities to generate audit fields effortlessly.
88
+
89
+ **Setup:**
90
+ This is usually loaded automatically, but you can explicitly extend it if needed in your initializers.
91
+
92
+ **Usage in Migrations:**
93
+
94
+ ```ruby
95
+ class CreateUsers < ActiveRecord::Migration[6.1]
96
+ def change
97
+ create_table :users do |t|
98
+ t.string :name
99
+
100
+ # Generates full audit suite:
101
+ # created_by, created_at, updated_by, updated_at, deleted(int), deleted_by, deleted_at
102
+ t.generate_operations
103
+
104
+ # Or generate specific custom audit fields:
105
+ # Generates: audit_by (bigint), audit_at (datetime)
106
+ t.operation_columns(:audit)
107
+
108
+ # Generates: my_review_user (bigint), my_review_time (datetime)
109
+ t.operation_columns(:review, operator_prefix: 'my_', operator_suffix: '_user', timestamp_suffix: '_time')
110
+ end
111
+ end
112
+ end
113
+ ```
114
+
115
+ ### 3. Soft Delete (DeletedOperation)
116
+
117
+ The `ActivemodelObjectInfo::DeletedOperation` module provides an out-of-the-box soft deletion feature that works perfectly with the fields generated by `TableDefinition`.
118
+
119
+ **Setup in Model:**
120
+
121
+ ```ruby
122
+ class User < ApplicationRecord
123
+ include ActivemodelObjectInfo::DeletedOperation
124
+
125
+ # Optional: Override default column names and values if your database uses different conventions
126
+ # DELETED_FIELD = 'is_deleted'
127
+ # DELETED_VALID_VALUE = false
128
+ # DELETED_INVALID_VALUE = true
129
+ end
130
+ ```
131
+
132
+ **Usage:**
40
133
 
41
- ## Code of Conduct
134
+ ```ruby
135
+ # 1. Automatic Scope:
136
+ # The module automatically injects a `default_scope` to hide deleted records.
137
+ User.all # => SELECT * FROM users WHERE deleted = 0
138
+
139
+ # 2. Perform Soft Delete:
140
+ user = User.find(1)
141
+
142
+ # You MUST provide the `user_id` of the operator performing the deletion
143
+ user.soft_delete(user_id: current_user.id)
144
+ # This will:
145
+ # - Set `deleted` to 1
146
+ # - Set `deleted_by` to current_user.id
147
+ # - Set `deleted_at` to Time.now
148
+ # - Call `save`
149
+
150
+ # 3. Soft Delete and refresh updated_at:
151
+ user.soft_delete(user_id: current_user.id, refresh_updated: true)
152
+
153
+ # 4. Strict Soft Delete (raises exception on failure):
154
+ user.soft_delete!(user_id: current_user.id)
155
+ ```
42
156
 
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).
157
+ ## Development & Testing
158
+
159
+ After checking out the repo, run `bin/setup` to install dependencies.
160
+
161
+ To run tests:
162
+ ```bash
163
+ $ bundle exec rspec
164
+ ```
165
+
166
+ To run code style checks:
167
+ ```bash
168
+ $ bundle exec rubocop
169
+ ```
170
+
171
+ ## License
172
+
173
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/README.zh-CN.md ADDED
@@ -0,0 +1,171 @@
1
+ # Activemodel::Object::Info
2
+
3
+ [English](README.md) | [中文文档](README.zh-CN.md)
4
+
5
+ **Activemodel::Object::Info** 是一个用于扩展 `ActiveModel` 和 `ActiveRecord` 的 Ruby Gem。它为日常业务开发中常见的数据输出格式化、数据库迁移中的审计追踪字段生成、以及软删除(Soft Delete)功能提供了优雅且标准化的解决方案。
6
+
7
+ ## 目录
8
+
9
+ - [核心特性](#核心特性)
10
+ - [适用场景](#适用场景)
11
+ - [安装指南](#安装指南)
12
+ - [详细使用方法](#详细使用方法)
13
+ - [1. 模型数据格式化输出 (Base)](#1-模型数据格式化输出-base)
14
+ - [2. 数据库迁移宏 (TableDefinition)](#2-数据库迁移宏-tabledefinition)
15
+ - [3. 软删除机制 (DeletedOperation)](#3-软删除机制-deletedoperation)
16
+ - [开发与测试](#开发与测试)
17
+ - [开源协议](#开源协议)
18
+
19
+ ## 核心特性
20
+
21
+ 1. **模型数据格式化 (`Base`)**:安全、灵活地将 `ActiveRecord` 实例转换为 Hash(便于输出为 JSON)。支持字段白名单、黑名单、字段别名、自定义转换逻辑以及时间格式化。
22
+ 2. **数据库迁移宏 (`TableDefinition`)**:在创建数据库表时,一键生成常见的审计字段(例如 `created_by`, `updated_by`, `deleted_by`)以及配套的时间戳。
23
+ 3. **软删除机制 (`DeletedOperation`)**:基于整型标记字段的标准软删除实现,自动注入隐藏已删除数据的 `default_scope`,并在删除时强制记录操作人及时间。
24
+
25
+ ## 适用场景
26
+
27
+ - **RESTful API 开发**:当你需要统一且高度可配置的方式,将 ActiveRecord 模型序列化为 JSON 响应并过滤掉敏感数据时。
28
+ - **企业级 / B2B 系统后台**:业务上对数据的审计追踪(Audit Trails)要求极其严格,必须精准记录每一条数据是谁在什么时候创建、修改和删除的。
29
+ - **数据留存要求高的系统**:严禁在数据库中执行硬删除(Destroy),要求所有的删除操作均采用软删除(Soft Delete)以备后续追溯。
30
+
31
+ ## 安装指南
32
+
33
+ 将以下代码添加到你项目中的 Gemfile 里:
34
+
35
+ ```ruby
36
+ gem 'activemodel-object-info', '~> 0.4.0'
37
+ ```
38
+
39
+ 然后执行依赖安装:
40
+
41
+ ```bash
42
+ $ bundle install
43
+ ```
44
+
45
+ ## 详细使用方法
46
+
47
+ ### 1. 模型数据格式化输出 (Base)
48
+
49
+ `ActivemodelObjectInfo::Base` 模块为你的模型注入了 `instance_info` 方法。它能让你基于类中预定义的常量或运行时传入的参数,将实例对象输出为格式化的 Hash。
50
+
51
+ **在模型中配置:**
52
+
53
+ ```ruby
54
+ class User < ApplicationRecord
55
+ include ActivemodelObjectInfo::Base
56
+
57
+ # 定义默认的输出配置选项
58
+ INSTANCE_INFO = {
59
+ only: [:id, :name, :status, :created_at],
60
+ attributes: [
61
+ :id,
62
+ { name: :name, as: :user_name }, # 使用 as 设定别名
63
+ { name: :status, filter: ->(v) { v == 1 ? '活跃' : '停用' } }, # 使用 lambda 自定义转换逻辑
64
+ { name: :created_at, format: :date }, # 格式化日期为 ('%Y-%m-%d')
65
+ { name: :virtual_field, type: :abstract, filter: ->(*) { "#{id}-#{name}" } } # 根据其他字段合成的虚拟字段
66
+ ]
67
+ }.freeze
68
+ end
69
+ ```
70
+
71
+ **实际调用:**
72
+
73
+ ```ruby
74
+ user = User.first
75
+
76
+ # 不传参时,默认使用模型中的 INSTANCE_INFO 常量配置
77
+ user.instance_info
78
+ # => { id: 1, user_name: "John", status: "活跃", created_at: "2026-07-28", virtual_field: "1-John" }
79
+
80
+ # 运行时覆盖配置(完美兼容 Ruby 2.x 的 Hash 传参和 Ruby 3.x 的关键字参数)
81
+ user.instance_info(only: [:id, :name])
82
+ # => { id: 1, name: "John" }
83
+ ```
84
+
85
+ ### 2. 数据库迁移宏 (TableDefinition)
86
+
87
+ `ActivemodelObjectInfo::TableDefinition` 模块对 ActiveRecord 的 Migration 进行了扩展,让你能够毫不费力地生成审计字段。
88
+
89
+ **在迁移文件中的使用:**
90
+
91
+ ```ruby
92
+ class CreateUsers < ActiveRecord::Migration[6.1]
93
+ def change
94
+ create_table :users do |t|
95
+ t.string :name
96
+
97
+ # 1. 批量生成全套生命周期审计字段:
98
+ # 将会自动生成: created_by, created_at, updated_by, updated_at, deleted(int), deleted_by, deleted_at
99
+ t.generate_operations
100
+
101
+ # 2. 或者生成指定的自定义审计字段:
102
+ # 将会自动生成: audit_by (bigint), audit_at (datetime)
103
+ t.operation_columns(:audit)
104
+
105
+ # 3. 甚至高度自定义前后缀:
106
+ # 将会自动生成: my_review_user (bigint), my_review_time (datetime)
107
+ t.operation_columns(:review, operator_prefix: 'my_', operator_suffix: '_user', timestamp_suffix: '_time')
108
+ end
109
+ end
110
+ end
111
+ ```
112
+
113
+ ### 3. 软删除机制 (DeletedOperation)
114
+
115
+ `ActivemodelObjectInfo::DeletedOperation` 模块提供了一套开箱即用的软删除功能,并且与 `TableDefinition` 宏生成的底层字段完美契合。
116
+
117
+ **在模型中配置:**
118
+
119
+ ```ruby
120
+ class User < ApplicationRecord
121
+ include ActivemodelObjectInfo::DeletedOperation
122
+
123
+ # 可选:如果你的遗留数据库使用了其他命名习惯,可以覆盖以下常量
124
+ # DELETED_FIELD = 'is_deleted'
125
+ # DELETED_VALID_VALUE = false
126
+ # DELETED_INVALID_VALUE = true
127
+ end
128
+ ```
129
+
130
+ **实际调用:**
131
+
132
+ ```ruby
133
+ # 1. 自动查询作用域:
134
+ # 引入该模块后,会自动注入 `default_scope` 隐藏已被删除的记录。
135
+ User.all # 实际执行 => SELECT * FROM users WHERE deleted = 0
136
+
137
+ # 2. 执行软删除:
138
+ user = User.find(1)
139
+
140
+ # 出于安全和审计规范,你【必须】提供执行删除操作的用户 ID (user_id)
141
+ user.soft_delete(user_id: current_user.id)
142
+ # 这一步将自动执行以下操作:
143
+ # - 将 `deleted` 标记设为 1
144
+ # - 将 `deleted_by` 设为 current_user.id
145
+ # - 将 `deleted_at` 设为当前时间 Time.now
146
+ # - 调用 `save` 保存到数据库
147
+
148
+ # 3. 软删除并同时刷新更新时间 (updated_at):
149
+ user.soft_delete(user_id: current_user.id, refresh_updated: true)
150
+
151
+ # 4. 强校验软删除 (底层调用 save!,失败时抛出异常):
152
+ user.soft_delete!(user_id: current_user.id)
153
+ ```
154
+
155
+ ## 开发与测试
156
+
157
+ 克隆本仓库后,运行 `bin/setup` 安装开发依赖。
158
+
159
+ 运行 RSpec 单元测试:
160
+ ```bash
161
+ $ bundle exec rspec
162
+ ```
163
+
164
+ 运行 RuboCop 代码风格静态检查:
165
+ ```bash
166
+ $ bundle exec rubocop
167
+ ```
168
+
169
+ ## 开源协议
170
+
171
+ 本项目遵循 [MIT License](https://opensource.org/licenses/MIT) 开源协议。
@@ -4,8 +4,37 @@ module ActivemodelObjectInfo
4
4
  #
5
5
  # 通用的设置操作项相关的模型方法和处理。
6
6
  #
7
+ # 核心作用与职责:
8
+ # 提供模型实例数据格式化输出能力。主要用于将 ActiveRecord 或 ActiveModel 的实例安全、灵活地转换为 Hash,
9
+ # 方便后续转化为 JSON 等格式输出给前端,支持字段过滤、重命名、自定义处理及日期格式化。
10
+ #
11
+ # @example 详细使用方法与示例
12
+ # class User
13
+ # include ActivemodelObjectInfo::Base
14
+ # attr_accessor :id, :name, :created_at
15
+ #
16
+ # INSTANCE_INFO = {
17
+ # only: [:id, :name, :created_at],
18
+ # attributes: [
19
+ # :id,
20
+ # { name: :name, as: :user_name },
21
+ # { name: :created_at, format: :date }
22
+ # ]
23
+ # }.freeze
24
+ # end
25
+ #
26
+ # user = User.new(id: 1, name: "test", created_at: Time.now)
27
+ # user.instance_info # => { id: 1, user_name: "test", created_at: "2026-07-28" }
28
+ #
29
+ # 核心重要方法:
30
+ # - {#instance_info}:核心输出方法,返回处理后的散列。
31
+ #
7
32
  # @author shiner527 <shiner527@hotmail.com>
8
33
  #
34
+ # [Changelog]
35
+ # [2026-07-28] 补充完整 YARD 文档及核心注释,重构 instance_info 签名以兼容 Ruby 3.x 关键字参数 (shiner527)
36
+ # [2021-04-19] 创建基础模块,提供 instance_info 数据格式化输出功能 (shiner527)
37
+ #
9
38
  module Base
10
39
  # ===== 常量定义 =====
11
40
 
@@ -25,22 +54,32 @@ module ActivemodelObjectInfo
25
54
  #
26
55
  # 对象信息输出。主要返回给前端一个可用的散列(会被转化为JSON格式)格式的信息并传递给前端。
27
56
  #
28
- # @param [Hash] options 设置选项
29
- # @option options [Array<Symbol, Hash>] :attributes 具体每一项输出的设置数组。每个元素既可以是标识符实例也可以是一个散列实例。
57
+ # @param [Hash] options_hash 设置选项(传统位置参数 Hash),如果传入将被与 keyword_options 合并
58
+ # @param [Hash] keyword_options 设置选项(Ruby 3 关键字参数)
59
+ # @option keyword_options [Array<Symbol, Hash>] :attributes 具体每一项输出的设置数组。每个元素既可以是标识符实例也可以是一个散列实例。
30
60
  # 如果是标识符实例,则表示输出该属性。如果是一个散列实例,则按照散列中的设定值去生成对应的内容。
31
- # @option options [Array<Symbol>] :only 给出具体可以用来输出的字段属性名数组。
61
+ # @option keyword_options [Array<Symbol>] :only 给出具体可以用来输出的字段属性名数组。
62
+ # @option keyword_options [Array<Symbol>] :except 给出需要被排除输出的字段属性名数组。
63
+ # @option keyword_options [String, Symbol] :datetime_format 全局的时间格式设置。
32
64
  #
33
65
  # @return [Hash] 返回的处理过的该对象的信息散列。
34
66
  #
35
- def instance_info(**options)
36
- # puts "ARGS: #{args}, OPTIONS: #{options}"
37
- # 参考默认的 options
67
+ def instance_info(options_hash = nil, **keyword_options)
68
+ # 合并传统参数与关键字参数,完美兼容 Ruby 2.x Hash 传参和 Ruby 3.x 的 **kwargs 传参
69
+ options = (options_hash || {}).merge(keyword_options)
70
+
71
+ # 尝试获取当前类上配置的 INSTANCE_INFO 常量作为默认选项
38
72
  options = "#{self.class}::INSTANCE_INFO".safe_constantize || {} if options.blank?
73
+
74
+ # 将 options 的 key 转为 symbol,避免传入字符串 key 导致匹配不到
75
+ options = options.deep_symbolize_keys
76
+
39
77
  result = {}
40
- # 仅包含的字段
78
+
79
+ # 1. 整理包含字段和排除字段
41
80
  only_attributes = (options[:only] || []).map(&:to_sym)
42
- # 要排除的字段
43
- # 默认不包含 :deleted, :deleted_by 和 :deleted_at 三个字段
81
+
82
+ # 默认排除逻辑:如果不特意指定,系统会默认排除 deleted 等敏感字段,防止软删除数据泄漏
44
83
  default_deleted_column = ::Constants::Base::TABLE_COLUMN_DELETE_COLUMN rescue 'deleted'
45
84
  default_except_attrs = [
46
85
  default_deleted_column,
@@ -48,42 +87,48 @@ module ActivemodelObjectInfo
48
87
  "#{default_deleted_column}_at",
49
88
  ]
50
89
  except_attributes = (options[:except] || default_except_attrs).map(&:to_sym)
51
- # 字段的具体属性设置
90
+
91
+ # 2. 从当前类所有属性中计算出最终要输出的属性名集合
52
92
  attribute_configs = options[:attributes] || []
53
- # 计算要输出的参数名称
54
93
  output_attributes = attribute_names.map(&:to_sym)
55
94
  output_attributes &= only_attributes if only_attributes.present?
56
95
  output_attributes -= except_attributes
57
96
 
58
- # puts "Excepts: #{except_attributes}, Only: #{only_attributes}, Output: #{output_attributes}"
97
+ # 3. 遍历并处理属性配置(attribute_configs)
59
98
  # 这里保证了要引入的类中含有 attributes 方法,且为 Hash 类型
99
+
100
+ # 补丁:如果传入了 only_attributes,但是 options[:attributes] 是空(即调用方没传),
101
+ # 我们需要将 only_attributes 中的字段视为基础配置,以确保只包含这几个字段的正常输出
102
+ attribute_configs = only_attributes.map { |attr| { name: attr } } if attribute_configs.empty? && only_attributes.present?
103
+
60
104
  attribute_configs.each do |attr_config|
61
- # 如果设置项单纯是字符串或者标识符的情况
105
+ # 根据配置项的类型(Symbol/String 或 Hash)提取字段名称和具体配置
62
106
  if [::String, ::Symbol].any? { |attr_class| attr_config.is_a?(attr_class) }
63
107
  attribute_name = attr_config.to_sym
64
108
  current_attr_config = {}
65
- elsif attr_config.is_a?(::Hash) # 如果设置项为一个散列
109
+ elsif attr_config.is_a?(::Hash)
66
110
  current_attr_config = attr_config.deep_symbolize_keys
67
111
  attribute_name = current_attr_config[:name]
68
- else # 如果不是指定的类型,则跳过进行下一项
112
+ else
113
+ # 如果配置格式非法则直接跳过
69
114
  next
70
115
  end
71
- # 输出的参数名称,如果用户给出了 as 指定,优先用用户给定的,否则默认用属性名
116
+
117
+ # 确定实际取值的属性名,支持通过 as 别名取值
72
118
  raw_name = current_attr_config[:as].present? ? current_attr_config[:as].to_sym : attribute_name
73
- # 过滤器输出字段,如果给定了一个块,则使用用户指定的,否则默认为没有过滤器
74
119
  filter = current_attr_config[:filter]
75
120
 
76
- # 获取真正对应的属性的名称和其值
121
+ # 取出对象中的实际值
77
122
  k = raw_name
78
- # 当类型为抽象类时,是不会有对应的方法和属性的,因此直接设置为 nil 并期待之后的 filter 等规则来生成实际值。
123
+ # 特殊处理 abstract 类型:当类型为抽象类时,由于可能没有对应的真实属性方法,直接赋值为 nil,后续依赖 filter 来生成实际值
79
124
  v = current_attr_config[:type] == :abstract ? nil : __send__(k)
80
125
 
81
- # puts "instance attributes #{k}: #{v}(#{v.class.name})"
82
- # 如果不是要输出的字段则跳过,或者如果不是给出type设置项为 :method 或者 :abstract 也跳过。
126
+ # 跳过条件:既不在最终输出名单中,也不是显式声明的 abstract/method 虚拟字段
83
127
  next unless output_attributes.include?(k) || %i[abstract method].include?(current_attr_config[:type])
84
128
 
85
- # 赋值。如果有过滤器,优先使用过滤器
129
+ # 4. 根据类型或 filter 对值进行格式化转换
86
130
  if filter.present?
131
+ # 如果配置了 filter,通过 Proc、Symbol(调用同名方法) 或 直接常量 的形式进行过滤转换
87
132
  result[attribute_name] = case filter
88
133
  when ::Proc
89
134
  instance_exec(v, &filter)
@@ -92,28 +137,29 @@ module ActivemodelObjectInfo
92
137
  else
93
138
  filter
94
139
  end
95
- # 如果值是时间类别的字段,默认转换为时间日期格式的字符串
96
140
  elsif [::Date, ::Time, ::DateTime].any? { |time_class| v.is_a?(time_class) }
97
- # 时间日期格式设定,优先使用当前字段自定义,否则使用通用自定义,最后使用默认格式
141
+ # 对时间类型的字段进行格式化,优先级:字段级自定义 format > 全局 datetime_format > 默认全格式
98
142
  attribute_format = current_attr_config[:format].present? ? current_attr_config[:format] : options[:datetime_format]
99
- result[attribute_name] = if attribute_format.to_s == 'standard' # 会被转换为标准时间日期格式
100
- v
101
- elsif attribute_format.present? # 按照设定时间日期方法的格式
102
- # 旧有格式匹配支持
143
+ result[attribute_name] = if attribute_format.to_s == 'standard'
144
+ v # standard 保持时间对象原生格式
145
+ elsif attribute_format.present?
146
+ # 匹配内置的别名格式 (full/min/date/month/year),否则作为自定义 strftime 字符串处理
103
147
  if %i[full min date month year].include?(attribute_format.to_sym)
104
148
  v.strftime("::#{self.class}::DATETIME_#{attribute_format.to_s.upcase}".constantize)
105
- else # 否则认为直接传递的是 strftime 支持的自定义格式
149
+ else
106
150
  v.strftime(attribute_format)
107
151
  end
108
- else # 默认使用 'yyyy-MM-dd hh:mm:ss' 的格式
152
+ else
153
+ # 缺省转换为 '%Y-%m-%d %H:%M:%S'
109
154
  v.strftime(DATETIME_FULL)
110
155
  end
111
- else # 其他值类型默认赋值自己
156
+ else
157
+ # 普通值直接赋值
112
158
  result[attribute_name] = v
113
159
  end
114
160
  end
115
161
 
116
- # 过滤第一层键名为符号并返回
162
+ # 统一将结果第一层的键名转化为 Symbol 格式并返回
117
163
  result.symbolize_keys!
118
164
  end
119
165
  end
@@ -4,59 +4,97 @@ module ActivemodelObjectInfo
4
4
  #
5
5
  # 用来进行和删除操作相关的数据库模型的功能模组。主要结合了使用 +:deleted+ 字段标识数据软删除的功能。
6
6
  #
7
+ # 核心作用与职责:
8
+ # 为 ActiveRecord 模型提供标准化的“软删除(Soft Delete)”功能。
9
+ # 引入该模块后,会自动通过 `default_scope` 过滤掉已标记为删除的记录,
10
+ # 并注入 `soft_delete` / `soft_delete!` 方法。在执行软删除时,会自动更新
11
+ # 相关的审计字段(如 deleted_by, deleted_at, updated_by)。
12
+ #
13
+ # @example 详细使用方法与示例
14
+ # class User < ApplicationRecord
15
+ # include ActivemodelObjectInfo::DeletedOperation
16
+ #
17
+ # # 可选:覆盖默认的配置常量
18
+ # DELETED_FIELD = 'is_deleted'
19
+ # DELETED_VALID_VALUE = false
20
+ # DELETED_INVALID_VALUE = true
21
+ # end
22
+ #
23
+ # # 查询数据时会自动应用 default_scope: WHERE is_deleted = false
24
+ # User.all
25
+ #
26
+ # # 软删除一条记录(必须要传入 user_id)
27
+ # user = User.find(1)
28
+ # user.soft_delete(user_id: current_user.id, refresh_updated: true)
29
+ #
30
+ # 核心重要方法:
31
+ # - {#soft_delete}:执行软删除并调用 save 更新数据。
32
+ # - {#soft_delete!}:执行软删除并调用 save! 更新数据(抛出异常)。
33
+ # - {#delete_block}:底层通用的删除字段赋值逻辑块。
34
+ #
7
35
  # @author shiner527 <shiner527@hotmail.com>
8
36
  #
37
+ # [Changelog]
38
+ # [2026-07-28] 补充完整 YARD 文档、行内注释,清理尾随空格 (shiner527)
39
+ # [2021-04-19] 创建基础模块,提供软删除与作用域注入功能 (shiner527)
40
+ #
9
41
  module DeletedOperation
10
42
  extend ::ActiveSupport::Concern
11
43
 
12
44
  # 当被 include 关键字引入后的处理
13
45
  included do |_|
14
- # 定义好默认的删除查询语句
46
+ # 1. 提取常量配置:如果宿主类定义了覆盖常量,则使用宿主类的,否则使用默认值
15
47
  deleted_field = const_defined?(:DELETED_FIELD) ? DELETED_FIELD : 'deleted'
16
48
  deleted_value_valid = const_defined?(:DELETED_VALID_VALUE) ? DELETED_VALID_VALUE : 0
17
49
  deleted_value_invalid = const_defined?(:DELETED_INVALID_VALUE) ? DELETED_INVALID_VALUE : 1
18
- # 如果定义了删除标记字段则默认搜索自动排除该内容
50
+
51
+ # 2. 注入 default_scope 默认查询作用域:如果模型拥有删除标记字段,则默认查询自动排除被标记的数据
19
52
  default_scope do
20
53
  _current_model = try(:name).to_s.safe_constantize
21
- where(deleted_field.to_sym => deleted_value_valid) if _current_model.method_defined?(deleted_field)
54
+ where(deleted_field.to_sym => deleted_value_valid) if _current_model.has_attribute?(deleted_field)
22
55
  end
23
56
 
24
- # 通用的删除过程
57
+ # 3. 定义底层的删除逻辑块 (给实例对象的内存属性赋值)
25
58
  define_method(:delete_block) do |**options|
26
- # 处理可选项参数
59
+ # 处理可选项参数:默认不刷新 updated_at 字段
27
60
  options[:refresh_updated] = options[:refresh_updated].nil? ? false : options[:refresh_updated]
28
61
 
29
- # 必须要传递操作的用户ID信息
62
+ # 必须要传递操作的用户ID信息,强制审计规范
30
63
  raise ArgumentError, 'Must give user id!' if options[:user_id].blank?
31
64
 
32
- # 获取更新人字段,删除人字段
65
+ # 提取字段名配置
33
66
  updated_by_field = (options[:updated_by_field] || 'updated_by').to_sym
34
67
  deleted_by_field = (options[:deleted_by_field] || 'deleted_by').to_sym
35
68
  deleted_at_field = (options[:deleted_at_field] || 'deleted_at').to_sym
36
- # 设置删除状态为已失效
69
+
70
+ # 核心:设置删除状态为已失效标记值
37
71
  __send__("#{deleted_field}=", deleted_value_invalid)
38
- # 设置删除人、删除时间和更新人
72
+
73
+ # 审计:如果模型存在对应的审计字段,则设置删除人、删除时间,以及根据配置选择性地设置更新人
39
74
  __send__("#{deleted_by_field}=", options[:user_id]) if respond_to?(deleted_by_field)
40
75
  __send__("#{deleted_at_field}=", Time.now.localtime) if respond_to?(deleted_at_field)
41
76
  __send__("#{updated_by_field}=", options[:user_id]) if respond_to?(updated_by_field) && options[:refresh_updated]
42
77
  end
43
78
 
44
- # 定义删除方法,但是不覆盖 ActiveRecord 的同名方法。
79
+ # 4. 暴露的常规软删除方法 (使用 save 存储,返回 boolean)
45
80
  define_method(:soft_delete) do |**options|
46
81
  opts = options.deep_symbolize_keys
47
- # 如果没有定义删除标记字段则不执行
82
+ # 安全防范:如果没有定义删除标记字段则直接返回,不执行任何操作
48
83
  return unless respond_to?(deleted_field)
84
+
49
85
  delete_block(**opts)
50
- # 根据设置决定是否刷更新时间
86
+
87
+ # 调用 ActiveRecord 内置方法持久化,根据 refresh_updated 参数决定是否让系统自动更新 updated_at
51
88
  save(touch: opts[:refresh_updated])
52
89
  end
53
90
 
54
- # 定义删除方法,区别为最终调用 save! 方法更新数据,所以遇到异常会抛出异常
91
+ # 5. 暴露的强校验软删除方法 (使用 save! 存储,失败抛出异常)
55
92
  define_method(:soft_delete!) do |**options|
56
93
  opts = options.deep_symbolize_keys
57
94
  return unless respond_to?(deleted_field)
95
+
58
96
  delete_block(**opts)
59
- # 根据设置决定是否刷更新时间
97
+
60
98
  save!(touch: opts[:refresh_updated])
61
99
  end
62
100
  end
@@ -5,8 +5,38 @@ module ActivemodelObjectInfo
5
5
  # 重新扩展一些方法,用来方便在创建迁移文件的时候进行一些通用类的设定。
6
6
  # 从 @version 0.3.0 开始支持了自动创建的时间戳字段增加索引
7
7
  #
8
+ # 核心作用与职责:
9
+ # 在 ActiveRecord 数据库迁移(Migration)建表时,提供便捷的宏方法,
10
+ # 一键生成通用的审计追踪字段(如操作人、操作时间)及软删除字段。
11
+ #
12
+ # @example 详细使用方法与示例
13
+ # class CreateUsers < ActiveRecord::Migration[6.1]
14
+ # def change
15
+ # create_table :users do |t|
16
+ # t.string :name
17
+ #
18
+ # # 示例1: 生成指定的自定义操作字段
19
+ # t.operation_columns(:audit, with_operator: true, operator_prefix: 'my_', operator_suffix: '_user')
20
+ # # => 将会生成 my_audit_user (bigint) 和 audit_at (datetime) 字段
21
+ #
22
+ # # 示例2: 生成常用的创建、更新、删除字段套装
23
+ # t.generate_operations
24
+ # # => 将会生成 created_by, created_at, updated_by, updated_at, deleted_by, deleted_at, 以及 deleted(integer)
25
+ # end
26
+ # end
27
+ # end
28
+ #
29
+ # 核心重要方法:
30
+ # - {#operation_columns}:生成指定操作的审计字段(含前缀、后缀配置)。
31
+ # - {#generate_operations}:批量生成创建、更新、删除相关的全套审计字段。
32
+ #
8
33
  # @author shiner527 <shiner527@hotmail.com>
9
34
  #
35
+ # [Changelog]
36
+ # [2026-07-28] 补充完整 YARD 文档、行内注释,清理尾随空格 (shiner527)
37
+ # [2021-04-20] 支持自动创建的时间戳字段增加索引 (@version 0.3.0) (shiner527)
38
+ # [2021-04-19] 创建基础模块,提供 operation_columns 与 generate_operations (shiner527)
39
+ #
10
40
  module TableDefinition
11
41
  #
12
42
  # 生成操作相关的字段。包含操作人、操作时间等信息。
@@ -38,21 +68,23 @@ module ActivemodelObjectInfo
38
68
  #
39
69
  def operation_columns(*fields, **options)
40
70
  # puts "operation_columns(#{fields}, #{options})"
41
- # 初始化设置选项
71
+ # 1. 提取并初始化各种设置选项,对于布尔值优先取用户配置,未配置则默认为 true
42
72
  with_operator = options[:with_operator].nil? ? true : options[:with_operator]
43
73
  with_timestamp = options[:with_timestamp].nil? ? true : options[:with_timestamp]
44
74
  operator_prefix = options[:operator_prefix]
45
75
  operator_suffix = options[:operator_suffix] || '_by'
46
76
  timestamp_prefix = options[:timestamp_prefix]
47
77
  timestamp_suffix = options[:timestamp_suffix] || '_at'
48
- # 依照每个字段进行设置
78
+
79
+ # 2. 遍历每一个需要生成的操作标识符
49
80
  fields.each do |field|
50
- # 如果要生成操作人信息
81
+ # 3. 动态拼接并生成操作人记录字段 (默认为 bigint 类型并添加索引)
51
82
  if with_operator
52
83
  operator_column_name = operator_prefix.to_s + field.to_s + operator_suffix.to_s
53
84
  column(operator_column_name, :bigint, index: true, comment: '操作人') if operator_column_name.present?
54
85
  end
55
- # 如果要生成操作时间戳信息
86
+
87
+ # 4. 动态拼接并生成操作时间戳字段 (默认为 datetime 类型并添加索引)
56
88
  if with_timestamp
57
89
  timestamp_column_name = timestamp_prefix.to_s + field.to_s + timestamp_suffix.to_s
58
90
  column(timestamp_column_name, :datetime, index: true, comment: '操作时间戳') if timestamp_column_name.present?
@@ -70,10 +102,16 @@ module ActivemodelObjectInfo
70
102
  def generate_operations(*operations)
71
103
  # puts "generate_operations(#{operations})"
72
104
  action_fields = operations
73
- # 默认只有创建、更新和删除相关的字段
105
+
106
+ # 1. 如果没有给出具体的动作名称,默认配置为创建、更新和删除这三个常规生命周期动作
74
107
  action_fields = %w[created updated deleted] if action_fields.empty?
108
+
109
+ # 2. 遍历所有的动作名称以生成相应的字段
75
110
  action_fields.each do |operation|
111
+ # 3. 特殊逻辑:当遇到 deleted 动作时,除了生成审计字段外,还需要额外部署一个整型的标记字段,用于承载软删除状态
76
112
  column(operation.to_s, :integer, default: 0, comment: '删除标记') if operation.to_sym == :deleted
113
+
114
+ # 4. 复用 operation_columns 方法来生成对应的 xxx_by 操作人和 xxx_at 操作时间字段
77
115
  operation_columns(operation)
78
116
  end
79
117
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module ActivemodelObjectInfo
4
4
  module Version
5
- VERSION = '0.3.3'
5
+ VERSION = '0.4.0'
6
6
  end
7
7
  end
@@ -7,9 +7,25 @@ require 'activemodel_object_info/table_definition'
7
7
  require 'activemodel_object_info/deleted_operation'
8
8
 
9
9
  #
10
- # 扩展模组。
10
+ # 扩展模组主入口。
11
+ #
12
+ # 核心作用与职责:
13
+ # 这是 activemodel-object-info Gem 的主命名空间和入口文件。
14
+ # 它负责统一加载所有相关的依赖库(ActiveSupport)和内部子模块(Base, TableDefinition, DeletedOperation)。
15
+ #
16
+ # @example 详细使用方法
17
+ # require 'activemodel_object_info'
18
+ #
19
+ # 包含的核心模块:
20
+ # - {ActivemodelObjectInfo::Base}:提供模型对象信息格式化输出能力。
21
+ # - {ActivemodelObjectInfo::TableDefinition}:提供数据库迁移时自动生成审计字段的能力。
22
+ # - {ActivemodelObjectInfo::DeletedOperation}:提供记录软删除及其作用域管理的能力。
11
23
  #
12
24
  # @author shiner527 <shiner527@hotmail.com>
13
25
  #
26
+ # [Changelog]
27
+ # [2026-07-28] 补充完整 YARD 文档 (shiner527)
28
+ # [2021-04-19] 创建基础模块结构及入口文件 (shiner527)
29
+ #
14
30
  module ActivemodelObjectInfo
15
31
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemodel_object_info
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - shiner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-30 00:00:00.000000000 Z
11
+ date: 2026-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -156,6 +156,7 @@ files:
156
156
  - Gemfile.lock
157
157
  - LICENSE
158
158
  - README.md
159
+ - README.zh-CN.md
159
160
  - Rakefile
160
161
  - activemodel_object_info.gemspec
161
162
  - bin/console
@@ -185,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
185
186
  - !ruby/object:Gem::Version
186
187
  version: '0'
187
188
  requirements: []
188
- rubygems_version: 3.4.10
189
+ rubygems_version: 3.1.6
189
190
  signing_key:
190
191
  specification_version: 4
191
192
  summary: Build a hash based on active record attributes.