Created
July 9, 2018 16:19
-
-
Save joegaudet/2bcf185ebd4c3d7bff71ebc6e4896606 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module Values | |
| module Support | |
| def self.included(base) | |
| base.extend(ClassMethods) | |
| end | |
| module ClassMethods | |
| # Defines a new value_attribute | |
| # | |
| # @usage value_attribute :value_menu, Values::Menus::Menu, builder: :from_ar_menu | |
| # | |
| # @param [Symbol] value_name | |
| # @param [Class] value_class | |
| # @param [Object] options | |
| # @!macro [attach] value_attribute | |
| # @return [$2] the $1 $0 | |
| def value_attribute(value_name, value_class, options = {}) | |
| builder = options[:builder] | |
| unless value_class < ::Values::Base | |
| raise ArgumentError.new("You tried to define value accessors for #{value_name} using #{value_class} which does not descend from Values::Base") | |
| end | |
| # Defines a setter method that converts your virtus struct into a hash that | |
| # can be stored in a jsonb column | |
| define_method(:"#{value_name}=") do |value| | |
| if value.nil? | |
| super(value) | |
| else | |
| # If a builder has been provided we can call a method provided on the class | |
| # which will take the provided value and convert it into the value class | |
| if builder | |
| value = value_class.send(builder, value) | |
| end | |
| unless value.is_a?(value_class) | |
| raise ArgumentError.new("You tried to set #{value_name} to an instance of #{value.class.name} when it only accepts #{value_class}") | |
| end | |
| super(value.to_hash) | |
| end | |
| end | |
| # defines a getter for the value which gets the jsonb field | |
| # and wraps it in your model | |
| define_method(value_name) do | |
| value = super() | |
| value.present? ? value_class.new(value) : nil | |
| end | |
| end | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require "test_helper" | |
| module Values | |
| class SupportTest < ActiveSupport::TestCase | |
| class AValueObject < Values::Base | |
| values do | |
| attribute :foo, String | |
| attribute :bar, String | |
| end | |
| end | |
| # The value_attribute macro works by calling | |
| # super on active record accessor models, so we need | |
| # to provide those. | |
| class PoroBase | |
| attr_accessor :test | |
| def foo=(hash) | |
| @test = hash | |
| end | |
| def foo | |
| @test | |
| end | |
| end | |
| class Poro < PoroBase | |
| include Values::Support | |
| value_attribute :foo, AValueObject | |
| end | |
| def test_assignment_enforces_type | |
| poro = Poro.new | |
| err = -> { | |
| poro.foo = {} | |
| }.must_raise ArgumentError | |
| err.message.must_match /You tried to set foo to/ | |
| end | |
| def test_assignment_extracts_a_hash | |
| poro = Poro.new | |
| foo = Faker::Lorem.word | |
| bar = Faker::Lorem.word | |
| poro.foo = AValueObject.new(foo: foo, bar: bar) | |
| assert_equal poro.test, {foo: foo, bar: bar} | |
| end | |
| def test_access_restores_from_a_hash | |
| poro = Poro.new | |
| foo = Faker::Lorem.word | |
| bar = Faker::Lorem.word | |
| poro.test = {foo: foo, bar: bar} | |
| assert_equal poro.foo.foo, foo | |
| assert_equal poro.foo.bar, bar | |
| end | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment