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.
@@ -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
- existing = record.class.where(**conditions)
18
- existing = existing.reject { |r| r.id == record.id } if record.id
19
- existing = existing.select { |r| options[:conditions].call(r) } if options[:conditions]
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 existing.any?
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? ? true : options)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveItem
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.3'
5
5
  end
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.1
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: 1980-01-02 00:00:00.000000000 Z
12
+ date: 2026-05-23 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: aws-sdk-dynamodb
15
+ name: activemodel
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - "~>"
18
+ - - ">="
18
19
  - !ruby/object:Gem::Version
19
- version: '1.0'
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: '1.0'
27
+ version: '7.0'
27
28
  - !ruby/object:Gem::Dependency
28
- name: activemodel
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: activesupport
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: '7.0'
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: '7.0'
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: rspec
71
+ name: parallel_tests
57
72
  requirement: !ruby/object:Gem::Requirement
58
73
  requirements:
59
74
  - - "~>"
60
75
  - !ruby/object:Gem::Version
61
- version: '3.12'
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: '3.12'
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.1'
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: 4.0.11
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: []