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 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
- end
17
- rescue LoadError
18
- puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
- end
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.3
1
+ 0.2.4
@@ -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
@@ -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