shopify-money 3.2.1 → 3.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d16632a569c75054c496e3d341e83ffb2ce08e25177b0c12a344c9b1c5fa23c2
4
- data.tar.gz: 43d4b4e5f1c8b79c2d366dbad9077056cdd00744a86035ff58931e192b8fbff7
3
+ metadata.gz: 36ae50865f51d78857ad4aadffe1a9acc8976fd248ba736d3e99b69487b5ba8a
4
+ data.tar.gz: 4c8365727256cc699e4f0429e45e85b458234c98a2509773a9c19bae303e9af4
5
5
  SHA512:
6
- metadata.gz: a15a1d6761fd8bed35cedf19ebec19b03aa7a8f10eb03c52004ce6fb166158feab2bba29a41f7fb005cf49339d39361f572c3636e716dd80dbd0a2a702db1476
7
- data.tar.gz: e2b3760497b1d8c4947ae5adea3592a01e625e41d4d916a8155c0d18809358a040e63b897edd5942030ec8652e7e39487adb5b965023b991c8e100fa4f637c73
6
+ metadata.gz: cbc2bac8bffd0456c62d2722108fd0c869a716018b43e99636f161efe72b7bbd000753b256d843d97d29bca34263e514d0f26f6d989b9898339c04fdfed724ee
7
+ data.tar.gz: 0eb55e57aeb3309adae9de14fc6cedad266708fa738809002c4d627751d699c7382062a81703e93476e34b40a5d045ca653c5e9f0761f5acb2f10f0277982bf7
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify-money (3.2.1)
4
+ shopify-money (3.2.3)
5
5
  bigdecimal (>= 3.0)
6
6
 
7
7
  GEM
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'delegate'
4
+ require 'bigdecimal'
4
5
 
5
6
  class Money
6
7
  class Allocator < SimpleDelegator
data/lib/money/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Money
4
- VERSION = "3.2.1"
4
+ VERSION = "3.2.3"
5
5
  end
@@ -74,9 +74,9 @@ module MoneyColumn
74
74
 
75
75
  def compatible_currency?(money, options)
76
76
  currency_column = options[:currency_column]
77
- currency = options[:currency] ||
78
- @money_raw_new_attributes[currency_column.to_sym] ||
79
- try(currency_column)
77
+ currency = options[:currency]
78
+ currency ||= @money_raw_new_attributes[currency_column.to_sym] if @money_raw_new_attributes
79
+ currency ||= try(currency_column)
80
80
 
81
81
  currency.nil? || money.currency.compatible?(Money::Helpers.value_to_currency(currency))
82
82
  end
@@ -222,12 +222,30 @@ RSpec.describe 'MoneyColumn' do
222
222
  end
223
223
 
224
224
  describe 'read_only_currency true' do
225
- it 'does not write the currency to the db' do
225
+ it 'raises CurrencyReadOnlyError when updating price with different currency' do
226
226
  record = MoneyWithReadOnlyCurrency.create
227
227
  record.update_columns(currency: 'USD')
228
228
  expect { record.update(price: Money.new(4, 'CAD')) }.to raise_error(MoneyColumn::CurrencyReadOnlyError)
229
229
  end
230
230
 
231
+ it 'raises CurrencyReadOnlyError when assigning money with different currency' do
232
+ record = MoneyWithReadOnlyCurrency.create(currency: 'USD', price: 1)
233
+ expect { record.price = Money.new(2, 'CAD') }.to raise_error(MoneyColumn::CurrencyReadOnlyError)
234
+ end
235
+
236
+ it 'allows updating price when currency matches existing currency' do
237
+ record = MoneyWithReadOnlyCurrency.create
238
+ record.update_columns(currency: 'USD')
239
+ record.update(price: Money.new(4, 'USD'))
240
+ expect(record.price.value).to eq(4)
241
+ end
242
+
243
+ it 'allows assigning price when currency matches existing currency' do
244
+ record = MoneyWithReadOnlyCurrency.create(currency: 'CAD', price: 1)
245
+ record.price = Money.new(2, 'CAD')
246
+ expect(record.price.value).to eq(2)
247
+ end
248
+
231
249
  it 'legacy_deprecations does not write the currency to the db' do
232
250
  configure(legacy_deprecations: true) do
233
251
  record = MoneyWithReadOnlyCurrency.create
