acts_as_sdata 1.0.0

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.
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,64 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+
3
+ describe SData::ControllerMixin, "#sdata_scope" do
4
+ context "given a controller which acts as sdata and accesses a linked model" do
5
+ context "being configured with user scoping" do
6
+ before :all do
7
+ BaseModel = Class.new(ActiveRecord::Base)
8
+
9
+ Object.__send__ :remove_const, :Model if defined?(Model)
10
+ class Model < SData::VirtualBase
11
+ self.baze_class = BaseModel
12
+
13
+ define_payload_map :born_at => { :baze_field => :born_at }
14
+
15
+ acts_as_sdata :link => :simply_guid
16
+ end
17
+
18
+ class Controller < ActionController::Base
19
+ extend SData::ControllerMixin
20
+
21
+ acts_as_sdata :model => Model, :feed =>
22
+ {:author => 'Billing Boss',
23
+ :path => '/trading_accounts',
24
+ :title => 'Billing Boss | Trading Accounts',
25
+ :default_items_per_page => 10,
26
+ :maximum_items_per_page => 100},
27
+ :scoping => ["created_by_id = ?"]
28
+ end
29
+ end
30
+
31
+ before :each do
32
+ @user = User.new.populate_defaults
33
+ @controller = Controller.new
34
+ Model.stub! :all => []
35
+ end
36
+
37
+ context "with no other params" do
38
+ before :each do
39
+ @controller.stub! :params => {}
40
+ @controller.stub! :current_user => @user
41
+ @controller.stub! :target_user => @user
42
+ end
43
+
44
+ it "should return all entity records created_by scope" do
45
+ Model.should_receive(:all).with :conditions => ['created_by_id = ?', "#{@user.id}"]
46
+ @controller.send :sdata_scope
47
+ end
48
+ end
49
+
50
+ context "with condition and where clause" do
51
+ before :each do
52
+ @controller.stub! :params => { 'where born_at gt 1900' => nil, :condition => '$linked' }
53
+ @controller.stub! :current_user => @user
54
+ @controller.stub! :target_user => @user
55
+ end
56
+
57
+ it "should return all entity records with created_by, predicate, and link scope" do
58
+ Model.should_receive(:all).with :conditions => ['"born_at" > ? and created_by_id = ? and id IN (SELECT bb_model_id FROM sd_uuids WHERE bb_model_type = \'BaseModel\' and sd_class = \'Model\')', '1900', @user.id.to_s]
59
+ @controller.send :sdata_scope
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,98 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData
4
+
5
+ describe ControllerMixin, "#sdata_show_instance" do
6
+ module Sage
7
+ module BusinessLogic
8
+ module Exception
9
+ class AccessDeniedException < StandardError
10
+
11
+ end
12
+ end
13
+ end
14
+ end
15
+ describe "given a controller which acts as sdata" do
16
+ before :all do
17
+ Base = Class.new(ActionController::Base)
18
+ Base.extend ControllerMixin
19
+ end
20
+ describe "given a model without baze" do
21
+ before :all do
22
+ Model = Class.new
23
+ Base.acts_as_sdata :model => Model
24
+ end
25
+
26
+ before :each do
27
+ @controller = Base.new
28
+ end
29
+
30
+ describe "when params contain :instance_id key" do
31
+ before :each do
32
+ @instance_id = 1
33
+ @controller.stub! :params => { :instance_id => @instance_id },
34
+ :current_user => OpenStruct.new(:sage_username => 'bob', :id => 1),
35
+ :target_user => OpenStruct.new(:sage_username => 'bob', :id => 1),
36
+ :logged_in? => true
37
+ end
38
+
39
+ # describe "when record with such id exists and belongs to user" do
40
+ # before :each do
41
+ # @record = Model.new
42
+ # @record.stub! :owner => OpenStruct.new(:sage_username => 'bob', :id => 1)
43
+ # Model.should_receive(:find_by_sdata_instance_id).with(@instance_id).and_return(@record)
44
+ # end
45
+ #
46
+ # it "should render atom entry of the record" do
47
+ # entry = Atom::Entry.new
48
+ # @record.should_receive(:to_atom).and_return(entry)
49
+ # @controller.should_receive(:render).with(:xml => entry, :content_type => "application/atom+xml; type=entry")
50
+ # @controller.sdata_show_instance
51
+ # end
52
+ # end
53
+
54
+ describe "when record with such id exists but does not belong to user" do
55
+ before :each do
56
+ @record = Model.new
57
+ @record.stub! :owner => OpenStruct.new(:sage_username => 'mary', :id => 2)
58
+ Model.should_receive(:find_by_sdata_instance_id).with(@instance_id).and_return(@record)
59
+ end
60
+
61
+ it "should render atom entry of the record" do
62
+ # Can't get raise_error to work properly :/
63
+ entry = Atom::Entry.new
64
+ @controller.should_receive(:handle_exception)
65
+ @controller.sdata_show_instance
66
+ end
67
+ end
68
+
69
+ describe "when record with such id does not exist" do
70
+ before :each do
71
+ Model.should_receive(:find_by_sdata_instance_id).with(@instance_id).and_return(nil)
72
+ end
73
+
74
+ it "should..." do
75
+ pending "wasn't defined yet"
76
+ end
77
+ end
78
+ end
79
+
80
+ describe "whem params does not contain :instance_id key" do
81
+ before :each do
82
+ @controller.stub! :params => Hash.new
83
+ end
84
+
85
+ it "should..." do
86
+ pending "wasn't defined yet"
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "given a model with baze" do
92
+ it "should ..." do
93
+ pending "not yet tested"
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData
4
+
5
+ describe ControllerMixin, "#sdata_update_instance" do
6
+ class BaseClass
7
+ attr_accessor :status
8
+ def id
9
+ 1
10
+ end
11
+
12
+ def self.find(*params)
13
+ self.new
14
+ end
15
+
16
+ def update_attributes(*params)
17
+ self.status = :updated
18
+ self
19
+ end
20
+ end
21
+
22
+ class VirtualModel < SData::VirtualBase
23
+ attr_accessor :baze
24
+ end
25
+
26
+ before :all do
27
+ VirtualModel.stub :baze_class => BaseClass
28
+ Base = Class.new(ActionController::Base)
29
+ Base.extend ControllerMixin
30
+ Base.acts_as_sdata :model => VirtualModel
31
+ VirtualModel.acts_as_sdata
32
+ end
33
+
34
+ before :each do
35
+ pending # not currently supported
36
+ @controller = Base.new
37
+ end
38
+
39
+ describe "given params contain Atom::Entry" do
40
+ before :each do
41
+ @entry = Atom::Entry.new
42
+ @controller.stub! :params => { :entry => @entry, :instance_id => 1},
43
+ :response => OpenStruct.new,
44
+ :request => OpenStruct.new(:fresh? => true)
45
+
46
+ @model = VirtualModel.new(BaseClass.new)
47
+ VirtualModel.should_receive(:new).and_return @model
48
+ end
49
+
50
+ describe "when update is successful" do
51
+ before :each do
52
+ @model.baze.stub! :save => true
53
+ @model.stub! :to_atom => stub(:to_xml => '<entry></entry>')
54
+ end
55
+
56
+ it "should respond with updated" do
57
+ @controller.should_receive(:render) do |args|
58
+ #TODO: what should I check for?.. Returns 1 right now, is this right?
59
+ end
60
+ @controller.sdata_update_instance
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -0,0 +1,84 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ include SData::PayloadMap
4
+
5
+ describe PayloadMapHash do
6
+ context "given a hash with payload map" do
7
+ before :each do
8
+ @hash = {
9
+ :taxation_country => { :baze_field => :country, :precedence => 3 },
10
+ :short_name => { :baze_field => :name, :precedence => 3 },
11
+
12
+ :company_person_flag => { :static_value => 'Company', :precedence => 50 },
13
+ :customer_supplier_flag => { :static_value => 'Customers', :precedence => 50 },
14
+ :nil_value => { :static_value => nil, :precedence => 50 },
15
+ :false_value => { :static_value => false, :precedence => 50 },
16
+
17
+ :billing_addresses => { :proc => lambda { SData::PostalAddress.build_for([self.baze], :billing) },
18
+ :precedence => 3},
19
+
20
+ :thingy_with_deleted => { :proc => lambda { SData::PostalAddress.build_for([self.baze], :billing) },
21
+ :precedence => 3,
22
+ :proc_with_deleted => lambda { puts "I am a lambda" } }
23
+ }
24
+ end
25
+
26
+ context "when PayloadMapHash is initialized with such hash" do
27
+ subject { PayloadMapHash.new(@hash) }
28
+
29
+ it { should be_kind_of(Hash) }
30
+
31
+ describe "#static_values" do
32
+ it "should return only staic values as a key-value hash" do
33
+ subject.static_values.should == {
34
+ :company_person_flag => 'Company',
35
+ :customer_supplier_flag => 'Customers',
36
+ :false_value => false,
37
+ :nil_value => nil
38
+ }
39
+ end
40
+ end
41
+
42
+ describe "#baze_fields" do
43
+ it "should return only baze fields as a key-value hash" do
44
+ subject.baze_fields.should == {
45
+ :taxation_country => :country,
46
+ :short_name => :name
47
+ }
48
+ end
49
+ end
50
+
51
+ describe "#attrs" do
52
+ it "should return attrs as union of static_values & baze_fields" do
53
+ subject.attrs.should == {
54
+ :taxation_country => { :baze_field => :country, :precedence => 3 },
55
+ :short_name => { :baze_field => :name, :precedence => 3 },
56
+
57
+ :company_person_flag => { :static_value => 'Company', :precedence => 50 },
58
+ :customer_supplier_flag => { :static_value => 'Customers', :precedence => 50 },
59
+ :false_value => { :static_value => false, :precedence => 50 },
60
+ :nil_value => { :static_value => nil, :precedence => 50 }
61
+ }
62
+ end
63
+ end
64
+
65
+ describe "#procs" do
66
+ it "should return only stored procs as a field_name-proc hash" do
67
+ subject.procs.should == {
68
+ :billing_addresses => @hash[:billing_addresses][:proc],
69
+ :thingy_with_deleted => @hash[:thingy_with_deleted][:proc]
70
+ }
71
+ end
72
+ end
73
+
74
+ describe "#procs_with_deleted" do
75
+ it "should return proc_with_deleted or otherwise proc as a field_name-proc hash" do
76
+ subject.procs_with_deleted.should == {
77
+ :billing_addresses => @hash[:billing_addresses][:proc],
78
+ :thingy_with_deleted => @hash[:thingy_with_deleted][:proc_with_deleted]
79
+ }
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,144 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe SData::PayloadMap do
4
+ context "given sdata model class extended by SData::PayloadMap" do
5
+ before :all do
6
+ class TradingAccount < SData::VirtualBase
7
+ # extend SData::PayloadMap
8
+ #
9
+ define_payload_map :foo => { :static_value => :bar }
10
+
11
+ # temporary
12
+ def method_missing(meth, *args, &block)
13
+ if @payload
14
+ @payload.send(meth, *args, &block)
15
+ else
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ it "should respond to #payload_map class method" do
23
+ TradingAccount.should respond_to(:define_payload_map)
24
+ end
25
+
26
+ it "should respond to #has_sdata_attr class method" do
27
+ TradingAccount.should respond_to(:has_sdata_attr)
28
+ #QUESTION: do we really need this method to be public?
29
+ end
30
+
31
+ describe "#define_payload_map" do
32
+ context "when mapping leads to static value" do
33
+ before :each do
34
+ TradingAccount.define_payload_map :tax_reference => { :static_value => 'Some static tax reference' }
35
+ end
36
+
37
+ subject { TradingAccount.new(Object) }
38
+
39
+ it { should respond_to(:payload) }
40
+
41
+ context "when correspondent field method is called" do
42
+ it "should return given static value" do
43
+ subject.tax_reference.should == 'Some static tax reference'
44
+ end
45
+ end
46
+
47
+ describe "#payload_map" do
48
+ it "should store it correctly" do
49
+ subject.payload_map[:tax_reference].should == { :static_value => 'Some static tax reference', :method_name => :tax_reference, :method_name_with_deleted=>:tax_reference}
50
+ end
51
+ end
52
+ end
53
+
54
+ context "when mapping leads to a baze field" do
55
+ before :each do
56
+ @baze = Struct.new(:country).new('Kyrgyzstan')
57
+
58
+ TradingAccount.baze_class = Customer
59
+ TradingAccount.define_payload_map :taxation_country => { :baze_field => :country }
60
+ end
61
+
62
+ subject { TradingAccount.new(@baze) }
63
+
64
+ it "should apply to baze class" do
65
+ subject.taxation_country.should == @baze.country
66
+ end
67
+
68
+ it "should not cache value, but fetch it each time" do
69
+ @baze.country = 'Canada'
70
+ subject.taxation_country.should == 'Canada'
71
+ end
72
+ end
73
+
74
+ context "when mapping leads to a proc" do
75
+ before :each do
76
+ @mock = mock("thing", :dynamic => 1, :dynamic_deleted => 3)
77
+ mock = @mock
78
+ TradingAccount.define_payload_map :dynamic_field => { :proc => lambda { mock.dynamic }, :proc_with_deleted => lambda { mock.dynamic_deleted }}
79
+ end
80
+
81
+ subject { TradingAccount.new(Object) }
82
+
83
+ it "should call given lambda each time" do
84
+ @mock.should_receive(:dynamic).twice
85
+ 2.times { subject.dynamic_field }
86
+ end
87
+
88
+ it "should return lambda's return value" do
89
+ expected_return_value = 123456
90
+ @mock.stub! :dynamic => expected_return_value
91
+ subject.dynamic_field.should == expected_return_value
92
+ end
93
+
94
+ it "should set the method_name in the options to the attribute name" do
95
+ subject.payload_map[:dynamic_field][:method_name].should == :dynamic_field
96
+ end
97
+
98
+ it "should create a xxx_with_deleted method" do
99
+ expected_return_value = 498
100
+ @mock.stub! :dynamic_deleted => expected_return_value
101
+ subject.dynamic_field_with_deleted.should == expected_return_value
102
+ end
103
+
104
+ context "consider lambda calls local object methods" do
105
+ before :each do
106
+ TradingAccount.__send__ :attr_accessor, :local_field
107
+ TradingAccount.define_payload_map :access_to_local_field => { :proc => lambda { self.local_field } }
108
+ end
109
+
110
+ subject { TradingAccount.new(Object) }
111
+
112
+ it "should run lambda in context of SData model object" do
113
+ subject.local_field = 654321
114
+ subject.access_to_local_field.should == 654321
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#has_sdata_attr" do
121
+ before :each do
122
+ @mock_baze = mock("thing", :dude => "sweet")
123
+ end
124
+
125
+ subject { TradingAccount.new(@mock_baze) }
126
+
127
+ it "should add a static value attr" do
128
+ TradingAccount.has_sdata_attr :some_static_value, { :static_value => 42 }
129
+ subject.some_static_value.should == 42
130
+ end
131
+
132
+ it "should add a static value attr whose value is nil" do
133
+ TradingAccount.has_sdata_attr :some_nil, { :static_value => nil }
134
+ subject.some_nil.should be_nil
135
+ end
136
+
137
+ it "should add a baze_field attr" do
138
+ TradingAccount.has_sdata_attr :some_baze_value, { :baze_field => :dude }
139
+ subject.some_baze_value.should == "sweet"
140
+ end
141
+
142
+ end
143
+ end
144
+ end