infopark_cloud_connector 6.8.0.beta.200.621.4c8e1b0

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/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,17 @@
1
+ #:enddoc:
2
+ module RailsConnector
3
+
4
+ class CacheMiddleware
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ Cache.begin_context
11
+ @app.call(env)
12
+ ensure
13
+ Cache.end_context
14
+ end
15
+ end
16
+
17
+ end # module RailsConnector
@@ -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,7 @@
1
+ #:enddoc:
2
+ module RailsConnector
3
+ # returns the filesystem path where the sources for the rails connector's couchdb views are stored.
4
+ def self.couchdb_views_source_path
5
+ File.expand_path("../../../db/views", __FILE__)
6
+ end
7
+ end
@@ -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
@@ -0,0 +1,6 @@
1
+ module RailsConnector
2
+ # This class provides a default implementation for accessing the search server.
3
+ # It is used by DefaultSearchController.
4
+ class DefaultSearchRequest < ElasticsearchRequest
5
+ end
6
+ end