orchestrate-rails 0.1.1

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.
@@ -0,0 +1,56 @@
1
+ module Orchestrate::Application
2
+
3
+ # Packages the result data from GET requests.
4
+ # Orchestrate.io records are returned as Document objects.
5
+ #
6
+ class Result
7
+ # Array of Document objects.
8
+ attr_reader :results
9
+
10
+ # Count of documents in #results. Set for list, search, events, and graph.
11
+ attr_reader :count
12
+
13
+ # Set for list results.
14
+ attr_reader :next
15
+
16
+ # Upon success: the result data is extracted from the response body,
17
+ # but the response body itself is not inluded as part of the result.
18
+ #
19
+ # Upon failure: the original response body, Orchestrate::API::ResponseBody
20
+ # is left intact and included to provide access to the error details.
21
+ attr_reader :response
22
+
23
+ # Boolean: return status of the api call.
24
+ attr_reader :status
25
+
26
+ # Initialize instance variables, based on type of results.
27
+ # - results (key/value)
28
+ # - results, count (events, graph)
29
+ # - results, count, next (list)
30
+ # - results, count, total_count (search)
31
+ def initialize(results)
32
+ results.each { |k,v| instance_variable_set "@#{k}", v }
33
+ end
34
+
35
+ def result
36
+ results.first
37
+ end
38
+
39
+ def result_keys
40
+ results.map { |result| result.metadata.key }
41
+ end
42
+
43
+ def success?
44
+ status == true
45
+ end
46
+ end
47
+
48
+ # Container class for Orchestrate.io search results.
49
+ # See parent class Result for more details.
50
+ #
51
+ class SearchResult < Result
52
+ # Total count of matched records.
53
+ attr_reader :total_count
54
+ end
55
+
56
+ end
@@ -0,0 +1,85 @@
1
+ module Orchestrate::Application
2
+
3
+ # ---------------------------------------------------------------------------
4
+ # Singleton class to define schema for Orchestrate.io application
5
+ #
6
+ class Schema < Object
7
+ include Singleton
8
+
9
+ def initialize
10
+ @@schema = {}
11
+ end
12
+
13
+ def schema
14
+ @@schema
15
+ end
16
+
17
+ def collections
18
+ schema
19
+ end
20
+
21
+ def collection_names
22
+ schema.keys
23
+ end
24
+
25
+ def get(collection_name)
26
+ schema[collection_name]
27
+ end
28
+
29
+ def get_collection(name)
30
+ get(name)
31
+ end
32
+
33
+ def load_collection(collection)
34
+ schema[collection.name] = collection
35
+ end
36
+
37
+ def define_collection(args)
38
+ load_collection SchemaCollection.new(args)
39
+ end
40
+
41
+ def define_event_type(collection_name, event_type, properties)
42
+ get(collection_name).define_event_type event_type, properties
43
+ end
44
+
45
+ def define_graph(collection_name, relation_kind, to_collection)
46
+ get(collection_name).define_graph relation_kind, to_collection
47
+ end
48
+
49
+ # Support (optional) loading of schema from a definition file
50
+ #
51
+ # Example usage:
52
+ #
53
+ # Orchestrate::Application::Schema.instance.load "./schema.rb"
54
+ #
55
+ # Example definition file - i.e. "<APP-ROOT>/schema.rb"
56
+ #
57
+ # Orchestrate::Application::Schema.instance.define_collection(
58
+ # :name => 'films',
59
+ # :properties => [ :Title, :Year, :Rated, :Released,
60
+ # :Runtime, :Genre, :Director, :Writer,
61
+ # :Actors, :Plot, :Poster, :imdbRating,
62
+ # :imdbVotes, :imdbID, :Type, :Response ],
63
+ # :event_types => [ :comments ],
64
+ # :graphs => [ :sequel ],
65
+ # )
66
+ #
67
+ # Orchestrate::Application::Schema.instance.define_event_type(
68
+ # :collection => 'films',
69
+ # :event_type => :comments,
70
+ # :properties => [ :User, :Comment ]
71
+ # )
72
+ #
73
+ # Orchestrate::Application::Schema.instance.define_graph(
74
+ # :collection => 'films',
75
+ # :relation_kind => :sequel,
76
+ # :to_collection => 'films',
77
+ # )
78
+ #
79
+ def load(schema)
80
+ require schema
81
+ end
82
+ end
83
+
84
+ end
85
+
@@ -0,0 +1,53 @@
1
+ module Orchestrate::Application
2
+
3
+ # Schema support object.
4
+ #
5
+ class SchemaCollection < Object
6
+ attr_reader :name, :properties, :event_types, :graphs
7
+
8
+ # args: { :name, :properties, :event_types, :graphs }
9
+ #
10
+ def initialize(args)
11
+ @name, @properties = args[:name], args[:properties] + [:id]
12
+
13
+ @event_types = {}
14
+ if args[:event_types]
15
+ args[:event_types].each { |etype| @event_types[etype] = etype }
16
+ end
17
+
18
+ @graphs = {}
19
+ if args[:graphs]
20
+ args[:graphs].each { |kind| @graphs[kind] = kind }
21
+ end
22
+
23
+ yield self if block_given?
24
+ end
25
+
26
+ def define_event_type(event_type, properties)
27
+ @event_types[event_type] = SchemaEventType.new(event_type, properties)
28
+ end
29
+
30
+ def define_graph(relation_kind, to_collection)
31
+ @graphs[relation_kind] = SchemaGraph.new(relation_kind, to_collection)
32
+ end
33
+ end
34
+
35
+ # Schema support object.
36
+ #
37
+ class SchemaEventType
38
+ attr_reader :name, :properties
39
+ def initialize(event_type, properties)
40
+ @name, @properties = event_type, properties
41
+ end
42
+ end
43
+
44
+ # Schema support object.
45
+ #
46
+ class SchemaGraph
47
+ attr_reader :kind, :to_collection
48
+ def initialize(kind, to_collection)
49
+ @kind, @to_collection = kind, to_collection
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,62 @@
1
+ module Orchestrate::Application
2
+
3
+ class SimpleCacheRequest
4
+ attr_reader :collection
5
+
6
+ def initialize(collection)
7
+ # puts "CACHE-init: '#{collection}'"
8
+ @collection = collection
9
+ @@cache_store ||= SimpleCacheStore.instance
10
+ end
11
+
12
+ def enabled?
13
+ cache.is_enabled?
14
+ end
15
+
16
+ def get(key)
17
+ doc = cache.get_cc(collection).fetch key
18
+ path = "/local_cache/#{collection}/#{key}"
19
+ puts "\n------- GET \"#{path}\" ------ #{doc ? 'OK' : 'not found'}"
20
+ response([doc]) if doc
21
+ end
22
+
23
+ def get_graph(key, kind)
24
+ docs = cache.get_cc(collection).fetch_graph key, kind
25
+ path = "/local_cache/#{collection}/#{key}/graph/#{kind}"
26
+ puts "\n------- GET \"#{path}\" ------ #{docs ? 'OK' : 'not found'}"
27
+ response(docs) if docs
28
+ end
29
+
30
+ def save_graph(key, kind)
31
+ cache.get_cc(collection).save_graph Metadata.new(from_key: key, kind: kind)
32
+ end
33
+
34
+ def get_events(key, event_type)
35
+ docs = cache.get_cc(collection).fetch_events key, event_type
36
+ path = "/local_cache/#{collection}/#{key}/events/#{event_type}"
37
+ puts "\n------- GET \"#{path}\" ------ #{docs ? 'OK' : 'not found'}"
38
+ response(docs) if docs
39
+ end
40
+
41
+ def save_event(key, event_type)
42
+ cache.get_cc(collection).save_event Metadata.new(key: key, etype: event_type)
43
+ end
44
+
45
+ private
46
+
47
+ def cache
48
+ @@cache_store
49
+ end
50
+
51
+ def response(docs)
52
+ locations = docs.map { |doc|
53
+ location = "/local_cache/refs/#{doc.id}"
54
+ puts " from \"#{location}\""
55
+ location
56
+ }
57
+ r_info = { locations: locations, code: 200, :status => :cache }
58
+ r_info.merge!(etag: docs.first.id) if docs.length == 1
59
+ SimpleCacheResponse.new(r_info, docs)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,41 @@
1
+ module Orchestrate::Application
2
+
3
+ # ---------------------------------------------------------------------------
4
+ # Class to handle Orchestrate.io API responses
5
+ #
6
+ class SimpleCacheResponse
7
+ attr_accessor :header, :body
8
+
9
+ def initialize(header, body)
10
+ @header, @body = Header.new(header), Body.new(body)
11
+ end
12
+
13
+ class Header
14
+ attr_reader :locations, :code, :status, :etag
15
+
16
+ def initialize(header)
17
+ @locations = header[:locations]
18
+ @code, @status = header[:code], header[:status]
19
+ @etag = header[:etag]
20
+ end
21
+
22
+ def location
23
+ locations.first
24
+ end
25
+ end
26
+
27
+ class Body
28
+ attr_reader :documents
29
+
30
+ def initialize(body)
31
+ @documents = body
32
+ end
33
+
34
+ def document
35
+ documents.first
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,215 @@
1
+ module Orchestrate::Application
2
+
3
+ # Class PrimaryKey
4
+ #
5
+ class PrimaryKey
6
+ attr_reader :events, :graphs
7
+ attr_accessor :ref
8
+
9
+ def initialize(ref)
10
+ # puts "SCC-primary_key: '#{ref}'"
11
+ @ref = ref
12
+ @events, @graphs = {}, {}
13
+ end
14
+
15
+ def add_event(etype, ref=nil)
16
+ # puts "SCC-add event"
17
+ @events[etype] = [] unless @events[etype]
18
+ @events[etype] << ref if ref
19
+ # puts "SCC-added event"
20
+ end
21
+
22
+ def add_graph(data)
23
+ # puts "SCC-add graph: '#{data.kind}'"
24
+ @graphs[data.kind] = [] unless @graphs[data.kind]
25
+ @graphs[data.kind] << data.ref if data.ref
26
+ # puts "SCC-added graph '#{@graphs[data.kind].last}'"
27
+ end
28
+ end
29
+
30
+
31
+ # Class SimpleCacheCollection
32
+ #
33
+ class SimpleCacheCollection
34
+
35
+ attr_reader :name, :primary_keys, :events, :graphs
36
+
37
+ def initialize(name)
38
+ @name = name
39
+ @primary_keys, @events, @graphs = {}, {}, {}
40
+ end
41
+
42
+ def flush!
43
+ @primary_keys, @events, @graphs = {}, {}, {}
44
+ end
45
+
46
+ def fetch(key)
47
+ # puts "SCC-fetching: '#{key}', '#{primary_keys[key]}'"
48
+ if primary_keys[key]
49
+ # puts "SCC-fetching: '#{primary_keys[key].ref}'"
50
+ SimpleCacheRefs.instance.document primary_keys[key].ref
51
+ end
52
+ end
53
+
54
+ def fetch_events(key, etype)
55
+ # puts "SCC-fetch_events: '#{key}', '#{etype}'"
56
+ if primary_keys[key] and primary_keys[key].events[etype]
57
+ primary_keys[key].events[etype].map { |ref|
58
+ # puts "SCC-fetch_events: '#{ref}'"
59
+ SimpleCacheRefs.instance.document ref
60
+ }
61
+ end
62
+ end
63
+
64
+ def fetch_graph(key, kind)
65
+ # puts "SCC-fetch_graph 1: '#{key}', '#{kind}'"
66
+ if primary_keys[key] and primary_keys[key].graphs and primary_keys[key].graphs[kind]
67
+ primary_keys[key].graphs[kind].map { |ref|
68
+ # puts "SCC-fetch_graph: '#{ref}'"
69
+ SimpleCacheRefs.instance.document ref
70
+ }
71
+ end
72
+ end
73
+
74
+ def get_ref(key)
75
+ primary_keys[key] and primary_keys[key].ref
76
+ end
77
+
78
+ def save(data)
79
+ key, ref = data.key, data.ref
80
+ @primary_keys[key] = PrimaryKey.new(ref) if primary_keys[key].nil?
81
+ if data.respond_to? :etype and data.etype
82
+ @primary_keys[key].add_event(data.etype, ref)
83
+ else
84
+ @primary_keys[key].ref = ref
85
+ end
86
+ end
87
+
88
+ def save_event(data)
89
+ @primary_keys[data.key] = PrimaryKey.new(0) if primary_keys[data.key].nil?
90
+ @primary_keys[data.key].add_event data.etype
91
+ end
92
+
93
+ def save_graph(data)
94
+ @primary_keys[data.from_key] = PrimaryKey.new(0) if primary_keys[data.from_key].nil?
95
+ @primary_keys[data.from_key].add_graph data
96
+ end
97
+
98
+
99
+ end
100
+
101
+ # Stores documents in cache, indexed by ref value.
102
+ #
103
+ class SimpleCacheRefs
104
+ include Singleton
105
+
106
+ def initialize
107
+ @@documents = {}
108
+ end
109
+
110
+ def save(ref, document)
111
+ @@documents[ref] = document
112
+ end
113
+
114
+ def document(ref)
115
+ @@documents[ref]
116
+ end
117
+
118
+ def flush!
119
+ @@documents = {}
120
+ end
121
+ end
122
+
123
+ # When enabled, the cache mechanism:
124
+ # * is useful for development/testing, minimizing calls to the server.
125
+ # * is updated upon each GET or PUT request.
126
+ # * remains active for the lifespan of the application.
127
+ #
128
+ class SimpleCacheStore
129
+
130
+ def initialize
131
+ @@is_enabled ||= false
132
+ @@cache ||= simple_cache_init
133
+ # puts "SC-init: cache -> '#{@@cache}', '#{!@@cache.nil?}'"
134
+ @@refs ||= {}
135
+ end
136
+
137
+ def simple_cache_init
138
+ cache = Hash[Schema.instance.collection_names.map { |name|
139
+ [name, SimpleCacheCollection.new(name)]
140
+ }]
141
+ # puts "SC-sc_init -> '#{cache}', is_enabled -> '#{@@is_enabled}'"
142
+ cache == {} ? nil : cache
143
+ end
144
+
145
+ def self.instance
146
+ @@cache ||= @@instance.simple_cache_init
147
+ @@instance
148
+ end
149
+
150
+ def self.enable
151
+ # puts "SC-ENABLE"
152
+ # @@is_enabled = true
153
+ puts "SIMPLE-CACHE has been DISABLED for the current version."
154
+ end
155
+
156
+ def self.disable
157
+ puts "SC-DISABLE"
158
+ @@is_enabled = false
159
+ end
160
+
161
+ @@instance = SimpleCacheStore.new
162
+ private_class_method :new
163
+
164
+ # -------------------------------------------------------------------------
165
+
166
+ #
167
+ def cache
168
+ @@cache
169
+ end
170
+
171
+ def flush!(collection=nil)
172
+ if collection
173
+ get_cc(collection).flush!
174
+ else
175
+ @@cache = simple_cache_init
176
+ end
177
+ end
178
+
179
+ def get_cache_collection(name)
180
+ cache[name]
181
+ end
182
+
183
+ def get_cc(name)
184
+ get_cache_collection name
185
+ end
186
+
187
+ def save(document)
188
+ m = document.metadata
189
+ get_cc(m.from_collection).save_graph(m) if m.respond_to? :kind and m.kind
190
+ get_cc(m.collection).save m
191
+ SimpleCacheRefs.instance.save m.ref, document
192
+ end
193
+
194
+ def get_ref(collection_name, key)
195
+ get_cc(collection_name).get_ref key
196
+ end
197
+
198
+ def fetch(collection, key)
199
+ get_cc(collection).fetch(key)
200
+ end
201
+
202
+ def is_enabled?
203
+ @@is_enabled
204
+ end
205
+
206
+ def enable
207
+ self.class.enable
208
+ end
209
+
210
+ def disable
211
+ self.class.disable
212
+ end
213
+
214
+ end
215
+ end
@@ -0,0 +1,38 @@
1
+ module Orchestrate::Rails
2
+
3
+ # Extensions #update_rails, #to_rails, #to_event are defined in the
4
+ # Orchestrate::Rails module and are accessible within that namespace.
5
+ #
6
+ class Orchestrate::Application::Document
7
+
8
+ # Updates the key/value pairs for the model instance with key/value data
9
+ # from the document.
10
+ # Defined by Orchestrate::Rails.
11
+ def update_rails(instance)
12
+ key_value_pairs.each { |k,v|
13
+ instance.instance_variable_set "@#{k.to_orchio_rails_attr}", v
14
+ }
15
+ instance.instance_variable_set "@__ref_value__", metadata.ref # JMC
16
+ instance
17
+ end
18
+
19
+ # Creates a new model instance, and calls #update_rails.
20
+ # Defined by Orchestrate::Rails.
21
+ def to_rails
22
+ update_rails Object.const_get(classname).new
23
+ end
24
+
25
+ # Creates a new event instance from the document's key/value data.
26
+ # Defined by Orchestrate::Rails.
27
+ def to_event
28
+ Object.const_get('Orchestrate::Rails::Event').new key_value_pairs
29
+ end
30
+
31
+ private
32
+ def classname
33
+ Orchestrate::Rails::Schema.instance.fullclassname metadata.collection
34
+ # Orchestrate::Rails::Schema.instance.classname(metadata.collection) JMC
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,23 @@
1
+ module Orchestrate::Rails
2
+
3
+ require "active_model"
4
+
5
+ # Class to support *dynamically defined* orchestrate.io/event instances
6
+ #
7
+ class Event
8
+ include ::ActiveModel::Conversion
9
+ extend ::ActiveModel::Naming
10
+
11
+ def initialize(event_record)
12
+ event_record.each do |k,v|
13
+ self.class.send(:attr_reader, k.to_orchio_rails_attr)
14
+ instance_variable_set("@#{k.to_orchio_rails_attr}", v)
15
+ end
16
+ end
17
+
18
+ def id
19
+ @timestamp
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,21 @@
1
+ module Orchestrate::Rails
2
+
3
+ # ---------------------------------------------------------------------------
4
+ # Add String and Symbol instance methods for easy conversion of
5
+ # orchestrate.io property names to rails-style model attribute names.
6
+
7
+ # Convert property name to attribute name.
8
+ class ::String
9
+ def to_orchio_rails_attr
10
+ underscore.downcase
11
+ end
12
+ end
13
+
14
+ # Convert property name to attribute name. Calls ::String#to_orchio_rails_attr.
15
+ class ::Symbol
16
+ def to_orchio_rails_attr
17
+ to_s.to_orchio_rails_attr
18
+ end
19
+ end
20
+
21
+ end