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
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ nbproject
2
+ .idea
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,200 @@
1
+ h1. acts_as_sdata
2
+
3
+ Ruby implementation of SData (Sage Data) protocol. Rails plugin which enables SData syndication and publishing.
4
+
5
+ h2. Demo
6
+
7
+ Let's consider we want to expose the list of US presidents in SData way (this is exactly what is done in "presidents":http://github.com/DanielVartanov/acts_as_sdata-examples/tree/master/presidents/ application in "acts_as_sdata-examples":http://github.com/DanielVartanov/acts_as_sdata-examples repository).
8
+
9
+ From client side it will look like this:
10
+
11
+ h3. Instance path
12
+
13
+ <code>http://localhost:3000/presidents/!Wilson</code>
14
+
15
+ <pre><?xml version="1.0" encoding="UTF-8"?>
16
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:attributes="http://sdata.sage.com/schemes/attributes">
17
+ <title>Wilson, Woodrow</title>
18
+ <summary>Wilson, Woodrow (1856-1924)</summary>
19
+ <attributes:born_at>1856</attributes:born_at>
20
+ <attributes:id>28</attributes:id>
21
+ <attributes:first_name>Woodrow</attributes:first_name>
22
+ <attributes:last_name>Wilson</attributes:last_name>
23
+ <attributes:party>Democrat</attributes:party>
24
+ <attributes:updated_at>Sat Jan 09 11:43:11 UTC 2010</attributes:updated_at>
25
+ <attributes:died_at>1924</attributes:died_at>
26
+ <attributes:order>28</attributes:order>
27
+ <attributes:term_started_at>1913</attributes:term_started_at>
28
+ <attributes:created_at>Sat Jan 09 11:43:11 UTC 2010</attributes:created_at>
29
+ <attributes:country>USA</attributes:country>
30
+ <attributes:term_ended_at>1921</attributes:term_ended_at>
31
+ </entry></pre>
32
+
33
+ h3. Collection path
34
+
35
+ <code>http://localhost:3000/presidents</code>
36
+
37
+ <pre><?xml version="1.0" encoding="UTF-8"?>
38
+ <feed xmlns="http://www.w3.org/2005/Atom">
39
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
40
+ <title>List of US presidents</title>
41
+ <updated>2009-11-17T10:55:28+06:00</updated>
42
+ <link href="http://example.com/presidents"/>
43
+ <author>
44
+ <name>Sage</name>
45
+ </author>
46
+ <entry>
47
+ <title>Washington, George</title>
48
+ <summary>Washington, George (1732-1799)</summary>
49
+ </entry>
50
+ <entry>
51
+ <title>Adams, John</title>
52
+ <summary>Adams, John (1735-1826)</summary>
53
+ </entry>
54
+ .....
55
+ </pre>
56
+
57
+ h3. Predicate
58
+
59
+ <code>http://localhost:3000/presidents(born_at gt 1900)</code>
60
+
61
+ <pre><?xml version="1.0" encoding="UTF-8"?>
62
+ <feed xmlns="http://www.w3.org/2005/Atom">
63
+ <id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
64
+ <title>List of US presidents</title>
65
+ <updated>2009-11-17T10:58:15+06:00</updated>
66
+ <link href="http://example.com/presidents"/>
67
+ <author>
68
+ <name>Sage</name>
69
+ </author>
70
+ <entry>
71
+ <title>Kennedy, John</title>
72
+ <summary>Kennedy, John (1917-1963)</summary>
73
+ </entry>
74
+ <entry>
75
+ <title>Johnson, Lyndon</title>
76
+ <summary>Johnson, Lyndon (1908-1973)</summary>
77
+ </entry>
78
+ .....
79
+ </pre>
80
+
81
+ h3. Publishing
82
+
83
+ SData protocol supports data publishing (as Atom Publishing Protocol does)
84
+
85
+ Example:
86
+
87
+ <pre>require 'atom/pub'
88
+
89
+ collection = Atom::Pub::Collection.new(:href => 'http://localhost:3000/presidents')
90
+
91
+ entry = Atom::Entry.load_entry <<XML
92
+ <?xml version="1.0" encoding="UTF-8"?>
93
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:attributes="http://sdata.sage.com/schemes/attributes">
94
+ <attributes:born_at>1961</attributes:born_at>
95
+ <attributes:first_name>Barack</attributes:first_name>
96
+ <attributes:last_name>Obama</attributes:last_name>
97
+ <attributes:party>Democrat</attributes:party>
98
+ <attributes:died_at/>
99
+ <attributes:order>44</attributes:order>
100
+ <attributes:term_started_at>2009</attributes:term_started_at>
101
+ <attributes:country>USA</attributes:country>
102
+ <attributes:term_ended_at/>
103
+ </entry>
104
+ XML
105
+
106
+ collection.publish entry</pre>
107
+
108
+ The response will be:
109
+
110
+ <pre>
111
+ <?xml version="1.0" encoding="UTF-8"?>
112
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:attributes="http://sdata.sage.com/schemes/attributes">
113
+ <attributes:id>46</attributes:id>
114
+ <attributes:created_at>Sat Jan 09 14:29:57 UTC 2010</attributes:created_at>
115
+ <attributes:first_name>Barack</attributes:first_name>
116
+ ...
117
+ </entry></pre>
118
+ _with status 201 (Created)_
119
+
120
+ h2. How to install
121
+
122
+ h3. Install Usher
123
+
124
+ _(Usher is an alternative router)_
125
+
126
+ It is better to refer to "Usher's page":http://github.com/joshbuddy/usher/, but usually installation of Usher plugin is just one commad:
127
+
128
+ <code>$ script/plugin install git://github.com/joshbuddy/usher.git</code>
129
+
130
+ h3. Install acts_as_sdata
131
+
132
+ <code>$ script/plugin install git://github.com/DanielVartanov/acts_as_sdata.git</code>
133
+
134
+ h2. How to use
135
+
136
+ In order to make application act as SData you should write the following.
137
+
138
+ h3. In router
139
+
140
+ <code>map.sdata_resource :presidents</code>
141
+ _this line will enable all SData-related paths for /presidents_
142
+
143
+ Also you should include special routing delimiters which are used in SData paths (remember, we are using Usher, this will _NOT_ work with native Rails Router).
144
+
145
+ Change
146
+ <code>ActionController::Routing::Routes.draw do |map|</code>
147
+ to
148
+ <code>ActionController::Routing::Routes.draw(:delimiters => ['/', '.', '!', '\(', '\)' ]) do |map|</code>
149
+
150
+ h3. In controller
151
+
152
+ <pre>acts_as_sdata :model => President,
153
+ :feed => { :id => 'urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6',
154
+ :author => 'Sage',
155
+ :path => '/presidents',
156
+ :title => 'List of US presidents' }</pre>
157
+ _<code>:feed</code> options defines fields of Atom feed_
158
+
159
+ h3. In model
160
+
161
+ <pre>acts_as_sdata :title => lambda { "#{last_name}, #{first_name}" },
162
+ :summary => lambda { "#{last_name}, #{first_name} (#{born_at}-#{died_at})" },
163
+ :instance_id => :last_name</pre>
164
+
165
+ _Note #1: these lambda's will be executed in context of the actual model instance._
166
+
167
+ _Note #2: SData specifications say that human-readable instance id's are preferred. The <code>:instance_id</code> option helps to enable search by any unique attribute._
168
+
169
+ h3. Initializer
170
+
171
+ In config/initializers/mime_types.rb add the following:
172
+
173
+ <pre>ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
174
+ { :entry => Atom::Entry.load_entry(data) }
175
+ end</pre>
176
+
177
+ h2. Removing ambiguity
178
+
179
+ h3. Problem
180
+
181
+ There is an ambigious path in case of exposing same resource in both REST and SData ways:
182
+
183
+ <code>/presidents</code>
184
+
185
+ It might be treated either as #index (REST way) or as #sdata_collection (SData way).
186
+
187
+ h3. Solution
188
+
189
+ In order to remove this ambiguity, a :formatted_paths option can be given as a second parameter to #sdata_resource in routes.rb:
190
+
191
+ <code>map.sdata_resource :presidents, :formatted_paths => true</code>
192
+
193
+ Then,
194
+ <code>/presidents</code> routes to Presidents#index and returns HTML
195
+ <code>/presidents.sdata</code> routes to Presidents#sdata_collection and returns Atom/XML
196
+
197
+ <i>without any changes in the controller code!</i>
198
+
199
+ Copyright Sage 2009
200
+ Released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+
6
+ Jeweler::Tasks.new do |gemspec|
7
+ gemspec.name = "acts_as_sdata"
8
+ gemspec.version = '1.0.0'
9
+ gemspec.authors = ["Daniel Vartanov", "Eugene Gilburg", "Michael Johnston"].sort
10
+ gemspec.email = "dan@vartanov.net"
11
+ gemspec.homepage = "http://sdata.sage.com/"
12
+ gemspec.summary = gemspec.description = "Ruby implementation of SData (Sage Data) protocol"
13
+ gemspec.has_rdoc = false
14
+ gemspec.extra_rdoc_files = ["README.textile"]
15
+ end
16
+
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: gem install jeweler"
20
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/config/sdata.yml ADDED
@@ -0,0 +1,13 @@
1
+ schemas:
2
+ xs: http://www.w3.org/2001/XMLSchema
3
+ cf: http://www.microsoft.com/schemas/rss/core/2005
4
+ sme: http://schemas.sage.com/sdata/sme/2007
5
+ sc: http://schemas.sage.com/sc/2009
6
+ http: http://schemas.sage.com/sdata/http/2008/1
7
+ sync: http://schemas.sage.com/sdata/sync/2008/1
8
+ opensearch: http://a9.com/-/spec/opensearch/1.1/
9
+ sdata: http://schemas.sage.com/sdata/2008/1
10
+ xsi: http://www.w3.org/2001/XMLSchema-instance
11
+ sle: http://www.microsoft.com/schemas/rss/core/2005
12
+
13
+ show_stack_trace: true
@@ -0,0 +1,20 @@
1
+ # example of app config adding to or overriding the standard plugin config
2
+
3
+ # required: at least 1 contract
4
+ schemas:
5
+ crmErp: http://schemas.sage.com/crmErp/2008
6
+
7
+ # required, the host-with-protocol of your server
8
+ base_url: http://example.com
9
+ # your sdata application name (required in sdata urls)
10
+ application: billingboss
11
+ # the default contract supported by your app (required in sdata urls)
12
+ contracts:
13
+ - crmErp
14
+ - bb
15
+
16
+ # default is 0, lower gets priority
17
+ sync_endpoint_conflict_priority: 10
18
+
19
+ # optional
20
+ show_stack_trace: true
@@ -0,0 +1,14 @@
1
+ schemas:
2
+ xs: http://www.w3.org/2001/XMLSchema
3
+ cf: http://www.microsoft.com/schemas/rss/core/2005
4
+ sme: http://schemas.sage.com/sdata/sme/2007
5
+ sc: http://schemas.sage.com/sc/2009
6
+ http: http://schemas.sage.com/sdata/http/2008/1
7
+ sync: http://schemas.sage.com/sdata/sync/2008/1
8
+ opensearch: http://a9.com/-/spec/opensearch/1.1/
9
+ sdata: http://schemas.sage.com/sdata/2008/1
10
+ xsi: http://www.w3.org/2001/XMLSchema-instance
11
+ sle: http://www.microsoft.com/schemas/rss/core/2005
12
+
13
+ show_stack_trace: true
14
+
@@ -0,0 +1,9 @@
1
+ class ActsAsSdataGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.migration_template "migration.rb", 'db/migrate',
5
+ :migration_file_name => "create_sdata_database_tables"
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,69 @@
1
+ create_table "sd_digest_entries", :force => true do |t|
2
+ t.integer "sd_digest_id"
3
+ t.integer "tick"
4
+ t.string "endpoint"
5
+ t.integer "conflict_priority"
6
+ t.datetime "created_at"
7
+ t.datetime "updated_at"
8
+ end
9
+
10
+ add_index "sd_digest_entries", ["sd_digest_id", "endpoint"], :name => "index_sd_digest_entries_on_sd_digest_id_and_endpoint", :unique => true
11
+
12
+ create_table "sd_digests", :force => true do |t|
13
+ t.integer "created_by_id"
14
+ t.string "sd_class"
15
+ t.datetime "locked_at"
16
+ t.datetime "created_at"
17
+ t.datetime "updated_at"
18
+ end
19
+
20
+ add_index "sd_digests", ["created_by_id", "sd_class"], :name => "index_sd_digests_on_created_by_id_and_sd_class", :unique => true
21
+
22
+ create_table "sd_sync_runs", :force => true do |t|
23
+ t.integer "created_by_id"
24
+ t.string "tracking_id"
25
+ t.string "run_name"
26
+ t.integer "lock_version"
27
+ t.string "sd_class"
28
+ t.string "sync_mode"
29
+ t.text "objects_data", :limit => 2147483647
30
+ t.text "target_digest_data"
31
+ t.text "source_digest_data"
32
+ t.string "phase"
33
+ t.string "phase_detail"
34
+ t.datetime "created_at"
35
+ t.datetime "updated_at"
36
+ end
37
+
38
+ create_table "sd_sync_states", :force => true do |t|
39
+ t.integer "sd_digest_id"
40
+ t.integer "sd_uuid_id"
41
+ t.integer "tick"
42
+ t.datetime "created_at"
43
+ t.datetime "updated_at"
44
+ t.string "endpoint"
45
+ end
46
+
47
+ add_index "sd_sync_states", ["sd_digest_id", "sd_uuid_id"], :name => "index_sd_sync_states_on_sd_digest_id_and_sd_uuid_id", :unique => true
48
+
49
+ create_table "sd_ticks", :force => true do |t|
50
+ t.integer "user_id"
51
+ t.integer "tick"
52
+ t.datetime "locked_at"
53
+ t.datetime "created_at"
54
+ t.datetime "updated_at"
55
+ end
56
+
57
+ add_index "sd_ticks", ["user_id"], :name => "index_sd_ticks_on_user_id", :unique => true
58
+
59
+ create_table "sd_uuids", :force => true do |t|
60
+ t.string "sd_class"
61
+ t.integer "bb_model_id"
62
+ t.string "bb_model_type"
63
+ t.string "uuid"
64
+ t.datetime "created_at"
65
+ t.datetime "updated_at"
66
+ end
67
+
68
+ add_index "sd_uuids", ["sd_class", "bb_model_type", "uuid"], :name => "index_sd_uuids_on_sd_class_and_bb_model_type_and_uuid", :unique => true
69
+
data/init.rb ADDED
@@ -0,0 +1,36 @@
1
+ # RADAR: The order makes a difference, and globbing Dir produces different results on different machines.
2
+ # We can .sort the glob, but still the order would be wrong, and we'd need to have repeated requires in
3
+ # Individual files. Michael and I think it's easier to statically call them all in one place in the right order.
4
+
5
+ __DIR__ = File.dirname(__FILE__)
6
+ require File.join(__DIR__, 'lib', 's_data')
7
+ require File.join(__DIR__, 'lib', 's_data', 'exceptions')
8
+ require File.join(__DIR__, 'lib', 's_data', 'formatting.rb')
9
+ require File.join(__DIR__, 'lib', 's_data', 'router_mixin.rb')
10
+ require File.join(__DIR__, 'lib', 's_data', 'sync', 'sdata_syncing_mixin.rb')
11
+ require File.join(__DIR__, 'lib', 's_data', 'sync', 'controller_mixin.rb')
12
+ require File.join(__DIR__, 'lib', 's_data', 'payload_map.rb')
13
+ require File.join(__DIR__, 'lib', 's_data', 'controller_mixin', 's_data_instance.rb')
14
+ require File.join(__DIR__, 'lib', 's_data', 'controller_mixin', 's_data_feed.rb')
15
+ require File.join(__DIR__, 'lib', 's_data', 'controller_mixin', 'collection_scope.rb')
16
+ require File.join(__DIR__, 'lib', 's_data', 'controller_mixin', 'actions.rb')
17
+ require File.join(__DIR__, 'lib', 's_data', 'atom_extensions', 'nodes', 'digest.rb')
18
+ require File.join(__DIR__, 'lib', 's_data', 'atom_extensions', 'nodes', 'payload.rb')
19
+ require File.join(__DIR__, 'lib', 's_data', 'atom_extensions', 'nodes', 'sync_state.rb')
20
+ require File.join(__DIR__, 'lib', 's_data', 'payload.rb')
21
+ require File.join(__DIR__, 'lib', 's_data', 'route_mapper.rb')
22
+ require File.join(__DIR__, 'lib', 's_data', 'atom_extensions', 'content_mixin.rb')
23
+ require File.join(__DIR__, 'lib', 's_data', 'atom_extensions', 'entry_mixin.rb')
24
+ require File.join(__DIR__, 'lib', 's_data', 'namespace_definitions.rb')
25
+ require File.join(__DIR__, 'lib', 's_data', 'predicate.rb')
26
+ require File.join(__DIR__, 'lib', 's_data', 'active_record_extensions', 'mixin.rb')
27
+ require File.join(__DIR__, 'lib', 's_data', 'active_record_extensions', 'sdata_uuid_mixin.rb')
28
+ require File.join(__DIR__, 'lib', 's_data', 'active_record_extensions', 'base.rb')
29
+ require File.join(__DIR__, 'lib', 's_data', 'virtual_base.rb')
30
+ require File.join(__DIR__, 'lib', 's_data', 'conditions_builder.rb')
31
+ require File.join(__DIR__, 'lib', 's_data', 'controller_mixin.rb')
32
+ require File.join(__DIR__, 'lib', 's_data', 'payload_map', 'payload_map_hash.rb')
33
+ require File.join(__DIR__, 'lib', 's_data', 'payload_map', 'payload_map.rb')
34
+ require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'diagnosis.rb')
35
+ require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'application_controller_mixin.rb')
36
+ require File.join(__DIR__, 'lib', 's_data', 'diagnosis', 'diagnosis_mapper.rb')
@@ -0,0 +1,7 @@
1
+ module SData
2
+ module ActiveRecordExtensions
3
+ class Base < ::ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,157 @@
1
+ require 'forwardable'
2
+
3
+ module SData
4
+ module ActiveRecordExtensions
5
+ module Mixin
6
+ def acts_as_sdata(options={})
7
+ cattr_accessor :sdata_options
8
+ self.sdata_options = options
9
+ self.__send__ :include, InstanceMethods
10
+ self.__send__ :extend, ClassMethods
11
+ end
12
+
13
+ def find_by_sdata_instance_id(value)
14
+ attribute = self.sdata_options[:instance_id]
15
+
16
+ attribute.nil? ?
17
+ self.find(value.to_i) :
18
+ self.first(:conditions => { attribute => value })
19
+ end
20
+
21
+ module ClassMethods
22
+
23
+ def sdata_node_name(entity=self)
24
+ self.name.demodulize.camelize(:lower)
25
+ end
26
+
27
+ def sdata_contract_name
28
+ SData.sdata_contract_name(self.name)
29
+ end
30
+
31
+ def sdata_resource_kind_url(dataset)
32
+ #FIXME: will change when we support bk use cases
33
+ postfix = self.sdata_node_name.pluralize
34
+ "#{SData.endpoint}/#{dataset}/#{postfix}"
35
+ end
36
+
37
+ def sdata_date(date_time)
38
+ SData::Formatting.format_date_time(date_time)
39
+ end
40
+
41
+ end
42
+
43
+ module InstanceMethods
44
+ def to_atom(params={}, opts={})
45
+ opts = {
46
+ :atom_show_categories => true,
47
+ :atom_show_links => true,
48
+ :atom_show_authors => true
49
+ }.merge(opts)
50
+ maximum_precedence = (!params[:precedence].blank? ? params[:precedence].to_i : 100)
51
+ included = params[:include].to_s.split(',')
52
+ selected = params[:select].to_s.split(',')
53
+ dataset = params[:dataset] #Maybe || '-' but I don't think it's a good idea to imply a default dataset at this level
54
+
55
+ query = "?#{clean_params(params).to_query}".chomp('?')
56
+ sync = (params[:sync].to_s == 'true')
57
+ expand = ((sync || included.include?('$children')) ? :all_children : :immediate_children)
58
+
59
+ returning Atom::Entry.new do |entry|
60
+ entry.id = self.sdata_resource_url(dataset)
61
+ entry.title = entry_title
62
+ entry.updated = self.class.sdata_date(self.updated_at)
63
+ entry.authors << Atom::Person.new(:name => self.respond_to?('author') ? self.author : sdata_default_author) if opts[:atom_show_authors]
64
+ entry.links << Atom::Link.new(:rel => 'self',
65
+ :href => "#{self.sdata_resource_url(dataset)}#{query}",
66
+ :type => 'application/atom+xml; type=entry',
67
+ :title => 'Refresh') if opts[:atom_show_links]
68
+ entry.categories << Atom::Category.new(:scheme => 'http://schemas.sage.com/sdata/categories',
69
+ :term => self.sdata_node_name,
70
+ :label => self.sdata_node_name.underscore.humanize.titleize) if opts[:atom_show_categories]
71
+
72
+ yield entry if block_given?
73
+
74
+ if maximum_precedence > 0
75
+ begin
76
+ payload = Payload.new(:included => included,
77
+ :selected => selected,
78
+ :maximum_precedence => maximum_precedence,
79
+ :sync => sync,
80
+ :contract => self.sdata_contract_name,
81
+ :entity => self,
82
+ :expand => expand,
83
+ :dataset => dataset)
84
+ payload.generate!
85
+ entry.sdata_payload = payload
86
+ rescue Exception => e
87
+ entry.diagnosis = Atom::Content::Diagnosis.new(ApplicationDiagnosis.new(:exception => e).to_xml(:entry))
88
+ end
89
+ end
90
+ entry.content = sdata_content
91
+ end
92
+ end
93
+
94
+ def sdata_name
95
+ self.class.name.demodulize
96
+ end
97
+
98
+ def sdata_node_name(entity=self.class)
99
+ self.class.sdata_node_name(entity)
100
+ end
101
+
102
+ def sdata_resource_url(dataset)
103
+ self.class.sdata_resource_kind_url(dataset) + "('#{self.id}')"
104
+ end
105
+
106
+ def resource_header_attributes(dataset, included)
107
+ hash = {}
108
+ hash.merge!({"sdata:key" => self.id, "sdata:url" => self.sdata_resource_url(dataset)}) if self.id
109
+ hash.merge!("sdata:descriptor" => self.entry_content) if included.include?("$descriptor")
110
+ hash.merge!("sdata:uuid" => self.uuid.to_s) if self.respond_to?("uuid") && !self.uuid.blank?
111
+ hash
112
+ end
113
+
114
+ protected
115
+
116
+ def sdata_contract_name
117
+ self.class.sdata_contract_name
118
+ end
119
+
120
+ def sdata_default_author
121
+ "Billing Boss"
122
+ end
123
+
124
+ def entry_title
125
+ title_proc = self.sdata_options[:title]
126
+ title_proc ? instance_eval(&title_proc) : default_entity_title
127
+ end
128
+
129
+ def default_entity_title
130
+ "#{self.class.name.demodulize.titleize} #{id}"
131
+ end
132
+
133
+ def entry_content
134
+ content_proc = self.sdata_options[:content]
135
+ content_proc ? instance_eval(&content_proc) : default_entry_content
136
+ end
137
+
138
+ def default_entry_content
139
+ self.class.name
140
+ end
141
+
142
+ # Thought about passing query_params from controller so wouldn't need this, but it causes other problems
143
+ # (dataset needs to be passed)
144
+ def clean_params(params)
145
+ params.stringify_keys.reject{|key,value|['action','controller','instance_id','dataset'].include?(key)}
146
+ end
147
+
148
+ end
149
+ end
150
+ end
151
+ end
152
+ # Extension of ActiveRecord removed due to refactoring. Now SData::VirtualBase is extended instead.
153
+ # Not sure yet if there is a case where we DO need to extend ActiveRecord directly.
154
+ # Might considering merging those two classes otherwise.
155
+ # RADAR: Tested refactoring, but it could still be buggy if I missed something! Watch out.
156
+
157
+