@@ -427,4 +445,533 @@ RSpec.describe 'MoneyColumn' do
427
445
  expect(record.currency).to eq('USD')
428
446
  end
429
447
  end
448
+
449
+ describe 'multiple money columns' do
450
+ it 'handles multiple money columns with different currencies' do
451
+ record = MoneyRecord.create!(
452
+ price: Money.new(100, 'USD'),
453
+ prix: Money.new(200, 'EUR'),
454
+ devise: 'EUR'
455
+ )
456
+ record.reload
457
+ expect(record.price.value).to eq(100)
458
+ expect(record.price.currency.to_s).to eq('USD')
459
+ expect(record.prix.value).to eq(200)
460
+ expect(record.prix.currency.to_s).to eq('EUR')
461
+ # price_usd is calculated from price * RATE (1.17) in before_validation
462
+ expect(record.price_usd.value).to eq(117)
463
+ expect(record.price_usd.currency.to_s).to eq('USD')
464
+ end
465
+
466
+ it 'maintains separate caches for each money column' do
467
+ record = MoneyRecord.new
468
+ record.price = Money.new(100, 'USD')
469
+ record.prix = Money.new(200, 'EUR')
470
+
471
+ expect(record.price).to eq(Money.new(100, 'USD'))
472
+ expect(record.prix).to eq(Money.new(200, 'EUR'))
473
+
474
+ # Verify they're independent by changing one
475
+ record.price = Money.new(300, 'CAD')
476
+ expect(record.price).to eq(Money.new(300, 'CAD'))
477
+ expect(record.prix).to eq(Money.new(200, 'EUR'))
478
+ end
479
+ end
480
+
481
+ describe 'blank money handling' do
482
+ it 'handles empty string as nil' do
483
+ record = MoneyRecord.new(price: '')
484
+ expect(record.price).to be_nil
485
+ end
486
+
487
+ it 'handles whitespace string as nil' do
488
+ record = MoneyRecord.new(price: ' ')
489
+ expect(record.price).to be_nil
490
+ end
491
+
492
+ it 'clears cache when setting to blank' do
493
+ record = MoneyRecord.new(price: Money.new(100, 'USD'))
494
+ expect(record.price).to eq(Money.new(100, 'USD'))
495
+
496
+ record.price = ''
497
+ expect(record.price).to be_nil
498
+
499
+ # Verify the cache was cleared by setting a new value
500
+ record.price = Money.new(200, 'EUR')
501
+ expect(record.price).to eq(Money.new(200, 'EUR'))
502
+ end
503
+ end
504
+
505
+ describe 'currency column cache clearing' do
506
+ it 'clears all money column caches when currency changes' do
507
+ record = MoneyRecord.new(
508
+ price: Money.new(100, 'USD'),
509
+ currency: 'USD'
510
+ )
511
+
512
+ expect(record.price).to eq(Money.new(100, 'USD'))
513
+
514
+ # Change currency should invalidate the cache
515
+ record.currency = 'EUR'
516
+ expect(record.price.currency.to_s).to eq('EUR')
517
+ end
518
+
519
+ it 'only defines currency setter once for shared currency columns' do
520
+ class MoneyWithSharedCurrency < ActiveRecord::Base
521
+ self.table_name = 'money_records'
522
+ money_column :price, currency_column: 'currency'
523
+ money_column :prix, currency_column: 'currency'
524
+ end
525
+
526
+ record = MoneyWithSharedCurrency.new
527
+ methods_count = record.methods.count { |m| m.to_s == 'currency=' }
528
+ expect(methods_count).to eq(1)
529
+ end
530
+ end
531
+
532
+ describe 'no_currency handling' do
533
+ it 'does not write currency when money has no_currency' do
534
+ record = MoneyRecord.create!(currency: 'USD')
535
+ record.price = Money.new(100, Money::NULL_CURRENCY)
536
+ record.save!
537
+ record.reload
538
+ expect(record.currency).to eq('USD')
539
+ end
540
+ end
541
+
542
+ describe 'edge cases' do
543
+ it 'handles BigDecimal values' do
544
+ record = MoneyRecord.new(price: BigDecimal('123.45'))
545
+ expect(record.price.value).to eq(123.45)
546
+ end
547
+
548
+ it 'handles negative values' do
549
+ record = MoneyRecord.new(price: Money.new(-100, 'USD'))
550
+ record.save!
551
+ record.reload
552
+ expect(record.price.value).to eq(-100)
553
+ expect(record.price.currency.to_s).to eq('USD')
554
+ end
555
+
556
+ it 'handles very large values' do
557
+ large_value = BigDecimal('999999999999999.999')
558
+ record = MoneyRecord.new(price: Money.new(large_value, 'USD'))
559
+ record.save!
560
+ record.reload
561
+ # Database might round very large values
562
+ expect(record.price.value).to be_within(0.001).of(large_value)
563
+ end
564
+
565
+ it 'handles zero values' do
566
+ record = MoneyRecord.new(price: Money.new(0, 'USD'))
567
+ record.save!
568
+ record.reload
569
+ expect(record.price.value).to eq(0)
570
+ expect(record.price.currency.to_s).to eq('USD')
571
+ end
572
+ end
573
+
574
+ describe 'ActiveRecord callbacks integration' do
575
+ class MoneyWithCallbacks < ActiveRecord::Base
576
+ self.table_name = 'money_records'
577
+ money_column :price, currency_column: 'currency'
578
+
579
+ before_save :double_price
580
+
581
+ private
582
+
583
+ def double_price
584
+ self.price = price * 2 if price
585
+ end
586
+ end
587
+
588
+ it 'works with before_save callbacks' do
589
+ record = MoneyWithCallbacks.new(price: Money.new(50, 'USD'))
590
+ record.save!
591
+ expect(record.price.value).to eq(100)
592
+ end
593
+ end
594
+
595
+ describe 'validation integration' do
596
+ class MoneyWithCustomValidation < ActiveRecord::Base
597
+ self.table_name = 'money_records'
598
+ money_column :price, currency_column: 'currency'
599
+
600
+ validate :price_must_be_positive
601
+
602
+ private
603
+
604
+ def price_must_be_positive
605
+ errors.add(:price, 'must be positive') if price && price.value < 0
606
+ end
607
+ end
608
+
609
+ it 'works with custom validations' do
610
+ record = MoneyWithCustomValidation.new(price: Money.new(-10, 'USD'))
611
+ expect(record).not_to be_valid
612
+ expect(record.errors[:price]).to include('must be positive')
613
+ end
614
+
615
+ it 'allows valid values' do
616
+ record = MoneyWithCustomValidation.new(price: Money.new(10, 'USD'))
617
+ expect(record).to be_valid
618
+ end
619
+ end
620
+
621
+ describe 'ActiveRecord query interface' do
622
+ before do
623
+ MoneyRecord.delete_all
624
+ MoneyRecord.create!(price: Money.new(100, 'USD'), currency: 'USD')
625
+ MoneyRecord.create!(price: Money.new(200, 'USD'), currency: 'USD')
626
+ MoneyRecord.create!(price: Money.new(150, 'EUR'), currency: 'EUR')
627
+ end
628
+
629
+ it 'supports where queries with money values' do
630
+ records = MoneyRecord.where(price: 100)
631
+ expect(records.count).to eq(1)
632
+ expect(records.first.price.value).to eq(100)
633
+ end
634
+
635
+ it 'supports range queries' do
636
+ records = MoneyRecord.where(price: 100..200)
637
+ expect(records.count).to eq(3)
638
+ end
639
+
640
+ it 'supports ordering by money columns' do
641
+ records = MoneyRecord.order(:price)
642
+ expect(records.map { |r| r.price.value }).to eq([100, 150, 200])
643
+ end
644
+
645
+ it 'supports pluck with money columns' do
646
+ values = MoneyRecord.pluck(:price)
647
+ expect(values).to contain_exactly(100, 200, 150)
648
+ end
649
+ end
650
+
651
+ describe 'thread safety' do
652
+ it 'maintains separate caches per instance' do
653
+ record1 = MoneyRecord.new
654
+ record2 = MoneyRecord.new
655
+
656
+ record1.price = Money.new(100, 'USD')
657
+ record2.price = Money.new(200, 'EUR')
658
+
659
+ expect(record1.price).to eq(Money.new(100, 'USD'))
660
+ expect(record2.price).to eq(Money.new(200, 'EUR'))
661
+ end
662
+ end
663
+
664
+ describe 'attribute assignment' do
665
+ it 'handles hash assignment with string keys' do
666
+ record = MoneyRecord.new('price' => 100, 'currency' => 'USD')
667
+ expect(record.price.value).to eq(100)
668
+ expect(record.price.currency.to_s).to eq('USD')
669
+ end
670
+
671
+ it 'handles hash assignment with symbol keys' do
672
+ record = MoneyRecord.new(price: 100, currency: 'USD')
673
+ expect(record.price.value).to eq(100)
674
+ expect(record.price.currency.to_s).to eq('USD')
675
+ end
676
+
677
+ it 'handles update_attributes' do
678
+ record = MoneyRecord.create!(price: Money.new(100, 'USD'))
679
+ record.update!(price: Money.new(200, 'EUR'))
680
+ expect(record.price.value).to eq(200)
681
+ expect(record.price.currency.to_s).to eq('EUR')
682
+ end
683
+ end
684
+
685
+ describe 'error handling' do
686
+ it 'provides helpful error message for invalid currency in money object' do
687
+ expect {
688
+ MoneyRecord.new(price: Money.new(100, 'INVALID'))
689
+ }.to raise_error(Money::Currency::UnknownCurrency)
690
+ end
691
+
692
+ it 'handles non-numeric string values' do
693
+ expect {
694
+ MoneyRecord.new(price: 'not a number')
695
+ }.to raise_error(ArgumentError)
696
+ end
697
+ end
698
+
699
+ describe 'coerce_null with different scenarios' do
700
+ it 'coerces nil to zero money with proper currency from column' do
701
+ record = MoneyRecordCoerceNull.new(currency: 'EUR')
702
+ expect(record.price.value).to eq(0)
703
+ expect(record.price.currency.to_s).to eq('EUR')
704
+ end
705
+
706
+ it 'coerces nil to zero money with hardcoded currency' do
707
+ record = MoneyRecordCoerceNull.new
708
+ expect(record.price_usd.value).to eq(0)
709
+ expect(record.price_usd.currency.to_s).to eq('USD')
710
+ end
711
+
712
+ it 'does not coerce non-nil values' do
713
+ record = MoneyRecordCoerceNull.new(price: Money.new(100, 'USD'))
714
+ expect(record.price.value).to eq(100)
715
+ end
716
+ end
717
+
718
+ describe 'currency_read_only with edge cases' do
719
+ it 'allows setting money when currency column is nil' do
720
+ record = MoneyWithReadOnlyCurrency.new
721
+ record.price = Money.new(100, 'USD')
722
+ expect(record.price.value).to eq(100)
723
+ # Currency is not written for read_only columns when not saved
724
+ expect(record.currency).to be_nil
725
+ end
726
+
727
+ it 'allows setting money with compatible currency using string' do
728
+ record = MoneyWithReadOnlyCurrency.create!(currency: 'USD')
729
+ record.price = Money.new(100, 'USD')
730
+ expect(record.price.value).to eq(100)
731
+ end
732
+ end
733
+
734
+ describe 'initialize_dup behavior' do
735
+ it 'creates independent cache for duplicated record' do
736
+ original = MoneyRecord.new(price: Money.new(100, 'USD'))
737
+ duplicate = original.dup
738
+
739
+ duplicate.price = Money.new(200, 'EUR')
740
+
741
+ expect(original.price).to eq(Money.new(100, 'USD'))
742
+ expect(duplicate.price).to eq(Money.new(200, 'EUR'))
743
+ end
744
+
745
+ it 'preserves money values when duplicating' do
746
+ original = MoneyRecord.create!(
747
+ price: Money.new(100, 'USD'),
748
+ prix: Money.new(200, 'EUR')
749
+ )
750
+
751
+ duplicate = original.dup
752
+ expect(duplicate.price).to eq(Money.new(100, 'USD'))
753
+ expect(duplicate.prix).to eq(Money.new(200, 'EUR'))
754
+ expect(duplicate).to be_new_record
755
+ end
756
+ end
757
+
758
+ describe 'ActiveRecord dirty tracking' do
759
+ it 'tracks changes to money columns' do
760
+ record = MoneyRecord.create!(price: Money.new(100, 'USD'))
761
+ record.price = Money.new(200, 'USD')
762
+
763
+ expect(record.price_changed?).to be true
764
+ expect(record.price_was).to eq(100)
765
+ expect(record.price_change).to eq([100, 200])
766
+ end
767
+
768
+ it 'tracks currency changes' do
769
+ record = MoneyRecord.create!(currency: 'USD', price: 100)
770
+ record.currency = 'EUR'
771
+
772
+ expect(record.currency_changed?).to be true
773
+ expect(record.currency_was).to eq('USD')
774
+ end
775
+ end
776
+
777
+ describe 'mass assignment with currency updates' do
778
+ it 'handles simultaneous updates of money and currency in mass assignment' do
779
+ record = MoneyWithReadOnlyCurrency.create!(currency: 'USD', price: 100)
780
+
781
+ record.assign_attributes(
782
+ currency: 'EUR',
783
+ price: Money.new(200, 'EUR')
784
+ )
785
+
786
+ expect { record.save! }.not_to raise_error
787
+ expect(record.price.value).to eq(200)
788
+ expect(record.currency).to eq('EUR')
789
+ end
790
+ end
791
+
792
+ describe 'decimal precision handling' do
793
+ it 'preserves precision up to currency minor units' do
794
+ # USD has 2 minor units, so 123.456 will be rounded to 123.46
795
+ record = MoneyRecord.create!(price: Money.new(123.456, 'USD'))
796
+ record.reload
797
+ expect(record.price.value.to_f).to eq(123.46)
798
+ end
799
+
800
+ it 'preserves full precision for currencies with 3 decimal places' do
801
+ # JOD has 3 minor units, so it preserves 3 decimal places
802
+ record = MoneyRecord.create!(price: Money.new(123.456, 'JOD'), currency: 'JOD')
803
+ record.reload
804
+ expect(record.price.value).to eq(123.456)
805
+ end
806
+
807
+ it 'rounds database values beyond 3 decimal places' do
808
+ record = MoneyRecord.new
809
+ record['price'] = 123.4567
810
+ record.currency = 'USD'
811
+ record.save!
812
+ record.reload
813
+ expect(record['price'].to_f.round(3)).to eq(123.457)
814
+ end
815
+ end
816
+
817
+ describe 'ActiveRecord Type integration' do
818
+ it 'uses MoneyColumn::ActiveRecordType for money columns' do
819
+ type = MoneyRecord.attribute_types['price']
820
+ expect(type).to be_a(MoneyColumn::ActiveRecordType)
821
+ end
822
+ end
823
+
824
+ describe 'money column options inheritance' do
825
+ it 'does not share options between different models' do
826
+ class MoneyModel1 < ActiveRecord::Base
827
+ self.table_name = 'money_records'
828
+ money_column :price, currency_column: 'currency'
829
+ end
830
+
831
+ class MoneyModel2 < ActiveRecord::Base
832
+ self.table_name = 'money_records'
833
+ money_column :price, currency: 'EUR'
834
+ end
835
+
836
+ expect(MoneyModel1.money_column_options['price'][:currency_column]).to eq('currency')
837
+ expect(MoneyModel1.money_column_options['price'][:currency]).to be_nil
838
+
839
+ expect(MoneyModel2.money_column_options['price'][:currency]).to eq('EUR')
840
+ expect(MoneyModel2.money_column_options['price'][:currency_column]).to be_nil
841
+ end
842
+ end
843
+
844
+ describe 'raw attributes access' do
845
+ it 'allows direct access to raw decimal value' do
846
+ record = MoneyRecord.create!(price: Money.new(123.45, 'USD'))
847
+ expect(record['price']).to eq(123.45)
848
+ expect(record.read_attribute(:price)).to eq(123.45)
849
+ end
850
+
851
+ it 'allows direct writing of raw decimal value' do
852
+ record = MoneyRecord.new
853
+ record['price'] = 99.99
854
+ record.currency = 'EUR'
855
+ expect(record.price.value).to eq(99.99)
856
+ expect(record.price.currency.to_s).to eq('EUR')
857
+ end
858
+ end
859
+
860
+ describe 'nil handling' do
861
+ it 'returns Money with default currency for zero values' do
862
+ record = MoneyRecord.new
863
+ # The default value in the schema is 0.000, not nil
864
+ expect(record['price']).to eq(0)
865
+ # With default currency CAD, it returns Money with 0 value
866
+ expect(record.price).to eq(Money.new(0, 'CAD'))
867
+ end
868
+
869
+ it 'returns nil when value is explicitly nil' do
870
+ record = MoneyRecord.new
871
+ record['price'] = nil
872
+ expect(record.price).to be_nil
873
+ end
874
+
875
+ it 'handles nil assignment' do
876
+ record = MoneyRecord.create!(price: Money.new(100, 'USD'))
877
+ record.price = nil
878
+ record.save!
879
+ record.reload
880
+ expect(record.price).to be_nil
881
+ end
882
+ end
883
+
884
+ describe 'currency normalization' do
885
+ it 'normalizes currency strings to uppercase' do
886
+ record = MoneyRecord.new(price: Money.new(100, 'usd'))
887
+ expect(record.price.currency.to_s).to eq('USD')
888
+ end
889
+
890
+ it 'freezes currency strings for performance' do
891
+ class MoneyWithFrozenCurrency < ActiveRecord::Base
892
+ self.table_name = 'money_records'
893
+ money_column :price, currency: 'USD'
894
+ end
895
+
896
+ expect(MoneyWithFrozenCurrency.money_column_options['price'][:currency]).to be_frozen
897
+ end
898
+ end
899
+
900
+ describe 'error messages' do
901
+ it 'provides clear error for missing currency when default_currency is nil' do
902
+ configure(default_currency: nil) do
903
+ record = MoneyRecord.create!(price: 100, currency: nil)
904
+ expect { record.reload.price }.to raise_error(ArgumentError, 'missing currency')
905
+ end
906
+ end
907
+ end
908
+
909
+ describe 'money column with different column names' do
910
+ class MoneyWithCustomColumns < ActiveRecord::Base
911
+ self.table_name = 'money_records'
912
+ money_column :price, currency_column: :devise
913
+ money_column :prix, currency_column: 'currency'
914
+ end
915
+
916
+ it 'supports both string and symbol currency column names' do
917
+ record = MoneyWithCustomColumns.new(
918
+ price: Money.new(100, 'EUR'),
919
+ devise: 'EUR',
920
+ prix: Money.new(200, 'USD'),
921
+ currency: 'USD'
922
+ )
923
+
924
+ expect(record.price.currency.to_s).to eq('EUR')
925
+ expect(record.prix.currency.to_s).to eq('USD')
926
+ end
927
+ end
928
+
929
+ describe 'money column array syntax' do
930
+ class MoneyWithArrayColumns < ActiveRecord::Base
931
+ self.table_name = 'money_records'
932
+ money_column [:price, :prix], currency_column: 'currency'
933
+ end
934
+
935
+ it 'supports defining multiple columns at once' do
936
+ record = MoneyWithArrayColumns.new(
937
+ price: Money.new(100, 'USD'),
938
+ prix: Money.new(200, 'USD'),
939
+ currency: 'USD'
940
+ )
941
+
942
+ expect(record.price).to eq(Money.new(100, 'USD'))
943
+ expect(record.prix).to eq(Money.new(200, 'USD'))
944
+ end
945
+ end
946
+
947
+ describe 'ActiveRecord scopes' do
948
+ it 'works with ActiveRecord scopes' do
949
+ MoneyRecord.delete_all
950
+ cheap = MoneyRecord.create!(price: Money.new(10, 'USD'))
951
+ expensive = MoneyRecord.create!(price: Money.new(100, 'USD'))
952
+
953
+ scope = MoneyRecord.where('price < ?', 50)
954
+ expect(scope.to_a).to eq([cheap])
955
+ end
956
+ end
957
+
958
+ describe 'JSON serialization' do
959
+ it 'includes money values in as_json' do
960
+ record = MoneyRecord.new(price: Money.new(100, 'USD'))
961
+ json = record.as_json
962
+ # Money columns are serialized as a hash with symbol keys
963
+ expect(json['price']).to eq({ currency: 'USD', value: '100.00' })
964
+ expect(json['currency']).to eq('USD')
965
+ end
966
+ end
967
+
968
+ describe 'update_columns behavior' do
969
+ it 'bypasses money column methods when using update_columns' do
970
+ record = MoneyRecord.create!(price: Money.new(100, 'USD'))
971
+ record.update_columns(price: 200)
972
+ record.reload
973
+ expect(record.price.value).to eq(200)
974
+ expect(record.price.currency.to_s).to eq('USD')
975
+ end
976
+ end
430
977
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify-money
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc