acts_as_sdata 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +200 -0
  4. data/Rakefile +20 -0
  5. data/VERSION +1 -0
  6. data/config/sdata.yml +13 -0
  7. data/config/sdata.yml.example +20 -0
  8. data/config/sdata.yml.tmpl.staging +14 -0
  9. data/generators/acts_as_sdata/acts_as_sdata_generator.rb +9 -0
  10. data/generators/acts_as_sdata/templates/migration.rb +69 -0
  11. data/init.rb +36 -0
  12. data/lib/s_data/active_record_extensions/base.rb +7 -0
  13. data/lib/s_data/active_record_extensions/mixin.rb +157 -0
  14. data/lib/s_data/active_record_extensions/sdata_uuid_mixin.rb +133 -0
  15. data/lib/s_data/atom_extensions/content_mixin.rb +14 -0
  16. data/lib/s_data/atom_extensions/entry_mixin.rb +41 -0
  17. data/lib/s_data/atom_extensions/nodes/digest.rb +48 -0
  18. data/lib/s_data/atom_extensions/nodes/payload.rb +34 -0
  19. data/lib/s_data/atom_extensions/nodes/sync_state.rb +14 -0
  20. data/lib/s_data/conditions_builder.rb +59 -0
  21. data/lib/s_data/controller_mixin.rb +11 -0
  22. data/lib/s_data/controller_mixin/actions.rb +87 -0
  23. data/lib/s_data/controller_mixin/collection_scope.rb +57 -0
  24. data/lib/s_data/controller_mixin/s_data_feed.rb +87 -0
  25. data/lib/s_data/controller_mixin/s_data_instance.rb +35 -0
  26. data/lib/s_data/diagnosis/application_controller_mixin.rb +16 -0
  27. data/lib/s_data/diagnosis/diagnosis.rb +130 -0
  28. data/lib/s_data/diagnosis/diagnosis_mapper.rb +39 -0
  29. data/lib/s_data/exceptions.rb +10 -0
  30. data/lib/s_data/formatting.rb +13 -0
  31. data/lib/s_data/namespace_definitions.rb +19 -0
  32. data/lib/s_data/payload.rb +158 -0
  33. data/lib/s_data/payload_map.rb +0 -0
  34. data/lib/s_data/payload_map/payload_map.rb +136 -0
  35. data/lib/s_data/payload_map/payload_map_hash.rb +39 -0
  36. data/lib/s_data/predicate.rb +31 -0
  37. data/lib/s_data/route_mapper.rb +143 -0
  38. data/lib/s_data/router_mixin.rb +10 -0
  39. data/lib/s_data/sync/controller_mixin.rb +122 -0
  40. data/lib/s_data/sync/sdata_syncing_mixin.rb +17 -0
  41. data/lib/s_data/virtual_base.rb +114 -0
  42. data/test/functional/Rakefile +0 -0
  43. data/test/unit/active_record_mixin/active_record_mixin_spec.rb +20 -0
  44. data/test/unit/active_record_mixin/acts_as_sdata_spec.rb +41 -0
  45. data/test/unit/active_record_mixin/find_by_sdata_instance_id_spec.rb +34 -0
  46. data/test/unit/active_record_mixin/payload_spec.rb +622 -0
  47. data/test/unit/active_record_mixin/to_atom_spec.rb +85 -0
  48. data/test/unit/atom_entry_mixin/atom_entry_mixin_spec.rb +11 -0
  49. data/test/unit/atom_entry_mixin/to_attributes_spec.rb +30 -0
  50. data/test/unit/class_stubs/address.rb +19 -0
  51. data/test/unit/class_stubs/contact.rb +25 -0
  52. data/test/unit/class_stubs/customer.rb +70 -0
  53. data/test/unit/class_stubs/model_base.rb +17 -0
  54. data/test/unit/class_stubs/payload.rb +15 -0
  55. data/test/unit/class_stubs/sd_uuid.rb +28 -0
  56. data/test/unit/class_stubs/user.rb +40 -0
  57. data/test/unit/conditions_builder_spec.rb +54 -0
  58. data/test/unit/controller_mixin/acts_as_sdata_spec.rb +29 -0
  59. data/test/unit/controller_mixin/build_sdata_feed_spec.rb +50 -0
  60. data/test/unit/controller_mixin/controller_mixin_spec.rb +22 -0
  61. data/test/unit/controller_mixin/diagnosis_spec.rb +232 -0
  62. data/test/unit/controller_mixin/sdata_collection_spec.rb +78 -0
  63. data/test/unit/controller_mixin/sdata_create_instance_spec.rb +173 -0
  64. data/test/unit/controller_mixin/sdata_opensearch_and_links_spec.rb +382 -0
  65. data/test/unit/controller_mixin/sdata_scope/linked_model_spec.rb +58 -0
  66. data/test/unit/controller_mixin/sdata_scope/non_linked_model_spec.rb +66 -0
  67. data/test/unit/controller_mixin/sdata_scope/scoping_in_config_spec.rb +64 -0
  68. data/test/unit/controller_mixin/sdata_show_instance_spec.rb +98 -0
  69. data/test/unit/controller_mixin/sdata_update_instance_spec.rb +65 -0
  70. data/test/unit/payload_map/payload_map_hash_spec.rb +84 -0
  71. data/test/unit/payload_map/payload_map_spec.rb +144 -0
  72. data/test/unit/predicate_spec.rb +59 -0
  73. data/test/unit/router_mixin/routes_spec.rb +138 -0
  74. data/test/unit/spec.opts +4 -0
  75. data/test/unit/spec_helper.rb +47 -0
  76. data/test/unit/spec_helpers/nokogiri_extensions.rb +16 -0
  77. data/test/unit/sync_controller_mixin/controller_mixin_spec.rb +22 -0
  78. data/test/unit/sync_controller_mixin/sdata_collection_sync_feed_spec.rb +69 -0
  79. metadata +175 -0
