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