medea 0.2.3 → 0.2.4
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/Rakefile +21 -20
- data/VERSION +1 -1
- data/lib/medea.rb +7 -7
- data/lib/medea/inheritable_attributes.rb +30 -30
- data/lib/medea/jasondb.rb +20 -20
- data/lib/medea/jasondeferredquery.rb +124 -124
- data/lib/medea/jasonlistproperty.rb +101 -101
- data/lib/medea/jasonobject.rb +238 -236
- metadata +23 -9
- data/assets.file +0 -0
- data/assets.http +0 -0
- data/index.html +0 -78
- data/medea-0.2.2.gem +0 -0
- data/medea.gemspec +0 -55
data/Rakefile
CHANGED
@@ -1,20 +1,21 @@
|
|
1
|
-
require 'rake'
|
2
|
-
|
3
|
-
begin
|
4
|
-
require 'jeweler'
|
5
|
-
Jeweler::Tasks.new do |s|
|
6
|
-
s.name = "medea"
|
7
|
-
s.summary = "Simple wrapper for persisting objects to JasonDB"
|
8
|
-
s.email = "michaelj@jasondb.com"
|
9
|
-
s.homepage = "https://github.com/rob-linton/Medea"
|
10
|
-
s.description = "Simple wrapper for persisting objects to JasonDB"
|
11
|
-
s.authors = ["Michael Jensen"]
|
12
|
-
s.files = FileList["[A-Z]*", "{lib}/medea.rb", "{lib}/medea/*"]
|
13
|
-
s.files.exclude '{lib}/test*'
|
14
|
-
s.add_dependency 'json'
|
15
|
-
s.add_dependency 'rest-client'
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
require 'rake'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
Jeweler::Tasks.new do |s|
|
6
|
+
s.name = "medea"
|
7
|
+
s.summary = "Simple wrapper for persisting objects to JasonDB"
|
8
|
+
s.email = "michaelj@jasondb.com"
|
9
|
+
s.homepage = "https://github.com/rob-linton/Medea"
|
10
|
+
s.description = "Simple wrapper for persisting objects to JasonDB"
|
11
|
+
s.authors = ["Michael Jensen"]
|
12
|
+
s.files = FileList["[A-Z]*", "{lib}/medea.rb", "{lib}/medea/*"]
|
13
|
+
s.files.exclude '{lib}/test*'
|
14
|
+
s.add_dependency 'json'
|
15
|
+
s.add_dependency 'rest-client'
|
16
|
+
s.add_dependency 'uuidtools'
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
21
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
data/lib/medea.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
|
-
module Medea
|
3
|
-
require 'medea/inheritable_attributes'
|
4
|
-
require 'medea/jasonobject'
|
5
|
-
require 'medea/jasondeferredquery'
|
6
|
-
require 'medea/jasonlistproperty'
|
7
|
-
require 'medea/jasondb'
|
1
|
+
|
2
|
+
module Medea
|
3
|
+
require 'medea/inheritable_attributes'
|
4
|
+
require 'medea/jasonobject'
|
5
|
+
require 'medea/jasondeferredquery'
|
6
|
+
require 'medea/jasonlistproperty'
|
7
|
+
require 'medea/jasondb'
|
8
8
|
end
|
@@ -1,31 +1,31 @@
|
|
1
|
-
#This module allows for an attribute to be defined on a superclass and carry down into sub-classes with its default.
|
2
|
-
#Taken from http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
|
3
|
-
|
4
|
-
module ClassLevelInheritableAttributes
|
5
|
-
def self.included(base)
|
6
|
-
base.extend(ClassMethods)
|
7
|
-
end
|
8
|
-
|
9
|
-
module ClassMethods
|
10
|
-
def inheritable_attributes(*args)
|
11
|
-
#for some reason, in rails, @inheritable_attributes is set to be an empty hash here...
|
12
|
-
#check for this strange case and account for it.
|
13
|
-
@inheritable_attributes = [:inheritable_attributes] if @inheritable_attributes == {} ||
|
14
|
-
@inheritable_attributes == nil
|
15
|
-
@inheritable_attributes += args
|
16
|
-
args.each do |arg|
|
17
|
-
class_eval %(
|
18
|
-
class << self; attr_accessor :#{arg} end
|
19
|
-
)
|
20
|
-
end
|
21
|
-
@inheritable_attributes
|
22
|
-
end
|
23
|
-
|
24
|
-
def inherited(subclass)
|
25
|
-
@inheritable_attributes.each do |inheritable_attribute|
|
26
|
-
instance_var = "@#{inheritable_attribute}"
|
27
|
-
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
1
|
+
#This module allows for an attribute to be defined on a superclass and carry down into sub-classes with its default.
|
2
|
+
#Taken from http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
|
3
|
+
|
4
|
+
module ClassLevelInheritableAttributes
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def inheritable_attributes(*args)
|
11
|
+
#for some reason, in rails, @inheritable_attributes is set to be an empty hash here...
|
12
|
+
#check for this strange case and account for it.
|
13
|
+
@inheritable_attributes = [:inheritable_attributes] if @inheritable_attributes == {} ||
|
14
|
+
@inheritable_attributes == nil
|
15
|
+
@inheritable_attributes += args
|
16
|
+
args.each do |arg|
|
17
|
+
class_eval %(
|
18
|
+
class << self; attr_accessor :#{arg} end
|
19
|
+
)
|
20
|
+
end
|
21
|
+
@inheritable_attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
def inherited(subclass)
|
25
|
+
@inheritable_attributes.each do |inheritable_attribute|
|
26
|
+
instance_var = "@#{inheritable_attribute}"
|
27
|
+
subclass.instance_variable_set(instance_var, instance_variable_get(instance_var))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
31
|
end
|
data/lib/medea/jasondb.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
module JasonDB
|
2
|
-
#jason_url here doesn't include the http[s]:// part, but does include the domain and a trailing '/'
|
3
|
-
#( so it's "rest.jasondb.com/<domain>/" )
|
4
|
-
|
5
|
-
def JasonDB::db_auth_url mode=:secure
|
6
|
-
config = Rails.configuration.database_configuration[Rails.env]
|
7
|
-
user = config["user"]
|
8
|
-
topic = config["topic"]
|
9
|
-
password = config["password"]
|
10
|
-
if config["jason_host"]
|
11
|
-
host = config["jason_host"]
|
12
|
-
else
|
13
|
-
host = "rest.jasondb.com"
|
14
|
-
end
|
15
|
-
protocol = "http"
|
16
|
-
protocol << "s" if mode == :secure
|
17
|
-
"#{protocol}://#{user}:#{password}@#{host}/#{topic}/"
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
1
|
+
module JasonDB
|
2
|
+
#jason_url here doesn't include the http[s]:// part, but does include the domain and a trailing '/'
|
3
|
+
#( so it's "rest.jasondb.com/<domain>/" )
|
4
|
+
|
5
|
+
def JasonDB::db_auth_url mode=:secure
|
6
|
+
config = Rails.configuration.database_configuration[Rails.env]
|
7
|
+
user = config["user"]
|
8
|
+
topic = config["topic"]
|
9
|
+
password = config["password"]
|
10
|
+
if config["jason_host"]
|
11
|
+
host = config["jason_host"]
|
12
|
+
else
|
13
|
+
host = "rest.jasondb.com"
|
14
|
+
end
|
15
|
+
protocol = "http"
|
16
|
+
protocol << "s" if mode == :secure
|
17
|
+
"#{protocol}://#{user}:#{password}@#{host}/#{topic}/"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -1,125 +1,125 @@
|
|
1
|
-
module Medea
|
2
|
-
class JasonDeferredQuery
|
3
|
-
require 'rest_client'
|
4
|
-
|
5
|
-
attr_accessor :time_limit, :result_format, :type, :result_format, :time_limit, :state, :contents, :filters
|
6
|
-
|
7
|
-
def initialize a_class, format=:json
|
8
|
-
@type = a_class
|
9
|
-
@filters = {:FILTER => {:HTTP_X_CLASS => a_class.name.to_s}}
|
10
|
-
@result_format = format
|
11
|
-
@time_limit = 0
|
12
|
-
@state = :prefetch
|
13
|
-
@contents = []
|
14
|
-
end
|
15
|
-
|
16
|
-
#Here we're going to put the "query" interface
|
17
|
-
|
18
|
-
#here we will capture:
|
19
|
-
#members_of(object) (where object is an instance of a class that this class can be a member of)
|
20
|
-
#members_of_<classname>(key)
|
21
|
-
#find_by_<property>(value)
|
22
|
-
#Will return a JasonDeferredQuery for this class with the appropriate data filter set
|
23
|
-
def method_missing(name, *args, &block)
|
24
|
-
#if we are postfetch, we throw away all our cached results
|
25
|
-
if self.state == :postfetch
|
26
|
-
self.state = :prefetch
|
27
|
-
self.contents = []
|
28
|
-
end
|
29
|
-
|
30
|
-
if name =~ /^members_of$/
|
31
|
-
#use the type and key of the first arg (being a JasonObject)
|
32
|
-
#args[0] must be a JasonObject (or child)
|
33
|
-
raise ArgumentError, "When looking for members, you must pass a JasonObject" unless args[0].is_a? JasonObject
|
34
|
-
|
35
|
-
self.filters[:DATA_FILTER] ||= {}
|
36
|
-
self.filters[:DATA_FILTER]["__member_of"] ||= []
|
37
|
-
self.filters[:DATA_FILTER]["__member_of"] << args[0].jason_key
|
38
|
-
elsif name =~ /^find_by_(.*)$/
|
39
|
-
#use the property name from the name variable, and the value from the first arg
|
40
|
-
add_data_filter $1, args[0].to_s
|
41
|
-
else
|
42
|
-
#no method!
|
43
|
-
super
|
44
|
-
return
|
45
|
-
end
|
46
|
-
#return self, so that we can chain up query refinements
|
47
|
-
self
|
48
|
-
end
|
49
|
-
#end query interface
|
50
|
-
|
51
|
-
def add_data_filter property, value
|
52
|
-
self.filters[:DATA_FILTER] ||= {}
|
53
|
-
self.filters[:DATA_FILTER][property] = value
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_url
|
57
|
-
url = "#{JasonDB::db_auth_url}@#{self.time_limit}.#{self.result_format}?"
|
58
|
-
filter_array = []
|
59
|
-
self.filters.each do |name, val|
|
60
|
-
if not val
|
61
|
-
filter_array << name.to_s
|
62
|
-
next
|
63
|
-
else
|
64
|
-
#FILTER's value is a hash (to avoid dupes)
|
65
|
-
#DATA_FILTER's value is a hash
|
66
|
-
if val.is_a? Hash
|
67
|
-
#for each k/v in the hash, we want to add an entry to filter_array
|
68
|
-
val.each do |field ,value|
|
69
|
-
if value.is_a? Array
|
70
|
-
value.each do |i|
|
71
|
-
filter_array << "#{name.to_s}=#{field}:#{i}"
|
72
|
-
end
|
73
|
-
else
|
74
|
-
filter_array << "#{name.to_s}=#{field.to_s}:#{value.to_s}"
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
url + filter_array.join("&")
|
82
|
-
end
|
83
|
-
|
84
|
-
#array access interface
|
85
|
-
def [](index)
|
86
|
-
execute_query unless self.state == :postfetch
|
87
|
-
self.contents[index]
|
88
|
-
end
|
89
|
-
|
90
|
-
def each(&block)
|
91
|
-
execute_query unless self.state == :postfetch
|
92
|
-
self.contents.each &block
|
93
|
-
end
|
94
|
-
|
95
|
-
def count
|
96
|
-
execute_query unless self.state == :postfetch
|
97
|
-
self.contents.count
|
98
|
-
end
|
99
|
-
#end array interface
|
100
|
-
|
101
|
-
def execute_query
|
102
|
-
#hit the URL
|
103
|
-
#fill self.contents with :ghost versions of JasonObjects
|
104
|
-
begin
|
105
|
-
#puts " = Executing #{type.name} deferred query! (#{to_url})"
|
106
|
-
result = JSON.parse(RestClient.get to_url)
|
107
|
-
|
108
|
-
#results are in a hash, their keys are just numbers
|
109
|
-
result.keys.each do |k|
|
110
|
-
if k =~ /^[0-9]+$/
|
111
|
-
#this is a result! get the key
|
112
|
-
/\/([^\/]*)\/([^\/]*)$/.match result[k]["POST_TO"]
|
113
|
-
#$1 is the class name, $2 is the key
|
114
|
-
item = type.new($2, :lazy)
|
115
|
-
self.contents << item
|
116
|
-
end
|
117
|
-
end
|
118
|
-
rescue
|
119
|
-
self.contents = []
|
120
|
-
ensure
|
121
|
-
self.state = :postfetch
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
1
|
+
module Medea
|
2
|
+
class JasonDeferredQuery
|
3
|
+
require 'rest_client'
|
4
|
+
|
5
|
+
attr_accessor :time_limit, :result_format, :type, :result_format, :time_limit, :state, :contents, :filters
|
6
|
+
|
7
|
+
def initialize a_class, format=:json
|
8
|
+
@type = a_class
|
9
|
+
@filters = {:FILTER => {:HTTP_X_CLASS => a_class.name.to_s}}
|
10
|
+
@result_format = format
|
11
|
+
@time_limit = 0
|
12
|
+
@state = :prefetch
|
13
|
+
@contents = []
|
14
|
+
end
|
15
|
+
|
16
|
+
#Here we're going to put the "query" interface
|
17
|
+
|
18
|
+
#here we will capture:
|
19
|
+
#members_of(object) (where object is an instance of a class that this class can be a member of)
|
20
|
+
#members_of_<classname>(key)
|
21
|
+
#find_by_<property>(value)
|
22
|
+
#Will return a JasonDeferredQuery for this class with the appropriate data filter set
|
23
|
+
def method_missing(name, *args, &block)
|
24
|
+
#if we are postfetch, we throw away all our cached results
|
25
|
+
if self.state == :postfetch
|
26
|
+
self.state = :prefetch
|
27
|
+
self.contents = []
|
28
|
+
end
|
29
|
+
|
30
|
+
if name =~ /^members_of$/
|
31
|
+
#use the type and key of the first arg (being a JasonObject)
|
32
|
+
#args[0] must be a JasonObject (or child)
|
33
|
+
raise ArgumentError, "When looking for members, you must pass a JasonObject" unless args[0].is_a? JasonObject
|
34
|
+
|
35
|
+
self.filters[:DATA_FILTER] ||= {}
|
36
|
+
self.filters[:DATA_FILTER]["__member_of"] ||= []
|
37
|
+
self.filters[:DATA_FILTER]["__member_of"] << args[0].jason_key
|
38
|
+
elsif name =~ /^find_by_(.*)$/
|
39
|
+
#use the property name from the name variable, and the value from the first arg
|
40
|
+
add_data_filter $1, args[0].to_s
|
41
|
+
else
|
42
|
+
#no method!
|
43
|
+
super
|
44
|
+
return
|
45
|
+
end
|
46
|
+
#return self, so that we can chain up query refinements
|
47
|
+
self
|
48
|
+
end
|
49
|
+
#end query interface
|
50
|
+
|
51
|
+
def add_data_filter property, value
|
52
|
+
self.filters[:DATA_FILTER] ||= {}
|
53
|
+
self.filters[:DATA_FILTER][property] = value
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_url
|
57
|
+
url = "#{JasonDB::db_auth_url}@#{self.time_limit}.#{self.result_format}?"
|
58
|
+
filter_array = []
|
59
|
+
self.filters.each do |name, val|
|
60
|
+
if not val
|
61
|
+
filter_array << name.to_s
|
62
|
+
next
|
63
|
+
else
|
64
|
+
#FILTER's value is a hash (to avoid dupes)
|
65
|
+
#DATA_FILTER's value is a hash
|
66
|
+
if val.is_a? Hash
|
67
|
+
#for each k/v in the hash, we want to add an entry to filter_array
|
68
|
+
val.each do |field ,value|
|
69
|
+
if value.is_a? Array
|
70
|
+
value.each do |i|
|
71
|
+
filter_array << "#{name.to_s}=#{field}:#{i}"
|
72
|
+
end
|
73
|
+
else
|
74
|
+
filter_array << "#{name.to_s}=#{field.to_s}:#{value.to_s}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
url + filter_array.join("&")
|
82
|
+
end
|
83
|
+
|
84
|
+
#array access interface
|
85
|
+
def [](index)
|
86
|
+
execute_query unless self.state == :postfetch
|
87
|
+
self.contents[index]
|
88
|
+
end
|
89
|
+
|
90
|
+
def each(&block)
|
91
|
+
execute_query unless self.state == :postfetch
|
92
|
+
self.contents.each &block
|
93
|
+
end
|
94
|
+
|
95
|
+
def count
|
96
|
+
execute_query unless self.state == :postfetch
|
97
|
+
self.contents.count
|
98
|
+
end
|
99
|
+
#end array interface
|
100
|
+
|
101
|
+
def execute_query
|
102
|
+
#hit the URL
|
103
|
+
#fill self.contents with :ghost versions of JasonObjects
|
104
|
+
begin
|
105
|
+
#puts " = Executing #{type.name} deferred query! (#{to_url})"
|
106
|
+
result = JSON.parse(RestClient.get to_url)
|
107
|
+
|
108
|
+
#results are in a hash, their keys are just numbers
|
109
|
+
result.keys.each do |k|
|
110
|
+
if k =~ /^[0-9]+$/
|
111
|
+
#this is a result! get the key
|
112
|
+
/\/([^\/]*)\/([^\/]*)$/.match result[k]["POST_TO"]
|
113
|
+
#$1 is the class name, $2 is the key
|
114
|
+
item = type.new($2, :lazy)
|
115
|
+
self.contents << item
|
116
|
+
end
|
117
|
+
end
|
118
|
+
rescue
|
119
|
+
self.contents = []
|
120
|
+
ensure
|
121
|
+
self.state = :postfetch
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
125
|
end
|
@@ -1,102 +1,102 @@
|
|
1
|
-
module Medea
|
2
|
-
require 'uri'
|
3
|
-
require 'rest_client'
|
4
|
-
class JasonListProperty < JasonDeferredQuery
|
5
|
-
|
6
|
-
attr_accessor :list_name, :parent, :list_type
|
7
|
-
|
8
|
-
def initialize parent, list_name, list_class, list_type
|
9
|
-
@type = list_class
|
10
|
-
@list_name = list_name
|
11
|
-
@list_type = list_type
|
12
|
-
@parent = parent
|
13
|
-
@result_format = :json
|
14
|
-
@time_limit = 0
|
15
|
-
@state = :prefetch
|
16
|
-
@contents = []
|
17
|
-
end
|
18
|
-
|
19
|
-
def method_missing name, *args, &block
|
20
|
-
#is this a list property on the base class?
|
21
|
-
if (@type.class_variable_defined? :@@lists) && (@type.class_variable_get(:@@lists).has_key? name)
|
22
|
-
#if so, we'll just return a new ListProperty with my query as the parent
|
23
|
-
new_list_class, new_list_type = @type.class_variable_get(:@@lists)[name]
|
24
|
-
base_query = self.clone
|
25
|
-
base_query.result_format = :keylist
|
26
|
-
JasonListProperty.new base_query, name.to_sym, new_list_class, new_list_type
|
27
|
-
else
|
28
|
-
#no method, let JasonDeferredQuery handle it
|
29
|
-
super
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def add! member, save=true
|
34
|
-
raise RuntimeError, "You can only add an item if you are accessing this list from an object." unless @parent.is_a? JasonObject
|
35
|
-
raise ArgumentError, "You can only add #{@type.name} items to this collection!" unless member.is_a? @type
|
36
|
-
|
37
|
-
if @list_type == :value
|
38
|
-
member.jason_parent = @parent
|
39
|
-
member.jason_parent_list = @list_name
|
40
|
-
elsif @list_type == :reference
|
41
|
-
|
42
|
-
#post to JasonDB::db_auth_url/a_class.name/
|
43
|
-
url = "#{JasonDB::db_auth_url}#{@type.name}/#{@parent.jason_key}/#{@list_name}/#{member.jason_key}"
|
44
|
-
post_headers = {
|
45
|
-
:content_type => 'application/json',
|
46
|
-
"X-CLASS" => @type.name,
|
47
|
-
"X-KEY" => member.jason_key,
|
48
|
-
"X-PARENT" => @parent.jason_key,
|
49
|
-
"X-LIST" => @list_name.to_s
|
50
|
-
}
|
51
|
-
content = {
|
52
|
-
"_id" => member.jason_key,
|
53
|
-
"_parent" => @parent.jason_key
|
54
|
-
}
|
55
|
-
#puts " = " + url
|
56
|
-
#puts " = #{post_headers}"
|
57
|
-
response = RestClient.post url, content.to_json, post_headers
|
58
|
-
|
59
|
-
if response.code == 201
|
60
|
-
#save successful!
|
61
|
-
#store the new eTag for this object
|
62
|
-
#puts response.raw_headers
|
63
|
-
#@__jason_etag = response.headers[:location] + ":" + response.headers[:content_md5]
|
64
|
-
else
|
65
|
-
raise "POST failed! Could not save membership"
|
66
|
-
end
|
67
|
-
else
|
68
|
-
#parent is a JasonObject, but this list is something other than :value or :reference??
|
69
|
-
raise "Invalid list type or trying to add an item to a subquery list!"
|
70
|
-
end
|
71
|
-
|
72
|
-
if member.jason_state == :new
|
73
|
-
#we want to save it? probably...
|
74
|
-
member.save! if save
|
75
|
-
end
|
76
|
-
|
77
|
-
@state = :prefetch
|
78
|
-
end
|
79
|
-
|
80
|
-
def to_url
|
81
|
-
url = "#{JasonDB::db_auth_url}@#{@time_limit}.#{@result_format}?"
|
82
|
-
params = ["VERSION0"]
|
83
|
-
|
84
|
-
params << "FILTER=HTTP_X_CLASS:#{@type.name}"
|
85
|
-
|
86
|
-
if @parent.is_a? JasonObject
|
87
|
-
params << "FILTER=HTTP_X_PARENT:#{@parent.jason_key}"
|
88
|
-
else # @parent.is_a? JasonListProperty ##(or DeferredQuery?)
|
89
|
-
#we can get the insecure url here, because it will be resolved and executed at JasonDB - on a secure subnet.
|
90
|
-
|
91
|
-
#subquery = "<%@LANGUAGE=\"URL\" #{@parent.to_url}%>"
|
92
|
-
#puts " = Fetching subquery stupidly. (#{@parent.to_url})"
|
93
|
-
|
94
|
-
subquery = (RestClient.get @parent.to_url).strip
|
95
|
-
#puts " = Result: #{subquery}"
|
96
|
-
params << URI.escape("FILTER={HTTP_X_PARENT:#{subquery}}", Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
97
|
-
end
|
98
|
-
|
99
|
-
url << params.join("&")
|
100
|
-
end
|
101
|
-
end
|
1
|
+
module Medea
|
2
|
+
require 'uri'
|
3
|
+
require 'rest_client'
|
4
|
+
class JasonListProperty < JasonDeferredQuery
|
5
|
+
|
6
|
+
attr_accessor :list_name, :parent, :list_type
|
7
|
+
|
8
|
+
def initialize parent, list_name, list_class, list_type
|
9
|
+
@type = list_class
|
10
|
+
@list_name = list_name
|
11
|
+
@list_type = list_type
|
12
|
+
@parent = parent
|
13
|
+
@result_format = :json
|
14
|
+
@time_limit = 0
|
15
|
+
@state = :prefetch
|
16
|
+
@contents = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def method_missing name, *args, &block
|
20
|
+
#is this a list property on the base class?
|
21
|
+
if (@type.class_variable_defined? :@@lists) && (@type.class_variable_get(:@@lists).has_key? name)
|
22
|
+
#if so, we'll just return a new ListProperty with my query as the parent
|
23
|
+
new_list_class, new_list_type = @type.class_variable_get(:@@lists)[name]
|
24
|
+
base_query = self.clone
|
25
|
+
base_query.result_format = :keylist
|
26
|
+
JasonListProperty.new base_query, name.to_sym, new_list_class, new_list_type
|
27
|
+
else
|
28
|
+
#no method, let JasonDeferredQuery handle it
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add! member, save=true
|
34
|
+
raise RuntimeError, "You can only add an item if you are accessing this list from an object." unless @parent.is_a? JasonObject
|
35
|
+
raise ArgumentError, "You can only add #{@type.name} items to this collection!" unless member.is_a? @type
|
36
|
+
|
37
|
+
if @list_type == :value
|
38
|
+
member.jason_parent = @parent
|
39
|
+
member.jason_parent_list = @list_name
|
40
|
+
elsif @list_type == :reference
|
41
|
+
|
42
|
+
#post to JasonDB::db_auth_url/a_class.name/
|
43
|
+
url = "#{JasonDB::db_auth_url}#{@type.name}/#{@parent.jason_key}/#{@list_name}/#{member.jason_key}"
|
44
|
+
post_headers = {
|
45
|
+
:content_type => 'application/json',
|
46
|
+
"X-CLASS" => @type.name,
|
47
|
+
"X-KEY" => member.jason_key,
|
48
|
+
"X-PARENT" => @parent.jason_key,
|
49
|
+
"X-LIST" => @list_name.to_s
|
50
|
+
}
|
51
|
+
content = {
|
52
|
+
"_id" => member.jason_key,
|
53
|
+
"_parent" => @parent.jason_key
|
54
|
+
}
|
55
|
+
#puts " = " + url
|
56
|
+
#puts " = #{post_headers}"
|
57
|
+
response = RestClient.post url, content.to_json, post_headers
|
58
|
+
|
59
|
+
if response.code == 201
|
60
|
+
#save successful!
|
61
|
+
#store the new eTag for this object
|
62
|
+
#puts response.raw_headers
|
63
|
+
#@__jason_etag = response.headers[:location] + ":" + response.headers[:content_md5]
|
64
|
+
else
|
65
|
+
raise "POST failed! Could not save membership"
|
66
|
+
end
|
67
|
+
else
|
68
|
+
#parent is a JasonObject, but this list is something other than :value or :reference??
|
69
|
+
raise "Invalid list type or trying to add an item to a subquery list!"
|
70
|
+
end
|
71
|
+
|
72
|
+
if member.jason_state == :new
|
73
|
+
#we want to save it? probably...
|
74
|
+
member.save! if save
|
75
|
+
end
|
76
|
+
|
77
|
+
@state = :prefetch
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_url
|
81
|
+
url = "#{JasonDB::db_auth_url}@#{@time_limit}.#{@result_format}?"
|
82
|
+
params = ["VERSION0"]
|
83
|
+
|
84
|
+
params << "FILTER=HTTP_X_CLASS:#{@type.name}"
|
85
|
+
|
86
|
+
if @parent.is_a? JasonObject
|
87
|
+
params << "FILTER=HTTP_X_PARENT:#{@parent.jason_key}"
|
88
|
+
else # @parent.is_a? JasonListProperty ##(or DeferredQuery?)
|
89
|
+
#we can get the insecure url here, because it will be resolved and executed at JasonDB - on a secure subnet.
|
90
|
+
|
91
|
+
#subquery = "<%@LANGUAGE=\"URL\" #{@parent.to_url}%>"
|
92
|
+
#puts " = Fetching subquery stupidly. (#{@parent.to_url})"
|
93
|
+
|
94
|
+
subquery = (RestClient.get @parent.to_url).strip
|
95
|
+
#puts " = Result: #{subquery}"
|
96
|
+
params << URI.escape("FILTER={HTTP_X_PARENT:#{subquery}}", Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
97
|
+
end
|
98
|
+
|
99
|
+
url << params.join("&")
|
100
|
+
end
|
101
|
+
end
|
102
102
|
end
|