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