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,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