Module: ActivemodelObjectInfo::Base
- Defined in:
- lib/activemodel_object_info/base.rb
Overview
通用的设置操作项相关的模型方法和处理。
核心作用与职责: 提供模型实例数据格式化输出能力。主要用于将 ActiveRecord 或 ActiveModel 的实例安全、灵活地转换为 Hash, 方便后续转化为 JSON 等格式输出给前端,支持字段过滤、重命名、自定义处理及日期格式化。
核心重要方法:
- #instance_info:核心输出方法,返回处理后的散列。
[Changelog] [2026-07-28] 支持通过 Proc / Lambda 完全自定义时间格式化 (shiner527) [2026-07-28] 支持关联对象嵌套输出 (includes) 与场景化输出配置 (context) (shiner527) [2026-07-28] 补充完整 YARD 文档及核心注释,重构 instance_info 签名以兼容 Ruby 3.x 关键字参数 (shiner527) [2021-04-19] 创建基础模块,提供 instance_info 数据格式化输出功能 (shiner527)
Constant Summary collapse
- DATETIME_FULL =
满格式
'%Y-%m-%d %H:%M:%S'- DATETIME_MIN =
截止到分
'%Y-%m-%d %H:%M'- DATETIME_DATE =
仅日期
'%Y-%m-%d'- DATETIME_MONTH =
仅年月
'%Y-%m'- DATETIME_YEAR =
仅年份
'%Y'
Instance Method Summary collapse
-
#instance_info(options_hash = nil, **keyword_options) ⇒ Hash
对象信息输出。主要返回给前端一个可用的散列(会被转化为 JSON 格式)格式的信息并传递给前端。.
Instance Method Details
#instance_info(options_hash = nil, **keyword_options) ⇒ Hash
对象信息输出。主要返回给前端一个可用的散列(会被转化为 JSON 格式)格式的信息并传递给前端。
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/activemodel_object_info/base.rb', line 116 def instance_info( = nil, **) # 合并传统参数与关键字参数,完美兼容 Ruby 2.x 的 Hash 传参和 Ruby 3.x 的 **kwargs 传参 = ( || {}).merge() # 尝试获取当前类上配置的常量作为默认选项 # 优先级:传入的参数 options > 场景常量 INSTANCE_INFO_#{CONTEXT} > 默认常量 INSTANCE_INFO if .blank? || (.keys.map(&:to_sym) == [:context] && [:context].present?) context_name = [:context].to_s.upcase if [:context].present? # 尝试寻找带场景的常量 = ("#{self.class}::INSTANCE_INFO_#{context_name}".safe_constantize if context_name.present?) # 如果带场景的常量不存在,或者没传场景,降级寻找默认常量 ||= "#{self.class}::INSTANCE_INFO".safe_constantize || {} end # 将 options 的 key 转为 symbol,避免传入字符串 key 导致匹配不到 = .deep_symbolize_keys result = {} # 1. 整理包含字段和排除字段 only_attributes = ([:only] || []).map(&:to_sym) # 默认排除逻辑:如果不特意指定,系统会默认排除 deleted 等敏感字段,防止软删除数据泄漏 default_deleted_column = ::Constants::Base::TABLE_COLUMN_DELETE_COLUMN rescue 'deleted' default_except_attrs = [ default_deleted_column, "#{default_deleted_column}_by", "#{default_deleted_column}_at", ] except_attributes = ([:except] || default_except_attrs).map(&:to_sym) # 2. 从当前类所有属性中计算出最终要输出的属性名集合 attribute_configs = [:attributes] || [] output_attributes = attribute_names.map(&:to_sym) output_attributes &= only_attributes if only_attributes.present? output_attributes -= except_attributes # 3. 遍历并处理属性配置(attribute_configs) # 这里保证了要引入的类中含有 attributes 方法,且为 Hash 类型 # 补丁:如果传入了 only_attributes,但是 options[:attributes] 是空(即调用方没传), # 并且当前并没有匹配到任何场景常量,我们需要将 only_attributes 中的字段视为基础配置,以确保只包含这几个字段的正常输出 attribute_configs = only_attributes.map { |attr| { name: attr } } if attribute_configs.empty? && only_attributes.present? # 补丁 2:如果在处理完毕后,attribute_configs 仍然为空,并且 output_attributes 存在 # 则说明我们正在执行类似 default options 的全量输出或者包含了所有字段的嵌套输出 # 此时,我们需要基于 output_attributes 自动生成 attribute_configs,但需要保留 options[:attributes] 中已经配置了 format 等逻辑的字段配置 if attribute_configs.empty? && output_attributes.present? # 取出最初预设在 options 里的属性配置 preset_configs = [:attributes] || [] # 获取 options 中显式定义了的所有字段的原始名和别名 preset_attr_names = preset_configs.flat_map do |config| case config when ::Hash [config[:name], config[:as]].compact.map(&:to_sym) when ::Symbol, ::String config.to_sym end end.compact.uniq supplementary_configs = (output_attributes - preset_attr_names).map { |attr| { name: attr } } # 将预设的规则配置和自动补充的基础配置合并,作为最终的 attribute_configs # 注意:为了避免重复处理以及处理顺序问题,我们统一使用预先配置,并在末尾追加没有配置的字段 attribute_configs = preset_configs + supplementary_configs # 移除 attribute_configs 中的重复项 (如果存在) attribute_configs.uniq! end attribute_configs.each do |attr_config| # 根据配置项的类型(Symbol/String 或 Hash)提取字段名称和具体配置 if [::String, ::Symbol].any? { |attr_class| attr_config.is_a?(attr_class) } attribute_name = attr_config.to_sym current_attr_config = {} elsif attr_config.is_a?(::Hash) current_attr_config = attr_config.deep_symbolize_keys attribute_name = current_attr_config[:name] else # 如果配置格式非法则直接跳过 next end # 确定实际取值的属性名,支持通过 as 别名取值 raw_name = current_attr_config[:as].present? ? current_attr_config[:as].to_sym : attribute_name filter = current_attr_config[:filter] # 获取真正对应的属性的名称和其值 k = raw_name # 当类型为抽象类时,是不会有对应的方法和属性的,因此直接设置为 nil 并期待之后的 filter 等规则来生成实际值。 v = current_attr_config[:type] == :abstract ? nil : __send__(k) # 跳过条件:既不在最终输出名单中,也不是显式声明的 abstract/method 虚拟字段 # 注意:需要同时判断别名(attribute_name)和原名(k)是否在需要输出的列表中 next unless output_attributes.include?(attribute_name) || output_attributes.include?(k) || %i[abstract method].include?(current_attr_config[:type]) # 4. 根据类型或 filter 对值进行格式化转换 if filter.present? # 如果配置了 filter,通过 Proc、Symbol(调用同名方法) 或 直接常量 的形式进行过滤转换 result[attribute_name] = case filter when ::Proc instance_exec(v, &filter) when ::Symbol __send__(filter) else filter end elsif [::Date, ::Time, ::DateTime].any? { |time_class| v.is_a?(time_class) } # 对时间类型的字段进行格式化,优先级:字段级自定义 format > 全局 datetime_format > 默认全格式 attribute_format = current_attr_config[:format].present? ? current_attr_config[:format] : [:datetime_format] result[attribute_name] = if attribute_format.to_s == 'standard' v # standard 保持时间对象原生格式 elsif attribute_format.is_a?(::Proc) # 支持传入 Lambda 或 Proc 进行完全自定义的格式化 instance_exec(v, &attribute_format) elsif attribute_format.present? # 匹配内置的别名格式 (full/min/date/month/year),否则作为自定义 strftime 字符串处理 if %i[full min date month year].include?(attribute_format.to_sym) v.strftime("::#{self.class}::DATETIME_#{attribute_format.to_s.upcase}".constantize) else v.strftime(attribute_format) end else # 缺省转换为 '%Y-%m-%d %H:%M:%S' v.strftime(DATETIME_FULL) end else # 普通值直接赋值 result[attribute_name] = v end end # 统一将结果第一层的键名转化为 Symbol 格式 result.symbolize_keys! # 5. 处理嵌套关联对象 (includes) format_associations!(result, ) result end |