@@ -0,0 +1,17 @@
1
+ module SData
2
+ module Sync
3
+ module SdataSyncingMixin
4
+ def sd_digest
5
+ SData::SdDigest.find_or_create(self.owner, self.sdata_name)
6
+ end
7
+
8
+ def sd_sync_state
9
+ @sd_sync_state ||= SdSyncState.find(:first, :conditions => {:sd_uuid_id => self.sd_uuid, :sd_digest_id => self.sd_digest})
10
+ end
11
+
12
+ def sd_sync_state=(s)
13
+ @sd_sync_state = s
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,114 @@
1
+ module SData
2
+ class VirtualBase
3
+ extend Forwardable
4
+ include SData::Sync::SdataSyncingMixin
5
+
6
+ class << self
7
+ attr_accessor :baze_class
8
+ end
9
+
10
+ class_inheritable_accessor :owner_key
11
+ self.owner_key = 'created_by_id'
12
+
13
+ attr_accessor :baze
14
+ def_delegators :baze, :id, :created_at, :updated_at, :save, :save!, :update_attributes, :update_attributes!, :created_by, :reload
15
+
16
+ def initialize(the_baze, the_type=nil)
17
+ @uuid_class = SData::SdUuid
18
+ self.baze = the_baze
19
+ if self.respond_to?('sdata_type') && the_type
20
+ self.sdata_type = the_type
21
+ end
22
+ super()
23
+ end
24
+
25
+ # temporary
26
+ def method_missing(meth, *args, &block)
27
+ if @payload
28
+ @payload.send(meth, *args, &block)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def baze_class_name
35
+ self.class.baze_class_name
36
+ end
37
+
38
+ def reference
39
+ # GCRM requires reference to be present and unique to each GCRM model. Decided to use baze id, but
40
+ # alone it is insufficient because some GCRM models (e.g. Address) can have the same baze id for different
41
+ # bazes (e.g. customer vs user). So using the form below.
42
+ # Adding GCRM model name is fine but unneeded. GCRM name _only_, without BB model name, is insufficient.
43
+ "#{self.baze.class.name.demodulize}_#{self.baze.id}"
44
+ end
45
+
46
+ def sd_class
47
+ self.class.name
48
+ end
49
+
50
+ def self.build_for(data, the_type=nil)
51
+ if data.is_a? Array
52
+ data.map {|item| virtual_base_for_object(item, the_type) }.compact
53
+ else
54
+ virtual_base_for_object(data, the_type)
55
+ end
56
+ end
57
+
58
+ def self.virtual_base_for_object(obj, the_type=nil)
59
+ if obj
60
+ vb = self.new(obj, the_type)
61
+ vb = DeletedObjectProxy.from_virtual_base(vb) if obj.class.paranoid? && obj.deleted?
62
+ vb
63
+ else
64
+ nil
65
+ end
66
+ end
67
+
68
+ def owner
69
+ raise "Security problem: owner not defined in subclass!"
70
+ end
71
+
72
+ # TODO -- should return all bb models that are used to composite the virtual model (or at least the subset whose modifications must be tracked)
73
+
74
+ def self.baze_classes
75
+ [self.baze_class]
76
+ end
77
+
78
+ def self.baze_class_name
79
+ baze_class.nil? ? nil : baze_class.name.demodulize
80
+ end
81
+
82
+ def self.sdata_name
83
+ name.demodulize
84
+ end
85
+
86
+ def self.find(*params)
87
+ self.new(self.baze_class.find(*params))
88
+ end
89
+
90
+ def self.first(*params)
91
+ self.new(self.baze_class.first(*params))
92
+ end
93
+
94
+ def self.all(*params)
95
+ self.collection(self.baze_class.all(*params))
96
+ end
97
+
98
+ def self.collection(arr)
99
+ arr.map do |obj|
100
+ case obj
101
+ when VirtualBase, DeletedObjectProxy
102
+ obj
103
+ when ActiveRecord::Base
104
+ self.new(obj)
105
+ else
106
+ obj
107
+ end
108
+ end
109
+ end
110
+
111
+ end
112
+ VirtualBase.extend SData::ActiveRecordExtensions::Mixin
113
+ VirtualBase.extend SData::ActiveRecordExtensions::SdataUuidableMixin
114
+ end
File without changes
@@ -0,0 +1,20 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe SData::ActiveRecordExtensions::Mixin do
4
+ describe "given a class which behaves like ActiveRecord::Base" do
5
+ before :all do
6
+ Base = Class.new
7
+ end
8
+
9
+ describe "when ActiveRecordExtensions::Mixin is included" do
10
+ before :each do
11
+ Base.extend SData::ActiveRecordExtensions::Mixin
12
+ end
13
+
14
+ it "should define .acts_as_data class method" do
15
+ Base.should respond_to(:acts_as_sdata)
16
+ Base.new.should_not respond_to(:acts_as_sdata)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+
4
+ describe SData::ActiveRecordExtensions::Mixin do
5
+ describe "given a class extended by ActiveRecordExtensions" do
6
+ before :all do
7
+ Base = Class.new
8
+ Base.extend SData::ActiveRecordExtensions::Mixin
9
+ end
10
+
11
+ describe ".acts_as_sdata" do
12
+ before :each do
13
+ @options = { :title => lambda { "#{id}: #{name}" },
14
+ :summary => lambda { "#{name}" } }
15
+
16
+ options = @options
17
+ Base.class_eval do
18
+ acts_as_sdata options
19
+ end
20
+ end
21
+
22
+ it "should save passed options to variable accessible by class" do
23
+ Base.sdata_options.should == @options
24
+ end
25
+
26
+ it "should save passed options to variable accessible by instances" do
27
+ Base.new.sdata_options.should == @options
28
+ end
29
+
30
+ it "should include instance methods" do
31
+ Base.new.should respond_to(:to_atom)
32
+ end
33
+
34
+ describe "when :instance_id is passed" do
35
+ it "should check if passed attribute is unique" do
36
+ pending "Not implemented yet"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe SData::ActiveRecordExtensions::Mixin, "#find_by_sdata_instance_id" do
4
+ describe "given a class extended by ActiveRecordExtensions" do
5
+ before :all do
6
+ Model = Class.new(ActiveRecord::Base)
7
+ Model.extend SData::ActiveRecordExtensions::Mixin
8
+ end
9
+
10
+ describe "when @@sdata_options contain :instance_id" do
11
+ before :each do
12
+ Model.class_eval { acts_as_sdata :instance_id => :email }
13
+ end
14
+
15
+ it "should find by a field assigned to :instance_id option value" do
16
+ email = "e@ma.il"
17
+ Model.should_receive(:find).with(:first, :conditions => { :email => email }).once
18
+ Model.find_by_sdata_instance_id(email)
19
+ end
20
+ end
21
+
22
+ describe "when @@sdata_options does not contain :instance_id" do
23
+ before :each do
24
+ Model.class_eval { acts_as_sdata }
25
+ end
26
+
27
+ it "should consider :id as SData instance ID" do
28
+ id = 1
29
+ Model.should_receive(:find).with(id).once
30
+ Model.find_by_sdata_instance_id(id)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,622 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe SData::ActiveRecordExtensions::Mixin, "#to_atom" do
4
+ describe "given a class extended by ActiveRecordExtensions" do
5
+ before :all do
6
+ SData::SdUuid.extend SData::ActiveRecordExtensions::SdataUuidMixin
7
+ SData::SdUuid.class_eval { acts_as_sdata_uuid }
8
+ [User, Customer, Contact, Address].each do |model|
9
+ model.extend SData::ActiveRecordExtensions::Mixin
10
+ model.extend SData::ActiveRecordExtensions::SdataUuidableMixin
11
+ model.class_eval { acts_as_sdata }
12
+ end
13
+ Customer.class_eval { has_sdata_uuid }
14
+ Contact.class_eval { has_sdata_uuid }
15
+ end
16
+
17
+ def customer_attributes
18
+ ["crmErp:address", "crmErp:associatedContacts", "crmErp:createdAt", "crmErp:hashValue", "crmErp:myContacts", "crmErp:myDefaultContact", "crmErp:name", "crmErp:number", "crmErp:simpleElements", "crmErp:updatedAt"].to_set
19
+ end
20
+
21
+ def contact_attributes
22
+ ["crmErp:createdAt", "crmErp:customerId", "crmErp:name", "crmErp:updatedAt"].to_set
23
+ end
24
+
25
+ def payload_header_assertions(payload)
26
+ payload.children.size.should == 1
27
+ customer = payload.xpath('crmErp:customer').first
28
+ customer.attributes_with_ns.keys.to_set.should == ["sdata:key", "sdata:url", "sdata:uuid"].to_set
29
+ customer.attributes_with_ns["sdata:key"].should == "12345"
30
+ customer.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/customers('12345')"
31
+ customer.attributes_with_ns["sdata:uuid"].should == 'CUST-10000'
32
+ end
33
+
34
+ describe "given the payload generating conditions" do
35
+ before :each do
36
+ Customer.extend SData::PayloadMap
37
+ @customer = Customer.new.populate_defaults
38
+ @customer.id = 12345
39
+ @customer.contacts[0].id = 123
40
+ @customer.contacts[1].id = 456
41
+ @customer.contacts[1].name = "Second Contact Name"
42
+ @customer.contacts.each do |contact|
43
+ contact.populate_defaults
44
+ end
45
+ @customer.address.populate_defaults
46
+ end
47
+
48
+ def payload(options)
49
+ xml = Nokogiri::XML(@customer.to_atom(options).to_xml) { |config| config.noblanks }
50
+ xml.xpath('xmlns:entry/sdata:payload').first
51
+ end
52
+
53
+ it "describes elements with recursively included children without including association data" do
54
+ payload = payload(:dataset => 'myDataSet', :include => '$children')
55
+
56
+ payload_header_assertions(payload)
57
+
58
+ customer = payload.xpath('crmErp:customer')
59
+ customer.children.map(&:name_with_ns).to_set.should == customer_attributes
60
+ customer.children.each do |element|
61
+ case element.name_with_ns
62
+
63
+ when 'crmErp:associatedContacts'
64
+ element.attributes_with_ns.keys.should == ['sdata:url']
65
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/associatedContacts"
66
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
67
+ element.children.each do |child_element|
68
+ case child_element.attributes_with_ns['sdata:key']
69
+ when '123'
70
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
71
+ child_element.attributes_with_ns['sdata:key'].should == "123"
72
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
73
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
74
+ child_element.children.should be_blank
75
+ when '456'
76
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
77
+ child_element.attributes_with_ns['sdata:key'].should == "456"
78
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
79
+ child_element.children.should be_blank
80
+ else
81
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
82
+ end
83
+ end
84
+ when 'crmErp:simpleElements'
85
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:simpleElement", "crmErp:simpleElement"].to_set
86
+ element.xpath('crmErp:simpleElement/text()').map(&:to_s).should == ["element 1", "element 2"]
87
+ when 'crmErp:hashValue'
88
+ element.children.size.should == 1
89
+ element.children[0].name_with_ns.should == "crmErp:simpleObjectKey"
90
+ element.children[0].text.should == "simple_object_value"
91
+ when 'crmErp:myContacts'
92
+
93
+ element.attributes_with_ns.keys.should == ['sdata:url']
94
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/myContacts"
95
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
96
+ element.children.each do |child_element|
97
+ case child_element.attributes_with_ns['sdata:key']
98
+ when '123'
99
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
100
+ child_element.attributes_with_ns['sdata:key'].should == "123"
101
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
102
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
103
+ child_element.children.map(&:name_with_ns).to_set.should == contact_attributes
104
+ child_element.children.each do |grandchild_element|
105
+ case grandchild_element.name_with_ns
106
+ when "crmErp:createdAt"
107
+ Time.parse(grandchild_element.text).should < Time.now-2.days
108
+ when "crmErp:name"
109
+ grandchild_element.attributes_with_ns["xsi:nil"].should == nil
110
+ grandchild_element.text.should == "Contact Name"
111
+ when "crmErp:customerId"
112
+ grandchild_element.attributes_with_ns["xsi:nil"].should == 'true'
113
+ when "crmErp:updatedAt"
114
+ Time.parse(grandchild_element.text).should < Time.now-1.day
115
+ else
116
+ raise "Unknown contact element: #{grandchild_element.name_with_ns}"
117
+ end
118
+ end
119
+ when '456'
120
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
121
+ child_element.attributes_with_ns['sdata:key'].should == "456"
122
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
123
+ child_element.children.map(&:name_with_ns).to_set.should == contact_attributes
124
+ child_element.children.each do |grandchild_element|
125
+ case grandchild_element.name_with_ns
126
+ when "crmErp:createdAt"
127
+ Time.parse(grandchild_element.text).should < Time.now-2.days
128
+ when "crmErp:name"
129
+ grandchild_element.attributes_with_ns["xsi:nil"].should == nil
130
+ grandchild_element.text.should == "Second Contact Name"
131
+ when "crmErp:customerId"
132
+ grandchild_element.attributes_with_ns["xsi:nil"].should == 'true'
133
+ when "crmErp:updatedAt"
134
+ Time.parse(child_element.text).should < Time.now-1.day
135
+ else
136
+ raise "Unknown contact element: #{grandchild_element.name_with_ns}"
137
+ end
138
+ end
139
+ else
140
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
141
+ end
142
+ end
143
+ when 'crmErp:myDefaultContact'
144
+ element.attributes_with_ns["sdata:key"].should == "123"
145
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
146
+ element.children.map(&:name_with_ns).to_set.should == contact_attributes
147
+ element.children.each do |child_element|
148
+ case child_element.name_with_ns
149
+ when "crmErp:createdAt"
150
+ Time.parse(child_element.text).should < Time.now-2.days
151
+ when "crmErp:name"
152
+ child_element.text.should == "Contact Name"
153
+ when "crmErp:customerId"
154
+ child_element.attributes_with_ns["xsi:nil"].should == 'true'
155
+ when "crmErp:updatedAt"
156
+ Time.parse(child_element.text).should < Time.now-1.day
157
+ when "crmErp:id"
158
+ element.text.should_not be_nil
159
+ else
160
+ raise "Unknown contact element: #{child_element.name_with_ns}"
161
+ end
162
+ end
163
+ when 'crmErp:address'
164
+ element.attributes_with_ns["sdata:key"].should == "12345"
165
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/addresses('12345')"
166
+ element.children.each do |child_element|
167
+ case child_element.name_with_ns
168
+ when "crmErp:createdAt"
169
+ Time.parse(child_element.text).should < Time.now-2.days
170
+ when "crmErp:customerId"
171
+ child_element.attributes_with_ns["xsi:nil"].should == 'true'
172
+ when "crmErp:updatedAt"
173
+ Time.parse(child_element.text).should < Time.now-1.day
174
+ when "crmErp:city"
175
+ child_element.text.should == 'Vancouver'
176
+ else
177
+ raise "Unknown address element: #{child_element.name_with_ns}"
178
+ end
179
+ end
180
+ when 'crmErp:name'
181
+ element.text.should == "Customer Name"
182
+ when 'crmErp:number'
183
+ element.text.should == "12345"
184
+ when "crmErp:createdAt"
185
+ Time.parse(element.text).should < Time.now-2.days
186
+ when "crmErp:updatedAt"
187
+ Time.parse(element.text).should < Time.now-1.days
188
+ else
189
+ raise "Unexpected customer element: #{element.name_with_ns}"
190
+ end
191
+ end
192
+ end
193
+
194
+ it "describes elements with recursively included children without including association data when in sync mode" do
195
+ payload = payload(:dataset => 'myDataSet', :sync => 'true')
196
+
197
+ payload_header_assertions(payload)
198
+
199
+ customer = payload.xpath('crmErp:customer')
200
+ customer.children.map(&:name_with_ns).to_set.should == customer_attributes
201
+ customer.children.each do |element|
202
+ case element.name_with_ns
203
+
204
+ when 'crmErp:associatedContacts'
205
+ element.attributes_with_ns.keys.should == ['sdata:url']
206
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/associatedContacts"
207
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
208
+ element.children.each do |child_element|
209
+ case child_element.attributes_with_ns['sdata:key']
210
+ when '123'
211
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
212
+ child_element.attributes_with_ns['sdata:key'].should == "123"
213
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
214
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
215
+ child_element.children.should be_blank
216
+ when '456'
217
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
218
+ child_element.attributes_with_ns['sdata:key'].should == "456"
219
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
220
+ child_element.children.should be_blank
221
+ else
222
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
223
+ end
224
+ end
225
+ when 'crmErp:simpleElements'
226
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:simpleElement", "crmErp:simpleElement"].to_set
227
+ element.xpath('crmErp:simpleElement/text()').map(&:to_s).should == ["element 1", "element 2"]
228
+ when 'crmErp:hashValue'
229
+ element.children.size.should == 1
230
+ element.children[0].name_with_ns.should == "crmErp:simpleObjectKey"
231
+ element.children[0].text.should == "simple_object_value"
232
+ when 'crmErp:myContacts'
233
+ element.attributes_with_ns.keys.should == ['sdata:url']
234
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/myContacts"
235
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
236
+ element.children.each do |child_element|
237
+ case child_element.attributes_with_ns['sdata:key']
238
+ when '123'
239
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
240
+ child_element.attributes_with_ns['sdata:key'].should == "123"
241
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
242
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
243
+ child_element.children.map(&:name_with_ns).to_set.should == contact_attributes
244
+ child_element.children.each do |grandchild_element|
245
+ case grandchild_element.name_with_ns
246
+ when "crmErp:createdAt"
247
+ Time.parse(grandchild_element.text).should < Time.now-2.days
248
+ when "crmErp:name"
249
+ grandchild_element.attributes_with_ns["xsi:nil"].should == nil
250
+ grandchild_element.text.should == "Contact Name"
251
+ when "crmErp:customerId"
252
+ grandchild_element.attributes_with_ns["xsi:nil"].should == 'true'
253
+ when "crmErp:updatedAt"
254
+ Time.parse(grandchild_element.text).should < Time.now-1.day
255
+ else
256
+ raise "Unknown contact element: #{grandchild_element.name_with_ns}"
257
+ end
258
+ end
259
+ when '456'
260
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
261
+ child_element.attributes_with_ns['sdata:key'].should == "456"
262
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
263
+ child_element.children.map(&:name_with_ns).to_set.should == contact_attributes
264
+ child_element.children.each do |grandchild_element|
265
+ case grandchild_element.name_with_ns
266
+ when "crmErp:createdAt"
267
+ Time.parse(grandchild_element.text).should < Time.now-2.days
268
+ when "crmErp:name"
269
+ grandchild_element.attributes_with_ns["xsi:nil"].should == nil
270
+ grandchild_element.text.should == "Second Contact Name"
271
+ when "crmErp:customerId"
272
+ grandchild_element.attributes_with_ns["xsi:nil"].should == 'true'
273
+ when "crmErp:updatedAt"
274
+ Time.parse(child_element.text).should < Time.now-1.day
275
+ else
276
+ raise "Unknown contact element: #{grandchild_element.name_with_ns}"
277
+ end
278
+ end
279
+ else
280
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
281
+ end
282
+ end
283
+ when 'crmErp:myDefaultContact'
284
+ element.attributes_with_ns["sdata:key"].should == "123"
285
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
286
+ element.children.map(&:name_with_ns).to_set.should == contact_attributes
287
+ element.children.each do |child_element|
288
+ case child_element.name_with_ns
289
+ when "crmErp:createdAt"
290
+ Time.parse(child_element.text).should < Time.now-2.days
291
+ when "crmErp:name"
292
+ child_element.text.should == "Contact Name"
293
+ when "crmErp:customerId"
294
+ child_element.attributes_with_ns["xsi:nil"].should == 'true'
295
+ when "crmErp:updatedAt"
296
+ Time.parse(child_element.text).should < Time.now-1.day
297
+ when "crmErp:id"
298
+ element.text.should_not be_nil
299
+ else
300
+ raise "Unknown contact element: #{child_element.name_with_ns}"
301
+ end
302
+ end
303
+ when 'crmErp:address'
304
+ element.attributes_with_ns["sdata:key"].should == "12345"
305
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/addresses('12345')"
306
+ element.children.each do |child_element|
307
+ case child_element.name_with_ns
308
+ when "crmErp:createdAt"
309
+ Time.parse(child_element.text).should < Time.now-2.days
310
+ when "crmErp:customerId"
311
+ child_element.attributes_with_ns["xsi:nil"].should == 'true'
312
+ when "crmErp:updatedAt"
313
+ Time.parse(child_element.text).should < Time.now-1.day
314
+ when "crmErp:city"
315
+ child_element.text.should == 'Vancouver'
316
+ else
317
+ raise "Unknown address element: #{child_element.name_with_ns}"
318
+ end
319
+ end
320
+ when 'crmErp:name'
321
+ element.text.should == "Customer Name"
322
+ when 'crmErp:number'
323
+ element.text.should == "12345"
324
+ when "crmErp:createdAt"
325
+ Time.parse(element.text).should < Time.now-2.days
326
+ when "crmErp:updatedAt"
327
+ Time.parse(element.text).should < Time.now-1.days
328
+ else
329
+ raise "Unexpected customer element: #{element.name_with_ns}"
330
+ end
331
+ end
332
+ end
333
+
334
+ it "describes elements with immediate children only" do
335
+ payload = payload(:dataset => 'myDataSet')
336
+
337
+ payload_header_assertions(payload)
338
+ customer = payload.xpath('crmErp:customer')
339
+ customer.children.map(&:name_with_ns).to_set.should == customer_attributes
340
+ customer.children.each do |element|
341
+ case element.name_with_ns
342
+ when 'crmErp:associatedContacts'
343
+ element.attributes_with_ns.keys.should == ['sdata:url']
344
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/associatedContacts"
345
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
346
+ element.children.each do |child_element|
347
+ case child_element.attributes_with_ns['sdata:key']
348
+ when '123'
349
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
350
+ child_element.attributes_with_ns['sdata:key'].should == "123"
351
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
352
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
353
+ child_element.children.should be_blank
354
+ when '456'
355
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
356
+ child_element.attributes_with_ns['sdata:key'].should == "456"
357
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
358
+ child_element.children.should be_blank
359
+ else
360
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
361
+ end
362
+ end
363
+ when 'crmErp:simpleElements'
364
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:simpleElement", "crmErp:simpleElement"].to_set
365
+ element.xpath('crmErp:simpleElement/text()').map(&:to_s).should == ["element 1", "element 2"]
366
+ when 'crmErp:hashValue'
367
+ element.children.size.should == 1
368
+ element.children[0].name_with_ns.should == "crmErp:simpleObjectKey"
369
+ element.children[0].text.should == "simple_object_value"
370
+ when 'crmErp:myContacts'
371
+ element.attributes_with_ns.keys.should == ['sdata:url']
372
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/myContacts"
373
+ element.attributes_with_ns.keys.should == ['sdata:url']
374
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/myContacts"
375
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
376
+ element.children.each do |child_element|
377
+ case child_element.attributes_with_ns['sdata:key']
378
+ when '123'
379
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
380
+ child_element.attributes_with_ns['sdata:key'].should == "123"
381
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
382
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
383
+ child_element.children.should be_blank
384
+ when '456'
385
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
386
+ child_element.attributes_with_ns['sdata:key'].should == "456"
387
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
388
+ child_element.children.should be_blank
389
+ else
390
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
391
+ end
392
+ end
393
+ when 'crmErp:myDefaultContact'
394
+ element.attributes_with_ns["sdata:key"].should == "123"
395
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
396
+ element.children.size.should == 0
397
+ when 'crmErp:name'
398
+ element.text.should == "Customer Name"
399
+ when 'crmErp:number'
400
+ element.text.should == "12345"
401
+ when "crmErp:createdAt"
402
+ Time.parse(element.text).should < Time.now-2.days
403
+ when "crmErp:updatedAt"
404
+ Time.parse(element.text).should < Time.now-1.days
405
+ when 'crmErp:address'
406
+ element.attributes_with_ns["sdata:key"].should == "12345"
407
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/addresses('12345')"
408
+
409
+ element.children.each do |child_element|
410
+ case child_element.name_with_ns
411
+ when "crmErp:createdAt"
412
+ Time.parse(child_element.text).should < Time.now-2.days
413
+ when "crmErp:customerId"
414
+ child_element.attributes_with_ns["xsi:nil"].should == 'true'
415
+ when "crmErp:updatedAt"
416
+ Time.parse(child_element.text).should < Time.now-1.day
417
+ when "crmErp:city"
418
+ child_element.text.should == 'Vancouver'
419
+ else
420
+ raise "Unknown address element: #{child_element.name_with_ns}"
421
+ end
422
+ end
423
+ else
424
+ raise "Unexpected customer element: #{element.name_with_ns}"
425
+ end
426
+ end
427
+ end
428
+
429
+ it "shows no payload at all with precedence 0" do
430
+ payload = payload(:dataset => 'myDataSet', :precedence => 0)
431
+
432
+ payload.should be_nil
433
+ end
434
+
435
+ it "shows header info only with precedence 1" do
436
+ payload = payload(:dataset => 'myDataSet', :precedence => 1)
437
+
438
+ payload_header_assertions(payload)
439
+ customer = payload.xpath('crmErp:customer').first
440
+ customer.children.size.should == 0
441
+ end
442
+
443
+ it "shows no uuid if match is not found" do
444
+ @customer.id = 4321
445
+ payload = payload(:dataset => 'myDataSet', :precedence => 1)
446
+
447
+ customer = payload.xpath('crmErp:customer').first
448
+ customer.attributes_with_ns.keys.to_set.should == ["sdata:key", "sdata:url"].to_set
449
+ customer.children.size.should == 0
450
+ end
451
+
452
+ it "shows only some attributes with precedence 2" do
453
+ payload = payload(:dataset => 'myDataSet', :precedence => 2)
454
+
455
+ payload_header_assertions(payload)
456
+
457
+ customer = payload.xpath('crmErp:customer').first
458
+ customer.children.map(&:name_with_ns).to_set.should == ["crmErp:name"].to_set
459
+ customer.children.each do |element|
460
+ case element.name_with_ns
461
+ when 'crmErp:name'
462
+ element.text.should == "Customer Name"
463
+ when "crmErp:id"
464
+ element.text.should_not be_nil
465
+ else
466
+ raise "Unexpected customer element: #{element.name_with_ns}"
467
+ end
468
+ end
469
+ end
470
+
471
+ it "applies precendence filter to child attributes as well" do
472
+ payload = payload(:dataset => 'myDataSet', :include => "$children", :precedence => 3)
473
+
474
+ payload_header_assertions(payload)
475
+ customer = payload.xpath('crmErp:customer').first
476
+ customer.children.map(&:name_with_ns).to_set.should == ["crmErp:associatedContacts", "crmErp:createdAt", "crmErp:myDefaultContact", "crmErp:name", "crmErp:updatedAt"].to_set
477
+ customer.children.each do |element|
478
+ case element.name_with_ns
479
+ when 'crmErp:associatedContacts'
480
+ element.attributes_with_ns.keys.should == ['sdata:url']
481
+ element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/customer('12345')/associatedContacts"
482
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:contact", "crmErp:contact"].to_set
483
+ element.children.each do |child_element|
484
+ case child_element.attributes_with_ns['sdata:key']
485
+ when '123'
486
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url', 'sdata:uuid'].to_set
487
+ child_element.attributes_with_ns['sdata:key'].should == "123"
488
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
489
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
490
+ child_element.children.should be_blank
491
+ when '456'
492
+ child_element.attributes_with_ns.keys.to_set.should == ['sdata:key', 'sdata:url'].to_set
493
+ child_element.attributes_with_ns['sdata:key'].should == "456"
494
+ child_element.attributes_with_ns['sdata:url'].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('456')"
495
+ child_element.children.should be_blank
496
+ else
497
+ raise "Unknown contact attribute: #{child_element.attributes_with_ns['sdata:key']}"
498
+ end
499
+ end
500
+ when 'crmErp:name'
501
+ element.text.should == "Customer Name"
502
+ when "crmErp:createdAt"
503
+ Time.parse(element.text).should < Time.now-2.days
504
+ when "crmErp:updatedAt"
505
+ Time.parse(element.text).should < Time.now-1.days
506
+ when 'crmErp:myDefaultContact'
507
+ element.attributes_with_ns["sdata:key"].should == "123"
508
+ element.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/contacts('123')"
509
+ element.children.map(&:name_with_ns).to_set.should == ["crmErp:customerId", "crmErp:name"].to_set
510
+ element.children.each do |child_element|
511
+ case child_element.name_with_ns
512
+ when "crmErp:name"
513
+ child_element.text.should == "Contact Name"
514
+ when "crmErp:customerId"
515
+ child_element.attributes_with_ns["xsi:nil"].should == 'true'
516
+ when "crmErp:id"
517
+ element.text.should_not be_nil
518
+ else
519
+ raise "Unknown contact element: #{child_element.name_with_ns}"
520
+ end
521
+ end
522
+ when "crmErp:id"
523
+ element.text.should_not be_nil
524
+ else
525
+ raise "Unexpected customer element: #{element.name_with_ns}"
526
+ end
527
+ end
528
+ end
529
+
530
+ it "shows custom content and descriptor fields when requested" do
531
+ Customer.class_eval { acts_as_sdata(:content => :sdata_content) }
532
+ Contact.class_eval { acts_as_sdata(:content => :sdata_content) }
533
+
534
+ payload = payload(:dataset => 'myDataSet', :include => "$descriptor,$children")
535
+
536
+ payload.children.size.should == 1
537
+ customer = payload.xpath('crmErp:customer').first
538
+ customer.name_with_ns.should == 'crmErp:customer'
539
+ customer.attributes_with_ns.keys.to_set.should == ["sdata:descriptor", "sdata:key", "sdata:url", "sdata:uuid"].to_set
540
+ customer.attributes_with_ns["sdata:key"].should == "12345"
541
+ customer.attributes_with_ns["sdata:url"].should == "http://www.example.com/sdata/example/myContract/myDataSet/customers('12345')"
542
+ customer.attributes_with_ns["sdata:descriptor"].should == "Customer #12345: Customer Name"
543
+ customer.children.each do |element|
544
+ case element.name_with_ns
545
+ when 'crmErp:myDefaultContact'
546
+ element.attributes_with_ns.keys.to_set.should == ["sdata:descriptor", "sdata:key", "sdata:url", "sdata:uuid"].to_set
547
+ element.attributes_with_ns['sdata:descriptor'].should == "Contact #123: Contact Name"
548
+ when 'crmErp:myContacts'
549
+ found_with_uuid, found_without_uuid = false
550
+ element.children.size.should == 2
551
+ element.children.each do |child_element|
552
+ if child_element.attributes_with_ns['sdata:uuid']
553
+ found_with_uuid = true
554
+ child_element.attributes_with_ns.keys.to_set.should == ["sdata:descriptor", "sdata:key", "sdata:url", "sdata:uuid"].to_set
555
+ child_element.attributes_with_ns['sdata:uuid'].should == "C-123-456"
556
+ else
557
+ found_without_uuid = true
558
+ child_element.attributes_with_ns.keys.to_set.should == ["sdata:descriptor", "sdata:key", "sdata:url"].to_set
559
+ end
560
+ child_element.attributes_with_ns['sdata:descriptor'].should =~ /Contact ##{child_element.attributes_with_ns['sdata:key']}.*/
561
+ end
562
+ found_with_uuid.should == true
563
+ found_without_uuid.should == true
564
+ end
565
+ end
566
+ end
567
+
568
+ it "makes include=something_other_than_$children_or_$descriptor do absolutely nothing until we implement the algorithm properly" do
569
+ [true, false].each do |bool|
570
+ SData::Payload.stub :is_sync? => bool
571
+ payload_1 = payload(:dataset => 'myDataSet')
572
+ payload_2 = payload(:dataset => 'myDataSet', :include => 'associatedContacts')
573
+ payload_3 = payload(:dataset => 'myDataSet', :include => 'myContacts')
574
+ payload_1.to_xml.should == payload_2.to_xml
575
+ payload_1.to_xml.should == payload_3.to_xml
576
+ end
577
+ end
578
+
579
+ it "includes associations on nested include request for that association and properly parses nesting levels" do
580
+ # ?include='child/grandchild' should find and include the child, then traverse thru child attributes
581
+ # and find the grandchild, to include it too.
582
+ # But it should not search for or include 'grandchild' association (or any other association except
583
+ # 'child') from outside of the child's tree, nor should it search for or include 'child' association
584
+ # (or any other association) from inside the child's tree.
585
+ pending # Not due June 1
586
+ end
587
+
588
+ it "only shows requested attributes when a simple select parameter is given" do
589
+ payload = payload(:dataset => 'myDataSet', :select => 'name,number')
590
+
591
+ payload_header_assertions(payload)
592
+ customer = payload.xpath('crmErp:customer').first
593
+ customer.children.map(&:name_with_ns).to_set.should == ['crmErp:name', 'crmErp:number'].to_set
594
+ customer.children.each do |element|
595
+ case element.name_with_ns
596
+ when 'crmErp:name'
597
+ element.text.should == "Customer Name"
598
+ when 'crmErp:number'
599
+ element.text.should == "12345"
600
+ else
601
+ raise "Unexpected customer element: #{element.name_with_ns}"
602
+ end
603
+ end
604
+ end
605
+
606
+ it "only shows requested attributes when a nested select parameter is given and properly parses nesting level" do
607
+ pending # Not due June 1
608
+ end
609
+
610
+ it "properly escapes xml content in user data" do
611
+ Customer.class_eval { acts_as_sdata(:content => :sdata_content) }
612
+ Contact.class_eval { acts_as_sdata(:content => :sdata_content) }
613
+ @customer.name = "</crmErp:name><div>asdf</div>"
614
+ @customer.number = "<div>123456</div>"
615
+ @customer.to_atom(:dataset => 'myDataSet').sdata_payload.to_xml.to_s.include?("</crmErp:name><div>asdf</div>").should == false
616
+ @customer.to_atom(:dataset => 'myDataSet').sdata_payload.to_xml.to_s.include?("<div>123456</div>").should == false
617
+ @customer.to_atom(:dataset => 'myDataSet').sdata_payload.to_xml.to_s.include?("&lt;/crmErp:name&gt;&lt;div&gt;asdf&lt;/div&gt;").should == true
618
+ @customer.to_atom(:dataset => 'myDataSet').sdata_payload.to_xml.to_s.include?("&lt;div&gt;123456&lt;/div&gt;").should == true
619
+ end
620
+ end
621
+ end
622
+ end