protip 0.18.5 → 0.18.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8aa9bb432c7fae05ac9e7f790545b1f50868bba1
4
- data.tar.gz: d1abe3cf1d8a52ca3a13002ec89e520831cc8ed6
3
+ metadata.gz: a9c25de6d9f409c5bb8c1659951ecc4fa10a5aa4
4
+ data.tar.gz: aab7d4182c10f0a540dd5762ea3921dac899c2b1
5
5
  SHA512:
6
- metadata.gz: 207674ca4d193805cab007b1ec73303d3ec3701186f3471245cd86f366fd6d967b25ad44982d02fcc5e0b44e1257e7bac93501db953de5acc1ae2047b5ca079a
7
- data.tar.gz: 136aca0f48b4d564370bfe6674758ea8e67d2c32b29c2cc9d922e240d70cf9ccd4400ca590a2aed8eaa234a7d9a8bcd318a49cbcc1686b514d38bfb485391750
6
+ metadata.gz: 530e2c08bf013f25afea7d4be6eb254b85b192fb8ca374ceb09ef48ce3e9e703926ca07335bac5396edcacc129f29710cb2cb9e1b619508210ba39c8170335f9
7
+ data.tar.gz: d65a08e07c0365afadfdf614f731bcf5939deed2e463f4e3d4159f70b0691e1d9735567f33dd1edcf0d7c893f919a128f01e0e634aceec33c4761422aa502e81
@@ -132,12 +132,13 @@ module Protip
132
132
  end
133
133
 
134
134
  define_method "#{field.name}=" do |new_value|
135
- old_wrapped_value = @wrapper.send(field.name)
135
+ old_value = self.message[field.name] # Only compare the raw values
136
136
  @wrapper.send("#{field.name}=", new_value)
137
- new_wrapped_value = @wrapper.send(field.name)
137
+ new_value = self.message[field.name]
138
138
 
139
- # needed for ActiveModel::Dirty
140
- send("#{field.name}_will_change!") if new_wrapped_value != old_wrapped_value
139
+ # Need to check that types are the same first, otherwise protobuf gets mad comparing
140
+ # messages with non-messages
141
+ send("#{field.name}_will_change!") unless new_value.class == old_value.class && new_value == old_value
141
142
  end
142
143
 
143
144
  # needed for ActiveModel::Dirty
@@ -263,8 +264,23 @@ module Protip
263
264
  end
264
265
 
265
266
  def assign_attributes(attributes)
266
- # the resource needs to call its own setters so that fields get marked as dirty
267
- attributes.each { |field_name, value| send("#{field_name}=", value) }
267
+ old_attributes = {}
268
+ descriptor = message.class.descriptor
269
+ attributes.keys.each do |key|
270
+ field = descriptor.lookup(key.to_s)
271
+ value = message[key.to_s]
272
+ # If the current value is a message, we need to clone it to get a reasonable comparison later,
273
+ # since we might just assign attributes to the current instance of the message directly
274
+ old_attributes[key] = field && field.type == :message && value ? value.clone : value
275
+ end
276
+ @wrapper.assign_attributes attributes
277
+ attributes.keys.each do |key|
278
+ old_value = old_attributes[key]
279
+ new_value = message[key.to_s]
280
+ unless old_value.class == new_value.class && old_value == new_value
281
+ send "#{key}_will_change!"
282
+ end
283
+ end
268
284
  nil # return nil to match ActiveRecord behavior
269
285
  end
270
286
 
@@ -115,17 +115,11 @@ module Protip
115
115
  field = message.class.descriptor.lookup(field_name.to_s) ||
116
116
  (raise ArgumentError.new("Unrecognized field: #{field_name}"))
117
117
 
118
- # For inconvertible nested messages, the value should be either a hash or a message
119
- if field.type == :message && !converter.convertible?(field.subtype.msgclass)
120
- if value.is_a?(field.subtype.msgclass) # If a message, set it directly
121
- set(field, value)
122
- elsif value.is_a?(Hash) # If a hash, pass it through to the nested message
123
- wrapper = get(field) || build(field.name) # Create the field if it doesn't already exist
124
- wrapper.assign_attributes value
125
- else # If value is a simple type (e.g. nil), set the value directly
126
- set(field, value)
127
- end
128
- # Otherwise, if the field is a convertible message or a simple type, we set the value directly
118
+ # For inconvertible nested messages, we allow a hash to be passed in with nested attributes
119
+ if field.type == :message && !converter.convertible?(field.subtype.msgclass) && value.is_a?(Hash)
120
+ wrapper = get(field) || build(field.name) # Create the field if it doesn't already exist
121
+ wrapper.assign_attributes value
122
+ # Otherwise, if the field is a message (convertible or not) or a simple type, we set the value directly
129
123
  else
