infopark_cloud_connector 6.8.0.beta.200.621.4c8e1b0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +71 -0
- data/lib/infopark_cloud_connector.rb +19 -0
- data/lib/rails_connector/attribute.rb +35 -0
- data/lib/rails_connector/blob.rb +66 -0
- data/lib/rails_connector/cache.rb +35 -0
- data/lib/rails_connector/cache_middleware.rb +17 -0
- data/lib/rails_connector/chain.rb +177 -0
- data/lib/rails_connector/cms_base_model.rb +61 -0
- data/lib/rails_connector/content_cache.rb +24 -0
- data/lib/rails_connector/controller_runtime.rb +35 -0
- data/lib/rails_connector/couch_blob.rb +44 -0
- data/lib/rails_connector/couchdb_views_source_path.rb +7 -0
- data/lib/rails_connector/date_attribute.rb +28 -0
- data/lib/rails_connector/default_search_request.rb +6 -0
- data/lib/rails_connector/elasticsearch_request.rb +78 -0
- data/lib/rails_connector/errors.rb +9 -0
- data/lib/rails_connector/link.rb +135 -0
- data/lib/rails_connector/log_subscriber.rb +29 -0
- data/lib/rails_connector/named_link.rb +72 -0
- data/lib/rails_connector/obj.rb +622 -0
- data/lib/rails_connector/obj_body.rb +60 -0
- data/lib/rails_connector/obj_class.rb +35 -0
- data/lib/rails_connector/path_conversion.rb +17 -0
- data/lib/rails_connector/permission.rb +39 -0
- data/lib/rails_connector/rack_middlewares.rb +6 -0
- data/lib/rails_connector/s3_blob.rb +67 -0
- data/lib/rails_connector/version.rb +39 -0
- data/lib/rails_connector/workspace.rb +139 -0
- data/lib/rails_connector/workspace_label.rb +10 -0
- data/lib/rails_connector/workspace_selection_middleware.rb +45 -0
- metadata +169 -0
data/README
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
= Infopark Rails Connector
|
2
|
+
|
3
|
+
Infopark Rails Connector allows you to integrate a number of ready-to-run features into your Rails application.
|
4
|
+
|
5
|
+
== Configuring Access to CMS Data
|
6
|
+
|
7
|
+
Place a file named <tt>rails_connector.yml</tt> into your Rails application's <tt>config</tt> directory.
|
8
|
+
The file should contain the following data:
|
9
|
+
|
10
|
+
cms_database_url: "http://<user>:<password>@<db_host>:<db_port>/<db_name>"
|
11
|
+
aws_access_key_id: "<amazon access key id>"
|
12
|
+
aws_secret_access_key: "<amazon secret access key>"
|
13
|
+
s3_bucket_name: "<s3_bucket_name>"
|
14
|
+
search: "http://<user>:<password>@<search_host>:<search_port>"
|
15
|
+
|
16
|
+
|
17
|
+
== Enabling Addons
|
18
|
+
Here is how all available addons would be enabled in the <tt>rails_connector.rb</tt> initializer file of your application:
|
19
|
+
|
20
|
+
RailsConnector::Configuration.enable(
|
21
|
+
:comments,
|
22
|
+
:google_analytics,
|
23
|
+
:infopark_tracking,
|
24
|
+
:omc,
|
25
|
+
:pdf_generator,
|
26
|
+
:ratings,
|
27
|
+
:rss,
|
28
|
+
:search,
|
29
|
+
:seo_sitemap,
|
30
|
+
:time_machine
|
31
|
+
)
|
32
|
+
|
33
|
+
== Layout Helpers
|
34
|
+
Some of those functions depend on html or javascript code being included in the application layout.
|
35
|
+
For example, Google Analytics does only track pageviews if their javascript snippet is included
|
36
|
+
at the bottom of the html body.
|
37
|
+
|
38
|
+
Rails Connector provides two helpers which manage that kind of layout code,
|
39
|
+
switching on or off parts depending on what features are enabled or not:
|
40
|
+
|
41
|
+
rails_connector_header_tags
|
42
|
+
|
43
|
+
This helper needs to be called inside the html head tag.
|
44
|
+
|
45
|
+
rails_connector_after_content_tags
|
46
|
+
|
47
|
+
That helper is to be put right before the closing body tag.
|
48
|
+
|
49
|
+
== Generators
|
50
|
+
Infopark Rails Connector comes with three generators to help you get up and running quickly:
|
51
|
+
|
52
|
+
<tt>rails generate rails_connector:install</tt>
|
53
|
+
|
54
|
+
Adds Time machine assets, a +rails_connector.rb+ initializer and rake tasks specific to the gem to your application.
|
55
|
+
|
56
|
+
<tt>rails generate rails_connector:comments</tt>
|
57
|
+
|
58
|
+
Specific to the +comments+ feature. Adds a comments migration to your application.
|
59
|
+
|
60
|
+
<tt>rails generate rails_connector:ratings</tt>
|
61
|
+
|
62
|
+
Specific to the +ratings+ feature. Adds a ratings migration to your application as well as an example stylesheet and background image.
|
63
|
+
|
64
|
+
== Infopark Tracking
|
65
|
+
|
66
|
+
If you enable the infopark_tracking, please refer to the OMC documentation for further details,
|
67
|
+
e.g. Apache logging options.
|
68
|
+
|
69
|
+
== Seo Sitemap
|
70
|
+
|
71
|
+
The default implementation for the seo sitemap will feature all files from your CMS in the generated <tt>sitemap.xml</tt>. For a large number of files, this can slow down your server. Please refer to <tt>RailsConnector::SEO::ClassMethods#find_all_for_sitemap</tt> for advise on optimization.
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rails_connector/rack_middlewares'
|
2
|
+
require 'rails_connector/couchdb_views_source_path'
|
3
|
+
require 'rails_connector/core_extensions'
|
4
|
+
require "rails_connector/errors"
|
5
|
+
|
6
|
+
module RailsConnector
|
7
|
+
def self.autoload_all_sources
|
8
|
+
source_files = Dir.glob(File.expand_path("../rails_connector/*.rb", __FILE__)).map do |file|
|
9
|
+
File.basename(file)
|
10
|
+
end
|
11
|
+
|
12
|
+
source_files.each do |file|
|
13
|
+
name = file.gsub(".rb", "")
|
14
|
+
autoload name.camelcase, "rails_connector/#{name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
autoload_all_sources
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#:enddoc:
|
2
|
+
module RailsConnector
|
3
|
+
|
4
|
+
class Attribute < CmsBaseModel
|
5
|
+
def self.type_of(name)
|
6
|
+
attribute = attribute_cache[name.to_s]
|
7
|
+
attribute.type if attribute
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.reset_cache
|
11
|
+
@attribute_cache = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
attribute_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def type
|
19
|
+
attribute_type.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
private
|
24
|
+
|
25
|
+
def attribute_cache
|
26
|
+
@attribute_cache ||= find(:all).each_with_object({}) do |attr, map|
|
27
|
+
map[attr.name] = attr
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
extend ClassMethods
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "aws-sdk"
|
2
|
+
|
3
|
+
module RailsConnector
|
4
|
+
|
5
|
+
class Blob
|
6
|
+
|
7
|
+
def self.find(id, options)
|
8
|
+
new(blob_class.find(id, options))
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.config
|
12
|
+
@config or raise "Blob storage has not been configured yet"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.configure(spec)
|
16
|
+
@config = spec.symbolize_keys
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.blob_class
|
20
|
+
@blob_class ||= blob_class_from_config
|
21
|
+
end
|
22
|
+
|
23
|
+
# for testing purposes
|
24
|
+
def self.reset_blob_class_cache # :nodoc:
|
25
|
+
@blob_class = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.blob_class_from_config
|
29
|
+
blob_class =
|
30
|
+
case config[:type]
|
31
|
+
when "s3"
|
32
|
+
S3Blob
|
33
|
+
when "couch"
|
34
|
+
CouchBlob
|
35
|
+
else
|
36
|
+
raise "Unsupported blob storage type #{config[:type]}"
|
37
|
+
end
|
38
|
+
blob_class.configure(config)
|
39
|
+
blob_class
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :instance
|
43
|
+
|
44
|
+
def initialize(blob_instance)
|
45
|
+
@instance = blob_instance
|
46
|
+
end
|
47
|
+
|
48
|
+
def id
|
49
|
+
instance.id
|
50
|
+
end
|
51
|
+
|
52
|
+
def url
|
53
|
+
instance.url
|
54
|
+
end
|
55
|
+
|
56
|
+
def content_type
|
57
|
+
instance.content_type
|
58
|
+
end
|
59
|
+
|
60
|
+
def length
|
61
|
+
instance.length
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end # module RailsConnector
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#:enddoc:
|
2
|
+
module RailsConnector
|
3
|
+
|
4
|
+
class Cache
|
5
|
+
@cache = {}
|
6
|
+
@context = false
|
7
|
+
|
8
|
+
def self.begin_context
|
9
|
+
@context = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.end_context
|
13
|
+
@context = false
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.in_context?
|
18
|
+
@context
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.load(key)
|
22
|
+
@cache[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.store(key, value)
|
26
|
+
@cache[key] = value if @context
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.reset
|
30
|
+
@cache.clear
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # module RailsConnector
|
35
|
+
|
@@ -0,0 +1,177 @@
|
|
1
|
+
#:enddoc:
|
2
|
+
module RailsConnector
|
3
|
+
|
4
|
+
class Chain
|
5
|
+
# the id of the content cache to use, or nil
|
6
|
+
attr_reader :content_cache
|
7
|
+
|
8
|
+
# a chain of patch operations that can be used to construct the target workspace
|
9
|
+
# each chain member is an array with two elements.
|
10
|
+
# the first element denotes the patch type (-1, 1), where
|
11
|
+
# 1 => apply the workspace
|
12
|
+
# -1 => revert the workspace
|
13
|
+
# and the second element is the workspace.
|
14
|
+
attr_reader :patches
|
15
|
+
|
16
|
+
def self.build_for(workspace, content_cache = nil)
|
17
|
+
if content_cache && content_cache.workspace
|
18
|
+
patches = patches_to(workspace, content_cache.workspace)
|
19
|
+
|
20
|
+
if content_cache.transfer_target
|
21
|
+
if patches.present? && patches.first.second == content_cache.transfer_target
|
22
|
+
# the transfer target is already included in the chain - no extra patch needed!
|
23
|
+
else
|
24
|
+
patches.unshift([-1, content_cache.transfer_target])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
new(content_cache, patches)
|
29
|
+
else
|
30
|
+
new(nil, patches_to(workspace))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.query_counter
|
35
|
+
@query_counter || 0
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.count_query
|
39
|
+
@query_counter = query_counter + 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(content_cache, patches)
|
43
|
+
@content_cache = content_cache
|
44
|
+
@patches = patches
|
45
|
+
end
|
46
|
+
|
47
|
+
# performs a workspace aware query on the cms database.
|
48
|
+
#
|
49
|
+
# returns a hash of results from the query.
|
50
|
+
# the keys are the criterion by which the query results are grouped (typically the OBJ ID).
|
51
|
+
# the values are tuples where
|
52
|
+
# the first element denotes the diff type and
|
53
|
+
# the second the document rows as returned by couchdb.
|
54
|
+
def query(index, key, is_compound_index)
|
55
|
+
self.class.count_query
|
56
|
+
result_map = perform_queries(index, key, is_compound_index)
|
57
|
+
version_map = result_map.merge(result_map) do |ignore, results|
|
58
|
+
results.map {|row| Version.new(row) }
|
59
|
+
end
|
60
|
+
workspace_aware_results(version_map)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# calculate a list of patches to get to the destionation workspace.
|
66
|
+
# if origin is given, the list will start at origin.
|
67
|
+
# otherwise the list will start at the initial workspace.
|
68
|
+
def self.patches_to(destination, origin = nil)
|
69
|
+
if destination.nil? && origin.nil?
|
70
|
+
[]
|
71
|
+
elsif origin.nil? || origin.generation < destination.generation
|
72
|
+
patches_to(destination.base_workspace, origin) + [[1, destination]]
|
73
|
+
elsif destination.nil? || origin.generation > destination.generation
|
74
|
+
[[-1, origin]] + patches_to(destination, origin.base_workspace)
|
75
|
+
elsif origin != destination
|
76
|
+
[[-1, origin]] + patches_to(destination.base_workspace, origin.base_workspace) + [[1, destination]]
|
77
|
+
else # origin == destination
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def perform_queries(index, key, is_compound_index)
|
83
|
+
result_map = {}
|
84
|
+
|
85
|
+
query_options = {
|
86
|
+
:hash_value => key,
|
87
|
+
}
|
88
|
+
query_options[:range_value_extender] = "/" if is_compound_index
|
89
|
+
|
90
|
+
if content_cache.present?
|
91
|
+
result_map[:content_cache] = CmsBaseModel.query_index(:obj, index, query_options.merge({
|
92
|
+
:range_value => "cc/#{content_cache.id}",
|
93
|
+
}))
|
94
|
+
end
|
95
|
+
if patches.present?
|
96
|
+
result_map[:patches] = CmsBaseModel.query_index(:obj, index, query_options.merge({
|
97
|
+
:range_value => workspace_range,
|
98
|
+
}))
|
99
|
+
end
|
100
|
+
|
101
|
+
result_map
|
102
|
+
end
|
103
|
+
|
104
|
+
# filter out those results that aren't on the chain.
|
105
|
+
# order the remaining results according to their chain position
|
106
|
+
def order_results_by_chain(result_map)
|
107
|
+
results = []
|
108
|
+
|
109
|
+
if result_map[:content_cache].present?
|
110
|
+
results << [1, result_map[:content_cache]]
|
111
|
+
end
|
112
|
+
if result_map[:patches].present?
|
113
|
+
results_by_workspace = result_map[:patches].group_by do |version|
|
114
|
+
version.workspace_id
|
115
|
+
end
|
116
|
+
patches.each do |(patch_diff_type, workspace)|
|
117
|
+
results_for_workspace = results_by_workspace[workspace.id]
|
118
|
+
results << [patch_diff_type, results_for_workspace] if results_for_workspace
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
results
|
123
|
+
end
|
124
|
+
|
125
|
+
# group the results by obj id
|
126
|
+
# i.e. calculate a separate chain for each obj
|
127
|
+
def group_results(result_map)
|
128
|
+
results_by_chain = order_results_by_chain(result_map)
|
129
|
+
|
130
|
+
result_index = {}
|
131
|
+
results_by_chain.each do |(patch_diff_type, versions)|
|
132
|
+
versions.each do |version|
|
133
|
+
id = version.obj_id
|
134
|
+
result_index[id] ||= []
|
135
|
+
result_index[id] << [patch_diff_type, version]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
result_index
|
140
|
+
end
|
141
|
+
|
142
|
+
# apply the diff chain for each of obj
|
143
|
+
# to determine which of them are included in the final workspace
|
144
|
+
def workspace_aware_results(result_map)
|
145
|
+
result_index = {}
|
146
|
+
|
147
|
+
# note: using merge as a map function for hashes
|
148
|
+
group_results(result_map).each do |id, apply_chain|
|
149
|
+
final_existance = apply_chain.inject(0) do |temp_existance, (patch_diff_type, version)|
|
150
|
+
# remember: -1 (revert) multiplied with -1 (revert) equals 1 (apply)
|
151
|
+
# and: +1 (create) added to -1 (delete) equals 0 (not existant)
|
152
|
+
temp_existance + patch_diff_type * version.diff
|
153
|
+
end
|
154
|
+
if final_existance > 0
|
155
|
+
patch_diff, version = apply_chain.last
|
156
|
+
result_index[id] = version.obj_data(patch_diff)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
result_index
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def workspace_range
|
166
|
+
@workspace_range ||=
|
167
|
+
begin
|
168
|
+
sorted_keys = patches.map do |(type, workspace)|
|
169
|
+
"#{"%010d" % workspace.generation}/#{workspace.id}"
|
170
|
+
end.sort
|
171
|
+
Range.new(sorted_keys.first, sorted_keys.last)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end # module RailsConnector
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "kvom"
|
2
|
+
require "kvom/adapter/couchdb_adapter"
|
3
|
+
require "kvom/adapter/dynamodb_adapter"
|
4
|
+
|
5
|
+
module RailsConnector
|
6
|
+
|
7
|
+
# This is the abstract class from which all CMS models derive.
|
8
|
+
#
|
9
|
+
class CmsBaseModel < Kvom::Base #:nodoc:
|
10
|
+
class << self
|
11
|
+
def instance_name=(ignore) # :nodoc:
|
12
|
+
# this method is here only for compatibility with the fiona connector.
|
13
|
+
end
|
14
|
+
|
15
|
+
def adapter
|
16
|
+
@@cms_database_adapter
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure_database(connection_spec)
|
20
|
+
@@cms_database_adapter =
|
21
|
+
case connection_spec["type"]
|
22
|
+
when "couch"
|
23
|
+
Kvom::Adapter::CouchdbAdapter.new(connection_spec)
|
24
|
+
when "dynamo"
|
25
|
+
Kvom::Adapter::DynamodbAdapter.new(connection_spec)
|
26
|
+
else
|
27
|
+
raise "Unexpected database type #{connection_spec["type"]}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_index(model, index, params = {})
|
32
|
+
hash_value = "#{model}/#{index}/#{params[:hash_value]}"
|
33
|
+
range_value = params[:range_value] || ""
|
34
|
+
range_value_extender = params[:range_value_extender]
|
35
|
+
|
36
|
+
# up to this point: backend independent
|
37
|
+
|
38
|
+
# this may be backend dependent (suffix character)
|
39
|
+
if range_value_extender
|
40
|
+
start_key, end_key =
|
41
|
+
case range_value
|
42
|
+
when Range
|
43
|
+
[range_value.begin, range_value.end]
|
44
|
+
else
|
45
|
+
[range_value, range_value]
|
46
|
+
end
|
47
|
+
range_value = Range.new(start_key + range_value_extender, end_key + range_value_extender + "~")
|
48
|
+
end
|
49
|
+
|
50
|
+
adapter.query(hash_value, range_value)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def save
|
56
|
+
raise "CmsBaseModel is read-only"
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#:enddoc:
|
2
|
+
module RailsConnector
|
3
|
+
|
4
|
+
class ContentCache < CmsBaseModel
|
5
|
+
self.key_prefix = "cc"
|
6
|
+
|
7
|
+
property :workspace_id
|
8
|
+
property :transferring_to
|
9
|
+
|
10
|
+
# returns the workspace that is represented by this content cache.
|
11
|
+
# returns nil if the workspace cannot be found.
|
12
|
+
def workspace
|
13
|
+
@workspace ||= Workspace.find_by_id(workspace_id)
|
14
|
+
end
|
15
|
+
|
16
|
+
# returns the workspace that this content cache is currently transferring to.
|
17
|
+
# returns nil if no transfer is taking place or if the workspace cannot be found.
|
18
|
+
def transfer_target
|
19
|
+
@transfer_target ||= transferring_to ? Workspace.find_by_id(transferring_to) : nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end # module RailsConnector
|
24
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/core_ext/module/attr_internal'
|
2
|
+
|
3
|
+
module RailsConnector
|
4
|
+
|
5
|
+
module ControllerRuntime
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
attr_internal :rc_runtime
|
11
|
+
|
12
|
+
def cleanup_view_runtime
|
13
|
+
rc_rt_before_render = RailsConnector::LogSubscriber.reset_runtime
|
14
|
+
runtime = super
|
15
|
+
rc_rt_after_render = RailsConnector::LogSubscriber.reset_runtime
|
16
|
+
self.rc_runtime = rc_rt_before_render + rc_rt_after_render
|
17
|
+
runtime - rc_rt_after_render
|
18
|
+
end
|
19
|
+
|
20
|
+
def append_info_to_payload(payload)
|
21
|
+
super
|
22
|
+
payload[:rc_runtime] = rc_runtime
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def log_process_action(payload)
|
27
|
+
messages, rc_runtime = super, payload[:rc_runtime]
|
28
|
+
messages << ("RailsConnector: %.1fms" % rc_runtime.to_f)
|
29
|
+
messages
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # module RailsConnector
|
35
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
class CouchBlob
|
4
|
+
|
5
|
+
def self.find(id, options)
|
6
|
+
cache_key = [:blob, id]
|
7
|
+
attachment = Cache.load(cache_key)
|
8
|
+
unless attachment
|
9
|
+
ActiveSupport::Notifications.instrumenter.instrument("cms_load.rails_connector",
|
10
|
+
:name => "Blob Load", :index => "blobs", :keys => [options[:context]]
|
11
|
+
) do
|
12
|
+
attachment = CmsBaseModel.adapter.blob_attachment(id)
|
13
|
+
Cache.store(cache_key, attachment)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
new(id, attachment)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.configure(spec)
|
20
|
+
# connection is taken from CmsBaseModel on demand
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :id, :attachment
|
24
|
+
|
25
|
+
def initialize(id, attachment)
|
26
|
+
@id = id
|
27
|
+
@attachment = attachment
|
28
|
+
end
|
29
|
+
|
30
|
+
def url
|
31
|
+
CmsBaseModel.adapter.blob_url(id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def content_type
|
35
|
+
attachment["content_type"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def length
|
39
|
+
attachment['length']
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end # module RailsConnector
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RailsConnector
|
2
|
+
|
3
|
+
# Adds support for string columns which contain ISO dates
|
4
|
+
module DateAttribute #:nodoc: all
|
5
|
+
module ClassMethods
|
6
|
+
def date_attribute(*names)
|
7
|
+
names.each do |name|
|
8
|
+
module_eval %Q!
|
9
|
+
def #{name}
|
10
|
+
DateAttribute.parse(#{name}_before_type_cast) unless #{name}_before_type_cast.nil?
|
11
|
+
end
|
12
|
+
!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
base.extend(ClassMethods)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parse(iso_date_time)
|
22
|
+
Time.from_iso(iso_date_time).localtime
|
23
|
+
rescue ArgumentError
|
24
|
+
raise "The value is not a valid ISO date time: #{iso_date_time.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|