activeitem 0.0.1 → 0.0.3
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 +4 -4
- data/CHANGELOG.md +36 -0
- data/LICENSE.txt +1 -1
- data/lib/active_item/associations.rb +21 -24
- data/lib/active_item/base.rb +88 -98
- data/lib/active_item/composed_of.rb +34 -23
- data/lib/active_item/configuration.rb +2 -0
- data/lib/active_item/database_helpers.rb +3 -3
- data/lib/active_item/errors.rb +4 -1
- data/lib/active_item/logging.rb +2 -0
- data/lib/active_item/model_loader.rb +4 -12
- data/lib/active_item/pagination.rb +8 -5
- data/lib/active_item/query_helpers.rb +44 -78
- data/lib/active_item/relation.rb +122 -113
- data/lib/active_item/transaction.rb +3 -1
- data/lib/active_item/validations.rb +22 -70
- data/lib/active_item/version.rb +1 -1
- data/lib/activeitem.rb +3 -0
- metadata +63 -17
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require 'active_model'
|
|
4
4
|
|
|
5
5
|
module ActiveItem
|
|
6
|
+
# ActiveModel validator that checks attribute uniqueness by querying
|
|
7
|
+
# DynamoDB, with optional scope and custom condition support.
|
|
6
8
|
class UniquenessValidator < ActiveModel::EachValidator
|
|
7
9
|
def validate_each(record, attribute, value)
|
|
8
10
|
return if value.nil? || value.to_s.empty?
|
|
@@ -14,82 +16,32 @@ module ActiveItem
|
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
relation = record.class.where(**conditions)
|
|
20
|
+
|
|
21
|
+
if options[:conditions]
|
|
22
|
+
# Custom conditions require loading records for Ruby-side filtering
|
|
23
|
+
existing = relation.to_a
|
|
24
|
+
existing = existing.reject { |r| r.id == record.id } if record.id
|
|
25
|
+
existing = existing.select { |r| options[:conditions].call(r) }
|
|
26
|
+
taken = existing.any?
|
|
27
|
+
elsif record.id
|
|
28
|
+
# Need to exclude current record — fetch up to 2 (current + one other)
|
|
29
|
+
existing = relation.limit(2).to_a
|
|
30
|
+
taken = existing.any? { |r| r.id != record.id }
|
|
31
|
+
else
|
|
32
|
+
# New record — just check if any match exists
|
|
33
|
+
taken = !relation.first.nil?
|
|
34
|
+
end
|
|
20
35
|
|
|
21
|
-
record.errors.add(attribute, options[:message] || 'has already been taken') if
|
|
36
|
+
record.errors.add(attribute, options[:message] || 'has already been taken') if taken
|
|
22
37
|
end
|
|
23
38
|
end
|
|
24
39
|
|
|
40
|
+
# Convenience validation macro for uniqueness (DynamoDB-specific).
|
|
41
|
+
# Length, numericality, and format validations are provided by ActiveModel.
|
|
25
42
|
module Validations
|
|
26
43
|
def validates_uniqueness_of(*attributes, **options)
|
|
27
|
-
validates(*attributes, uniqueness: options.empty?
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def validates_length_of(*attributes, **options)
|
|
31
|
-
attributes.each do |attribute|
|
|
32
|
-
validate do
|
|
33
|
-
value = send(attribute)
|
|
34
|
-
next if value.nil?
|
|
35
|
-
|
|
36
|
-
length = value.to_s.length
|
|
37
|
-
|
|
38
|
-
if options[:minimum] && length < options[:minimum]
|
|
39
|
-
errors.add(attribute, options[:message] || "is too short (minimum is #{options[:minimum]} characters)")
|
|
40
|
-
end
|
|
41
|
-
if options[:maximum] && length > options[:maximum]
|
|
42
|
-
errors.add(attribute, options[:message] || "is too long (maximum is #{options[:maximum]} characters)")
|
|
43
|
-
end
|
|
44
|
-
if options[:in] && !options[:in].include?(length)
|
|
45
|
-
errors.add(attribute, options[:message] || "length must be between #{options[:in].min} and #{options[:in].max} characters")
|
|
46
|
-
end
|
|
47
|
-
if options[:is] && length != options[:is]
|
|
48
|
-
errors.add(attribute, options[:message] || "must be exactly #{options[:is]} characters")
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def validates_numericality_of(*attributes, **options)
|
|
55
|
-
attributes.each do |attribute|
|
|
56
|
-
validate do
|
|
57
|
-
value = send(attribute)
|
|
58
|
-
next if value.nil?
|
|
59
|
-
|
|
60
|
-
unless value.is_a?(Numeric) || value.to_s.match?(/\A-?\d+(\.\d+)?\z/)
|
|
61
|
-
errors.add(attribute, options[:message] || "is not a number")
|
|
62
|
-
next
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
num_value = value.to_f
|
|
66
|
-
|
|
67
|
-
if options[:only_integer] && num_value != num_value.to_i
|
|
68
|
-
errors.add(attribute, options[:message] || "must be an integer")
|
|
69
|
-
next
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
if options[:greater_than] && num_value <= options[:greater_than]
|
|
73
|
-
errors.add(attribute, options[:message] || "must be greater than #{options[:greater_than]}")
|
|
74
|
-
end
|
|
75
|
-
if options[:greater_than_or_equal_to] && num_value < options[:greater_than_or_equal_to]
|
|
76
|
-
errors.add(attribute, options[:message] || "must be greater than or equal to #{options[:greater_than_or_equal_to]}")
|
|
77
|
-
end
|
|
78
|
-
if options[:less_than] && num_value >= options[:less_than]
|
|
79
|
-
errors.add(attribute, options[:message] || "must be less than #{options[:less_than]}")
|
|
80
|
-
end
|
|
81
|
-
if options[:less_than_or_equal_to] && num_value > options[:less_than_or_equal_to]
|
|
82
|
-
errors.add(attribute, options[:message] || "must be less than or equal to #{options[:less_than_or_equal_to]}")
|
|
83
|
-
end
|
|
84
|
-
if options[:equal_to] && num_value != options[:equal_to]
|
|
85
|
-
errors.add(attribute, options[:message] || "must be equal to #{options[:equal_to]}")
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def validates_format_of(*attributes, **options)
|
|
92
|
-
attributes.each { |attribute| validates attribute, format: options }
|
|
44
|
+
validates(*attributes, uniqueness: options.empty? || options)
|
|
93
45
|
end
|
|
94
46
|
end
|
|
95
47
|
end
|
data/lib/active_item/version.rb
CHANGED
data/lib/activeitem.rb
CHANGED
|
@@ -14,6 +14,9 @@ require_relative 'active_item/transaction'
|
|
|
14
14
|
require_relative 'active_item/pagination'
|
|
15
15
|
require_relative 'active_item/base'
|
|
16
16
|
|
|
17
|
+
# ActiveRecord-style ORM for Amazon DynamoDB.
|
|
18
|
+
# Provides model definitions, validations, associations, querying, and
|
|
19
|
+
# pagination backed by the DynamoDB API.
|
|
17
20
|
module ActiveItem
|
|
18
21
|
class << self
|
|
19
22
|
def configuration
|
metadata
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activeitem
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Andy Davis
|
|
8
8
|
- Adam Dalton
|
|
9
|
+
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
+
date: 2026-05-23 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
15
|
+
name: activemodel
|
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
|
16
17
|
requirements:
|
|
17
|
-
- - "
|
|
18
|
+
- - ">="
|
|
18
19
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
20
|
+
version: '7.0'
|
|
20
21
|
type: :runtime
|
|
21
22
|
prerelease: false
|
|
22
23
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
24
|
requirements:
|
|
24
|
-
- - "
|
|
25
|
+
- - ">="
|
|
25
26
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
27
|
+
version: '7.0'
|
|
27
28
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
29
|
+
name: activesupport
|
|
29
30
|
requirement: !ruby/object:Gem::Requirement
|
|
30
31
|
requirements:
|
|
31
32
|
- - ">="
|
|
@@ -39,33 +40,47 @@ dependencies:
|
|
|
39
40
|
- !ruby/object:Gem::Version
|
|
40
41
|
version: '7.0'
|
|
41
42
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
43
|
+
name: aws-sdk-dynamodb
|
|
43
44
|
requirement: !ruby/object:Gem::Requirement
|
|
44
45
|
requirements:
|
|
45
|
-
- - "
|
|
46
|
+
- - "~>"
|
|
46
47
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
48
|
+
version: '1.0'
|
|
48
49
|
type: :runtime
|
|
49
50
|
prerelease: false
|
|
50
51
|
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - "~>"
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '1.0'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: ostruct
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
51
59
|
requirements:
|
|
52
60
|
- - ">="
|
|
53
61
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
62
|
+
version: '0'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
55
70
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
71
|
+
name: parallel_tests
|
|
57
72
|
requirement: !ruby/object:Gem::Requirement
|
|
58
73
|
requirements:
|
|
59
74
|
- - "~>"
|
|
60
75
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
76
|
+
version: '5.0'
|
|
62
77
|
type: :development
|
|
63
78
|
prerelease: false
|
|
64
79
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
80
|
requirements:
|
|
66
81
|
- - "~>"
|
|
67
82
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
83
|
+
version: '5.0'
|
|
69
84
|
- !ruby/object:Gem::Dependency
|
|
70
85
|
name: rake
|
|
71
86
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -80,6 +95,34 @@ dependencies:
|
|
|
80
95
|
- - "~>"
|
|
81
96
|
- !ruby/object:Gem::Version
|
|
82
97
|
version: '13.0'
|
|
98
|
+
- !ruby/object:Gem::Dependency
|
|
99
|
+
name: rspec
|
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - "~>"
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '3.12'
|
|
105
|
+
type: :development
|
|
106
|
+
prerelease: false
|
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - "~>"
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '3.12'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: rubocop
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - "~>"
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '1.0'
|
|
119
|
+
type: :development
|
|
120
|
+
prerelease: false
|
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - "~>"
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '1.0'
|
|
83
126
|
description: A Rails-inspired ORM for DynamoDB with query builder, associations, callbacks,
|
|
84
127
|
dirty tracking, validations, transactions, and pagination.
|
|
85
128
|
email:
|
|
@@ -114,6 +157,8 @@ metadata:
|
|
|
114
157
|
homepage_uri: https://github.com/stowzilla/activeitem
|
|
115
158
|
source_code_uri: https://github.com/stowzilla/activeitem
|
|
116
159
|
changelog_uri: https://github.com/stowzilla/activeitem/blob/main/CHANGELOG.md
|
|
160
|
+
rubygems_mfa_required: 'true'
|
|
161
|
+
post_install_message:
|
|
117
162
|
rdoc_options: []
|
|
118
163
|
require_paths:
|
|
119
164
|
- lib
|
|
@@ -121,14 +166,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
121
166
|
requirements:
|
|
122
167
|
- - ">="
|
|
123
168
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '3.
|
|
169
|
+
version: '3.2'
|
|
125
170
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
171
|
requirements:
|
|
127
172
|
- - ">="
|
|
128
173
|
- !ruby/object:Gem::Version
|
|
129
174
|
version: '0'
|
|
130
175
|
requirements: []
|
|
131
|
-
rubygems_version:
|
|
176
|
+
rubygems_version: 3.5.22
|
|
177
|
+
signing_key:
|
|
132
178
|
specification_version: 4
|
|
133
179
|
summary: ActiveRecord-like ORM for AWS DynamoDB
|
|
134
180
|
test_files: []
|