activerecord-null 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7bb3c3183d7c9c124c3858a72534649bf8e501f6371942e8a1a06697df1caadf
4
+ data.tar.gz: c280cf8d79aba38ded094dd6140f2ff4f7075b295531326f4e23ecb2df7a4524
5
+ SHA512:
6
+ metadata.gz: c861a1f9cb0cc2685e6a611810521539ae1c6e91bff2864607d44f342380d1822bb9cd7f0ed32b9791d6e52dcc7e8c57b7a7ee01371964b6df25c0b8df83b3a5
7
+ data.tar.gz: 9aa04f050ad57a08a6005b8ac01b27548644a4098e301452a58d3c8f5d66c4d76efc158b5d6cb1347c36ae7906e2b01c00eaba0add423621c4a54963b9687f5e
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2024-10-23
9
+
10
+ ### Added
11
+
12
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jim Gay
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,69 @@
1
+ # ActiveRecord::Null
2
+
3
+ Create null objects for ActiveRecord models.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ $ bundle add activerecord-null
10
+
11
+ If bundler is not being used to manage dependencies, install the gem by executing:
12
+
13
+ $ gem install activerecord-null
14
+
15
+ ## Usage
16
+
17
+ Extend your primary abstract class with `ActiveRecord::Null`.
18
+
19
+ ```ruby
20
+ class ApplicationRecord < ActiveRecord::Base
21
+ primary_abstract_class
22
+ extend ActiveRecord::Null
23
+ end
24
+ ```
25
+
26
+ Define a null object for the model.
27
+
28
+ ```ruby
29
+ class User < ApplicationRecord
30
+ has_many :posts
31
+ Null do
32
+ def name = "None"
33
+ end
34
+ end
35
+ ```
36
+
37
+ This will create a Singleton class `User::Null` that mimics the `User` class.
38
+
39
+ Use the null object.
40
+
41
+ ```ruby
42
+ User.null
43
+ ```
44
+
45
+ It will even work with associations.
46
+
47
+ ```ruby
48
+ User.null.posts # => []
49
+ ```
50
+
51
+ ## Development
52
+
53
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
54
+
55
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
56
+
57
+ ## Releasing
58
+
59
+ Run `rake build:checksum` to build the gem and generate the checksum. This will also update the version number in the gemspec file.
60
+
61
+ Run `rake release` to create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
62
+
63
+ This will leave a new commit with the version number incremented in the version file and the changelog updated with the new version. Push the changes to the repository.
64
+
65
+ ## Contributing
66
+
67
+ This gem is managed with [Reissue](https://github.com/SOFware/reissue).
68
+
69
+ Bug reports and pull requests are welcome on GitHub at https://github.com/SOFware/activerecord-null.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ task default: :test
9
+
10
+ require "reissue/gem"
11
+
12
+ Reissue::Task.create :reissue do |task|
13
+ task.version_file = "lib/activerecord/null/version.rb"
14
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module ActiveRecord
6
+ module Null
7
+ module Mimic
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ def self.mimics(mimic_model_class)
12
+ @mimic_model_class = mimic_model_class
13
+ end
14
+
15
+ def self.mimic_model_class = @mimic_model_class
16
+
17
+ def self.table_name = @mimic_model_class.to_s.tableize
18
+
19
+ def self.primary_key = @mimic_model_class.primary_key
20
+ end
21
+
22
+ def mimic_model_class = self.class.mimic_model_class
23
+
24
+ attr_reader :id
25
+
26
+ def [](*)
27
+ end
28
+
29
+ def is_a?(klass)
30
+ if klass == mimic_model_class
31
+ true
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def destroyed? = false
38
+
39
+ def new_record? = false
40
+
41
+ def persisted? = false
42
+
43
+ def method_missing(method, ...)
44
+ reflections = mimic_model_class.reflect_on_all_associations
45
+ if (reflection = reflections.find { |r| r.name == method })
46
+ case reflection.macro
47
+ when :has_many
48
+ define_memoized_association(method, reflection.klass.none)
49
+ when :has_one, :belongs_to
50
+ define_memoized_association(method, reflection.klass.null)
51
+ else
52
+ super
53
+ end
54
+ else
55
+ super
56
+ end
57
+ end
58
+
59
+ def respond_to_missing?(method, include_private = false)
60
+ mimic_model_class.reflect_on_all_associations.map(&:name).include?(method) || super
61
+ end
62
+
63
+ private
64
+
65
+ def define_memoized_association(method, value)
66
+ define_singleton_method(method) do
67
+ if instance_variable_defined?(:"@#{method}")
68
+ instance_variable_get(:"@#{method}")
69
+ else
70
+ instance_variable_set(:"@#{method}", value)
71
+ end
72
+ end
73
+ value
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Null
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "null/version"
4
+ require_relative "null/mimic"
5
+ module ActiveRecord
6
+ # Extend any ActiveRecord class with this module to add a null object.
7
+ # Add it to your primary abstract class.
8
+ #
9
+ # @example
10
+ # class ApplicationRecord < ActiveRecord::Base
11
+ # primary_abstract_class
12
+ # extend ActiveRecord::Null
13
+ # end
14
+ module Null
15
+ # Define a Null class for the given class.
16
+ #
17
+ # @example
18
+ # class Business
19
+ # Null do
20
+ # def name = "None"
21
+ # end
22
+ # end
23
+ #
24
+ # Business.null # => #<Business::Null:0x0000000000000000>
25
+ #
26
+ def Null(klass = self, &)
27
+ null_class = Class.new do
28
+ include ::ActiveRecord::Null::Mimic
29
+ mimics klass
30
+
31
+ include Singleton
32
+ end
33
+ null_class.class_eval(&)
34
+ const_set(:Null, null_class)
35
+
36
+ define_singleton_method(:null) { null_class.instance }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1 @@
1
+ require_relative "activerecord/null"
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ class ApplicationRecord < ActiveRecord::Base
6
+ primary_abstract_class
7
+ extend ActiveRecord::Null
8
+ end
9
+
10
+ class Business < ApplicationRecord
11
+ Null do
12
+ def name = "None"
13
+ end
14
+ end
15
+
16
+ class Post < ApplicationRecord
17
+ belongs_to :user
18
+ end
19
+
20
+ class User < ApplicationRecord
21
+ belongs_to :business
22
+ has_many :posts
23
+
24
+ Null do
25
+ def name = "None"
26
+ end
27
+ end
28
+
29
+ class ActiveRecord::TestNull < Minitest::Spec
30
+ describe ".null" do
31
+ it "returns a null object" do
32
+ expect(User.null).must_be_instance_of User::Null
33
+ end
34
+ end
35
+
36
+ describe "Null" do
37
+ it "is not persisted" do
38
+ expect(User.null.persisted?).must_equal false
39
+ end
40
+
41
+ it "is not a new record" do
42
+ expect(User.null.new_record?).must_equal false
43
+ end
44
+
45
+ it "is not destroyed" do
46
+ expect(User.null.destroyed?).must_equal false
47
+ end
48
+
49
+ it "has a nil id" do
50
+ expect(User.null.id).must_be_nil
51
+ end
52
+
53
+ it "is a User" do
54
+ expect(User.null.is_a?(User)).must_equal true
55
+ end
56
+
57
+ it "is a User::Null" do
58
+ expect(User.null.is_a?(User::Null)).must_equal true
59
+ end
60
+
61
+ it "has a mimic model class" do
62
+ expect(User.null.mimic_model_class).must_equal User
63
+ end
64
+
65
+ it "responds to associations" do
66
+ expect(User.null.respond_to?(:business)).must_equal true
67
+ end
68
+
69
+ it "has the mimic model class's table name" do
70
+ expect(User::Null.table_name).must_equal "users"
71
+ end
72
+
73
+ it "has the mimic model class's primary key" do
74
+ expect(User::Null.primary_key).must_equal "id"
75
+ end
76
+
77
+ it "has a null belongs_to association" do
78
+ expect(User.null.business).must_be_instance_of Business::Null
79
+ end
80
+
81
+ it "has an empty relation for has_many association" do
82
+ expect(User.null.posts).must_be_kind_of ActiveRecord::Relation
83
+ expect(User.null.posts.to_a).must_equal []
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,15 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :businesses do |t|
3
+ t.string :name
4
+ end
5
+
6
+ create_table :users do |t|
7
+ t.string :name
8
+ t.references :business
9
+ end
10
+
11
+ create_table :posts do |t|
12
+ t.string :title
13
+ t.references :user
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "simplecov" if ENV["CI"]
4
+
5
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
6
+
7
+ require "active_record"
8
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
9
+ load "test/support/schema.rb"
10
+
11
+ require "activerecord/null"
12
+
13
+ require "minitest/autorun"
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-null
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jim Gay
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-10-23 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: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ description: Create Null Objects for ActiveRecord models with automatic support for
28
+ empty associations.
29
+ email:
30
+ - jim@saturnflyer.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - CHANGELOG.md
36
+ - LICENSE.txt
37
+ - README.md
38
+ - Rakefile
39
+ - lib/activerecord-null.rb
40
+ - lib/activerecord/null.rb
41
+ - lib/activerecord/null/mimic.rb
42
+ - lib/activerecord/null/version.rb
43
+ - test/activerecord/test_null.rb
44
+ - test/support/schema.rb
45
+ - test/test_helper.rb
46
+ homepage: https://github.com/SOFware/activerecord-null
47
+ licenses: []
48
+ metadata:
49
+ homepage_uri: https://github.com/SOFware/activerecord-null
50
+ source_code_uri: https://github.com/SOFware/activerecord-null
51
+ changelog_uri: https://github.com/SOFware/activerecord-null/blob/main/CHANGELOG.md
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 3.0.0
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.5.9
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: Null Objects for ActiveRecord
71
+ test_files: []