130
124
  set(field, value)
131
125
  end
@@ -224,7 +218,7 @@ module Protip
224
218
  elsif nested_resources.has_key?(field.name.to_sym)
225
219
  value.message
226
220
  else
227
- raise ArgumentError.new "Cannot convert from Ruby object: \"#{field}\""
221
+ raise ArgumentError.new "Cannot convert from Ruby object: \"#{field.name}\""
228
222
  end
229
223
  elsif field.type == :enum
230
224
  value.is_a?(Fixnum) ? value : value.to_sym
@@ -515,9 +515,97 @@ module Protip::ResourceTest # Namespace for internal constants
515
515
  attrs = {id: 2}
516
516
  assert_equal resource_message_class.new(attrs), resource_class.new(attrs).message
517
517
  end
518
+
519
+ it 'allows nested attributes to be given' do
520
+ attrs = {
521
+ nested_message: {
522
+ number: 3
523
+ }
524
+ }
525
+ assert_equal nested_message_class.new(number: 3), resource_class.new(attrs).message.nested_message
526
+ end
518
527
  end
519
528
  end
520
529
 
530
+ # Shared behavior for direct setters and #assign_attributes. The setter object must be able to be
531
+ # initialized as +setter_class.new(resource)+, and must respond to +setter.set({field => value, field2 => value2})+
532
+ # by performing the appropriate operation (e.g. +.field=+ or +.assign_attributes+).
533
+ def self.describe_dirty_attributes_setter(setter_class)
534
+
535
+ describe 'dirty attributes' do
536
+ let :converter do
537
+ Class.new do
538
+ include Protip::Converter
539
+ end.new
540
+ end
541
+
542
+ let :resource do
543
+ resource_class.new resource_message_class.new({
544
+ string: 'foo',
545
+ nested_message: nested_message_class.new(number: 32)
546
+ })
547
+ end
548
+
549
+ let :setter do
550
+ setter_class.new(resource)
551
+ end
552
+
553
+ before do
554
+ resource_class.converter = converter
555
+ raise 'sanity check failed' if resource.changed? || resource.string_changed? || resource.nested_message_changed?
556
+ end
557
+
558
+ it 'recognizes changes in scalar values' do
559
+ setter.set string: 'bar'
560
+ assert resource.changed?, 'resource was not marked as changed'
561
+ assert resource.string_changed?, 'field was not marked as changed'
562
+ end
563
+
564
+ it 'recognizes when scalar values do not change' do
565
+ setter.set string: 'foo'
566
+ refute resource.changed?, 'resource was marked as changed'
567
+ refute resource.string_changed?, 'field was marked as changed'
568
+ end
569
+
570
+ describe '(message attributes)' do
571
+ before do
572
+ converter.stubs(:convertible?).with(nested_message_class).returns(true)
573
+ converter.stubs(:to_message).with(42, nested_message_class).returns(nested_message_class.new(number: 52))
574
+ converter.stubs(:to_object).with(nested_message_class.new(number: 52)).returns(42)
575
+ converter.stubs(:to_object).with(nested_message_class.new(number: 62)).returns(72)
576
+ end
577
+ it 'marks convertible messages as changed if they are changed as Ruby values' do
578
+ setter.set nested_message: 42
579
+ assert resource.changed?, 'resource was not marked as changed'
580
+ assert resource.nested_message_changed?, 'field was not marked as changed'
581
+ end
582
+ it 'marks sub-messages as changed if they are changed as messages' do
583
+ setter.set nested_message: nested_message_class.new(number: 62)
584
+ assert resource.changed?, 'resource was not marked as changed'
585
+ assert resource.nested_message_changed?, 'field was not marked as changed'
586
+ end
587
+ it 'marks sub-messages as changed when they are nullified' do
588
+ setter.set nested_message: nil
589
+ assert resource.changed?, 'resource was not marked as changed'
590
+ assert resource.nested_message_changed?, 'field was not marked as changed'
591
+ end
592
+ it 'recognizes when convertible messages are not changed when set as Ruby values' do
593
+ resource.message.nested_message.number = 52
594
+ raise 'sanity check failed' if resource.changed? || resource.nested_message_changed?
595
+ setter.set nested_message: 42
596
+ refute resource.changed?, 'resource was marked as changed'
597
+ refute resource.string_changed?, 'field was marked as changed'
598
+ end
599
+ it 'recognizes when sub-messages are not changed when set as messages' do
600
+ setter.set nested_message: nested_message_class.new(number: 32)
601
+ refute resource.changed?, 'resource was marked as changed'
602
+ refute resource.string_changed?, 'field was marked as changed'
603
+ end
604
+ end
605
+ end
606
+ end
607
+
608
+
521
609
  describe 'attribute writer' do
522
610
  before do
