activejsonmodel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4bc1e18ab1cb9a6672dc6ab19a0a05340f4761b5cb51ef626d36800846f85a5c
4
+ data.tar.gz: 847077d254ef430b85ee5853f8d882e0df84f12b34494d1d47868aa32198989c
5
+ SHA512:
6
+ metadata.gz: 79b717de46de263cc15a03ca0bad9c636bf12458f037078c66b61bcf7c31fbead8106b4506b54731cf44d9a1a267a62e062f900ca37a0d30e79520f9b44e1ee3
7
+ data.tar.gz: 02cdb543f99326a32c4645898cc99bded4e3136a9b4b3e2d9b09f61792f6761db091ea52f6acf52ec8b4a8f0d1ad4575ddfa74e29ba6e561ea25251f3b1d4630
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Ryan Morlok
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # activejsonmodel
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/activejsonmodel.svg)](https://rubygems.org/gems/activejsonmodel)
4
+ [![Circle](https://circleci.com/gh/rmorlok/activejsonmodel/tree/main.svg?style=shield)](https://app.circleci.com/pipelines/github/rmorlok/activejsonmodel?branch=main)
5
+ [![Code Climate](https://codeclimate.com/github/rmorlok/activejsonmodel/badges/gpa.svg)](https://codeclimate.com/github/rmorlok/activejsonmodel)
6
+
7
+ TODO: Description of this gem goes here.
8
+
9
+ ---
10
+
11
+ - [Quick start](#quick-start)
12
+ - [Support](#support)
13
+ - [License](#license)
14
+ - [Code of conduct](#code-of-conduct)
15
+ - [Contribution guide](#contribution-guide)
16
+
17
+ ## Quick start
18
+
19
+ ```
20
+ $ gem install activejsonmodel
21
+ ```
22
+
23
+ ```ruby
24
+ require "active_json_model"
25
+ ```
26
+
27
+ ## Support
28
+
29
+ If you want to report a bug, or have ideas, feedback or questions about the gem, [let me know via GitHub issues](https://github.com/rmorlok/activejsonmodel/issues/new) and I will do my best to provide a helpful answer. Happy hacking!
30
+
31
+ ## License
32
+
33
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
34
+
35
+ ## Code of conduct
36
+
37
+ Everyone interacting in this project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
38
+
39
+ ## Contribution guide
40
+
41
+ Pull requests are welcome!
42
+
43
+ ### Development Setup
44
+
45
+ ```bash
46
+ brew install rbenv
47
+ ```
48
+
49
+ Add the following to `~/.zshrc`:
50
+
51
+ ```bash
52
+ RBENV=`which rbenv`
53
+ if [ $RBENV ] ; then
54
+ export PATH=$HOME/.rbenv/bin:$PATH
55
+ eval "$(rbenv init -)"
56
+ fi
57
+ ```
58
+
59
+ Reload the `~/.zshrc`.
60
+
61
+ Install development ruby version:
62
+
63
+ ```bash
64
+ cat .ruby-version | xargs rbenv install
65
+ ```
66
+
67
+ Exit and re-enter the directory to make sure the current version of ruby is used. Install dependencies:
68
+
69
+ ```bash
70
+ bundle install
71
+ ```
72
+
73
+ ### Running Tests
74
+
75
+ Active JSON Model uses [minitest](https://github.com/minitest/minitest). To run tests, use rake:
76
+
77
+ ```bash
78
+ rake test
79
+ ```
@@ -0,0 +1,8 @@
1
+ require 'active_model'
2
+
3
+ module ActiveJsonModel
4
+ autoload :VERSION, "activejsonmodel/version"
5
+ autoload :Model, "activejsonmodel/model"
6
+ autoload :Array, "activejsonmodel/array"
7
+ autoload :Utils, "activejsonmodel/utils"
8
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Only available if the symmetric-encryption gem is installed
4
+ # https://github.com/reidmorrison/symmetric-encryption
5
+ if Gem.find_files("symmetric-encryption").any? &&
6
+ Gem.find_files("active_record").any?
7
+
8
+ require "symmetric-encryption"
9
+ require 'active_support'
10
+ require_relative './active_record_type'
11
+
12
+ module ActiveJsonModel
13
+ # Allows instances of ActiveJsonModels to be serialized JSONB columns for ActiveRecord models.
14
+ #
15
+ # class Credentials < ::ActiveJsonModel
16
+ # def self.encrypted_attribute_type
17
+ # ActiveRecordEncryptedType.new(Credentials)
18
+ # end
19
+ # end
20
+ #
21
+ # class Integration < ActiveRecord::Base
22
+ # attribute :credentials, Credentials.encrypted_attribute_type
23
+ # end
24
+ class ActiveRecordEncryptedType < ::ActiveJsonModel::ActiveRecordType
25
+ def type
26
+ :string
27
+ end
28
+
29
+ def cast(value)
30
+ if value.is_a?(@clazz)
31
+ value
32
+ elsif value.is_a?(::Array)
33
+ @clazz.load(value)
34
+ end
35
+ end
36
+
37
+ def deserialize(value)
38
+ if String === value
39
+ decoded = SymmetricEncryption.decrypt(value, type: :json) rescue nil
40
+ @clazz.load(decoded)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def serialize(value)
47
+ case value
48
+ when @clazz
49
+ ::ActiveSupport::JSON.encode(@clazz.dump(value))
50
+ when Array, Hash
51
+ ::ActiveSupport::JSON.encode(value)
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ # Override to handle issues comparing hashes encoded as strings, where the actual order doesn't matter.
58
+ def changed_in_place?(raw_old_value, new_value)
59
+ if raw_old_value.nil? || new_value.nil?
60
+ raw_old_value == new_value
61
+ else
62
+ # Decode is necessary because postgres can change the order of hashes. Round-tripping on the new value side
63
+ # is to handle any dates that might be rendered as strings.
64
+ decoded_raw = ::ActiveSupport::JSON.decode(raw_old_value)
65
+ round_tripped_new = ::ActiveSupport::JSON.decode(new_value.class.dump(new_value))
66
+
67
+ decoded_raw != round_tripped_new
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Only available if the active record is installed, generally in a rails environment
4
+ if Gem.find_files("active_record").any?
5
+
6
+ require 'active_record'
7
+ require 'active_support'
8
+
9
+ module ActiveJsonModel
10
+ # Allows instances of ActiveJsonModels to be serialized JSONB columns for ActiveRecord models.
11
+ #
12
+ # class Credentials < ::ActiveJsonModel
13
+ # def self.attribute_type
14
+ # ActiveRecordType.new(Credentials)
15
+ # end
16
+ # end
17
+ #
18
+ # class Integration < ActiveRecord::Base
19
+ # attribute :credentials, Credentials.attribute_type
20
+ # end
21
+ #
22
+ # This is based on:
23
+ # https://jetrockets.pro/blog/rails-5-attributes-api-value-objects-and-jsonb
24
+ class ActiveRecordType < ::ActiveRecord::Type::Value
25
+ include ::ActiveModel::Type::Helpers::Mutable
26
+
27
+ # Create an instance bound to a ActiveJsonModel class.
28
+ #
29
+ # e.g.
30
+ # class Credentials < ::ActiveJsonModel; end
31
+ # #...
32
+ # return ActiveRecordType.new(Credentials)
33
+ def initialize(clazz)
34
+ @clazz = clazz
35
+ end
36
+
37
+ def type
38
+ :jsonb
39
+ end
40
+
41
+ def cast(value)
42
+ if value.is_a?(@clazz)
43
+ value
44
+ elsif value.is_a?(::Array)
45
+ @clazz.load(value)
46
+ end
47
+ end
48
+
49
+ def deserialize(value)
50
+ if String === value
51
+ decoded = ::ActiveSupport::JSON.decode(value) rescue nil
52
+ @clazz.load(decoded)
53
+ else
54
+ super
55
+ end
56
+ end
57
+
58
+ def serialize(value)
59
+ case value
60
+ when @clazz
61
+ ::ActiveSupport::JSON.encode(@clazz.dump(value))
62
+ when Array, Hash
63
+ ::ActiveSupport::JSON.encode(value)
64
+ else
65
+ super
66
+ end
67
+ end
68
+
69
+ # Override to handle issues comparing hashes encoded as strings, where the actual order doesn't matter.
70
+ def changed_in_place?(raw_old_value, new_value)
71
+ if raw_old_value.nil? || new_value.nil?
72
+ raw_old_value == new_value
73
+ else
74
+ # Decode is necessary because postgres can change the order of hashes. Round-tripping on the new value side
75
+ # is to handle any dates that might be rendered as strings.
76
+ decoded_raw = ::ActiveSupport::JSON.decode(raw_old_value)
77
+ round_tripped_new = ::ActiveSupport::JSON.decode(new_value.class.dump(new_value))
78
+
79
+ decoded_raw != round_tripped_new
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveJsonModel
4
+ class AfterLoadCallback
5
+ attr_reader :method_name
6
+ attr_reader :block
7
+
8
+ # Data holder for an after-load callback
9
+ #
10
+ # @param method_name [Symbol] the name of method to be invoked as a callback
11
+ # @param block [Proc] block to be executed as the callback
12
+ def initialize(method_name:, block:)
13
+ raise "ActiveJsonModel after load callback must either be a block or a method name" if method_name && block
14
+ raise "ActiveJsonModel after load callback must either specify a block or method name" unless method_name || block
15
+
16
+ @method_name = method_name&.to_sym
17
+ @block = block
18
+ end
19
+
20
+ # Invoke this callback on <code>on_object</code> the object just loaded from JSON
21
+ #
22
+ # @param on_object [Object] the object just loaded from JSON
23
+ def invoke(on_object)
24
+ if method_name
25
+ on_object.send(method_name)
26
+ else
27
+ if block.arity == 1
28
+ block.call(on_object)
29
+ else
30
+ block.call
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end