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 +4 -4
- data/lib/protip/resource.rb +22 -6
- data/lib/protip/wrapper.rb +6 -12
- data/test/unit/protip/resource_test.rb +138 -23
- data/test/unit/protip/wrapper_test.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9c25de6d9f409c5bb8c1659951ecc4fa10a5aa4
|
4
|
+
data.tar.gz: aab7d4182c10f0a540dd5762ea3921dac899c2b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 530e2c08bf013f25afea7d4be6eb254b85b192fb8ca374ceb09ef48ce3e9e703926ca07335bac5396edcacc129f29710cb2cb9e1b619508210ba39c8170335f9
|
7
|
+
data.tar.gz: d65a08e07c0365afadfdf614f731bcf5939deed2e463f4e3d4159f70b0691e1d9735567f33dd1edcf0d7c893f919a128f01e0e634aceec33c4761422aa502e81
|
data/lib/protip/resource.rb
CHANGED
@@ -132,12 +132,13 @@ module Protip
|
|
132
132
|
end
|
133
133
|
|
134
134
|
define_method "#{field.name}=" do |new_value|
|
135
|
-
|
135
|
+
old_value = self.message[field.name] # Only compare the raw values
|
136
136
|
@wrapper.send("#{field.name}=", new_value)
|
137
|
-
|
137
|
+
new_value = self.message[field.name]
|
138
138
|
|
139
|
-
#
|
140
|
-
|
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
|
-
|
267
|
-
|
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
|
|
data/lib/protip/wrapper.rb
CHANGED
@@ -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,
|
119
|
-
if field.type == :message && !converter.convertible?(field.subtype.msgclass)
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
536
|
-
resource =
|
537
|
-
resource.
|
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
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
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
|
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.
|
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-
|
11
|
+
date: 2016-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|