523
611
  resource_class.class_exec(resource_message_class) do |resource_message_class|
@@ -532,24 +620,11 @@ module Protip::ResourceTest # Namespace for internal constants
532
620
  resource.string = test_string
533
621
  end
534
622
 
535
- it 'marks the resource and attribute as changed if the value is changed' do
536
- resource = resource_class.new string: 'original'
537
- resource.string = 'new'
538
- assert resource.changed?, 'resource should be marked as changed'
539
- assert resource.string_changed?, 'string field should be marked as changed'
540
- end
541
-
542
- it 'does not mark the resource and attribute as changed if the value is not changed' do
543
- resource = resource_class.new string: 'original'
544
- resource.send :changes_applied # clear the changes
545
- # establish that the changes were cleared
546
- assert !resource.changed?, 'resource should be not marked as changed'
547
- assert !resource.string_changed?, 'string field should not be marked as changed'
548
-
549
- resource.string = 'original'
550
- assert !resource.changed?, 'resource should be not marked as changed'
551
- assert !resource.string_changed?, 'string field should not be marked as changed'
623
+ setter_class = Class.new do
624
+ def initialize(resource) ; @resource = resource ; end
625
+ def set(attributes) ; attributes.each{|attribute, value| @resource.public_send(:"#{attribute}=", value)} ; end
552
626
  end
627
+ describe_dirty_attributes_setter(setter_class)
553
628
  end
554
629
 
555
630
  describe '#assign_attributes' do
@@ -559,15 +634,55 @@ module Protip::ResourceTest # Namespace for internal constants
559
634
  end
560
635
  end
561
636
 
562
- it 'calls the attribute writer for each attribute' do
563
- resource = resource_class.new
564
- test_string = 'whodunnit'
565
- resource.expects(:string=).with(test_string)
566
- resource.assign_attributes(string: test_string)
637
+ let :resource do
638
+ resource_class.new
639
+ end
640
+
641
+ it 'delegates to #assign_attributes on the wrapper' do
642
+ # Instantiate the resource before setting the expectation, since assign_attributes is allowed to be
643
+ # called during #initialize as well
644
+ resource
645
+
646
+ Protip::Wrapper.any_instance.expects(:assign_attributes).with(string: 'foo')
647
+ resource.assign_attributes string: 'foo'
648
+ end
649
+
650
+ setter_class = Class.new do
651
+ def initialize(resource) ; @resource = resource ; end
652
+ def set(attributes) ; @resource.assign_attributes attributes ; end
653
+ end
654
+ describe_dirty_attributes_setter setter_class
655
+ describe 'dirty attributes (nested hashes)' do
656
+
657
+ it 'marks nested hashes as changed if they set a new field' do
658
+ resource.assign_attributes nested_message: {number: 52}
659
+ assert resource.changed?, 'resource was not marked as changed'
660
+ assert resource.nested_message_changed?, 'field was not marked as changed'
661
+ end
662
+
663
+ describe '(when a nested message has an initial value)' do
664
+ before do
665
+ resource.nested_message = nested_message_class.new(number: 32)
666
+ resource.send(:changes_applied) # Clear the list of changes
667
+ # Sanity check
668
+ raise 'unexpected' if resource.changed? || resource.string_changed? || resource.nested_message_changed?
669
+ end
670
+
671
+ it 'marks nested hashes as changed if they change a field' do
672
+ resource.assign_attributes nested_message: {number: 42}
673
+ assert resource.changed?, 'resource was not marked as changed'
674
+ assert resource.nested_message_changed?, 'field was not marked as changed'
675
+ end
676
+
677
+ it 'does not mark nested hashes as changed if they do not change the underlying message' do
678
+ resource.assign_attributes nested_message: {number: 32}
679
+ refute resource.changed?, 'resource was marked as changed'
680
+ refute resource.nested_message_changed?, 'field was marked as changed'
681
+ end
682
+ end
567
683
  end
568
684
 
569
685
  it 'returns nil' do
570
- resource = resource_class.new
571
686
  assert_nil resource.assign_attributes(string: 'asdf')
572
687
  end
573
688
  end
@@ -255,7 +255,7 @@ module Protip::WrapperTest # namespace for internal constants
255
255
  wrapper.assign_attributes inner: {value: 50, note: 'noted'}
256
256
  end
257
257
 
258
- it "sets message fields to nil when they're assigned nil" do
258
+ it 'sets message fields to nil when they\'re assigned nil' do
259
259
  wrapped_message.inner = inner_message_class.new(value: 60)
260
260
  assert wrapped_message.inner
261
261
  wrapper.assign_attributes inner: nil
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protip
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.5
4
+ version: 0.18.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - AngelList
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-24 00:00:00.000000000 Z
11
+ date: 2016-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel