acts_as_sdata 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +200 -0
- data/Rakefile +20 -0
- data/VERSION +1 -0
- data/config/sdata.yml +13 -0
- data/config/sdata.yml.example +20 -0
- data/config/sdata.yml.tmpl.staging +14 -0
- data/generators/acts_as_sdata/acts_as_sdata_generator.rb +9 -0
- data/generators/acts_as_sdata/templates/migration.rb +69 -0
- data/init.rb +36 -0
- data/lib/s_data/active_record_extensions/base.rb +7 -0
- data/lib/s_data/active_record_extensions/mixin.rb +157 -0
- data/lib/s_data/active_record_extensions/sdata_uuid_mixin.rb +133 -0
- data/lib/s_data/atom_extensions/content_mixin.rb +14 -0
- data/lib/s_data/atom_extensions/entry_mixin.rb +41 -0
- data/lib/s_data/atom_extensions/nodes/digest.rb +48 -0
- data/lib/s_data/atom_extensions/nodes/payload.rb +34 -0
- data/lib/s_data/atom_extensions/nodes/sync_state.rb +14 -0
- data/lib/s_data/conditions_builder.rb +59 -0
- data/lib/s_data/controller_mixin.rb +11 -0
- data/lib/s_data/controller_mixin/actions.rb +87 -0
- data/lib/s_data/controller_mixin/collection_scope.rb +57 -0
- data/lib/s_data/controller_mixin/s_data_feed.rb +87 -0
- data/lib/s_data/controller_mixin/s_data_instance.rb +35 -0
- data/lib/s_data/diagnosis/application_controller_mixin.rb +16 -0
- data/lib/s_data/diagnosis/diagnosis.rb +130 -0
- data/lib/s_data/diagnosis/diagnosis_mapper.rb +39 -0
- data/lib/s_data/exceptions.rb +10 -0
- data/lib/s_data/formatting.rb +13 -0
- data/lib/s_data/namespace_definitions.rb +19 -0
- data/lib/s_data/payload.rb +158 -0
- data/lib/s_data/payload_map.rb +0 -0
- data/lib/s_data/payload_map/payload_map.rb +136 -0
- data/lib/s_data/payload_map/payload_map_hash.rb +39 -0
- data/lib/s_data/predicate.rb +31 -0
- data/lib/s_data/route_mapper.rb +143 -0
- data/lib/s_data/router_mixin.rb +10 -0
- data/lib/s_data/sync/controller_mixin.rb +122 -0
- data/lib/s_data/sync/sdata_syncing_mixin.rb +17 -0
- data/lib/s_data/virtual_base.rb +114 -0
- data/test/functional/Rakefile +0 -0
- data/test/unit/active_record_mixin/active_record_mixin_spec.rb +20 -0
- data/test/unit/active_record_mixin/acts_as_sdata_spec.rb +41 -0
- data/test/unit/active_record_mixin/find_by_sdata_instance_id_spec.rb +34 -0
- data/test/unit/active_record_mixin/payload_spec.rb +622 -0
- data/test/unit/active_record_mixin/to_atom_spec.rb +85 -0
- data/test/unit/atom_entry_mixin/atom_entry_mixin_spec.rb +11 -0
- data/test/unit/atom_entry_mixin/to_attributes_spec.rb +30 -0
- data/test/unit/class_stubs/address.rb +19 -0
- data/test/unit/class_stubs/contact.rb +25 -0
- data/test/unit/class_stubs/customer.rb +70 -0
- data/test/unit/class_stubs/model_base.rb +17 -0
- data/test/unit/class_stubs/payload.rb +15 -0
- data/test/unit/class_stubs/sd_uuid.rb +28 -0
- data/test/unit/class_stubs/user.rb +40 -0
- data/test/unit/conditions_builder_spec.rb +54 -0
- data/test/unit/controller_mixin/acts_as_sdata_spec.rb +29 -0
- data/test/unit/controller_mixin/build_sdata_feed_spec.rb +50 -0
- data/test/unit/controller_mixin/controller_mixin_spec.rb +22 -0
- data/test/unit/controller_mixin/diagnosis_spec.rb +232 -0
- data/test/unit/controller_mixin/sdata_collection_spec.rb +78 -0
- data/test/unit/controller_mixin/sdata_create_instance_spec.rb +173 -0
- data/test/unit/controller_mixin/sdata_opensearch_and_links_spec.rb +382 -0
- data/test/unit/controller_mixin/sdata_scope/linked_model_spec.rb +58 -0
- data/test/unit/controller_mixin/sdata_scope/non_linked_model_spec.rb +66 -0
- data/test/unit/controller_mixin/sdata_scope/scoping_in_config_spec.rb +64 -0
- data/test/unit/controller_mixin/sdata_show_instance_spec.rb +98 -0
- data/test/unit/controller_mixin/sdata_update_instance_spec.rb +65 -0
- data/test/unit/payload_map/payload_map_hash_spec.rb +84 -0
- data/test/unit/payload_map/payload_map_spec.rb +144 -0
- data/test/unit/predicate_spec.rb +59 -0
- data/test/unit/router_mixin/routes_spec.rb +138 -0
- data/test/unit/spec.opts +4 -0
- data/test/unit/spec_helper.rb +47 -0
- data/test/unit/spec_helpers/nokogiri_extensions.rb +16 -0
- data/test/unit/sync_controller_mixin/controller_mixin_spec.rb +22 -0
- data/test/unit/sync_controller_mixin/sdata_collection_sync_feed_spec.rb +69 -0
- metadata +175 -0
data/.gitignore
ADDED
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,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,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
|
+
|