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