Class: Medea::JasonObject

Inherits:
Object
  • Object
show all
Includes:
ClassLevelInheritableAttributes, JasonObjectListProperties, ActiveModelMethods
Defined in:
lib/medea/jasonobject.rb

Direct Known Subclasses

Company, Message, Person, User

Class Method Summary collapse

Instance Method Summary collapse

Methods included from JasonObjectListProperties

included

Methods included from ClassLevelInheritableAttributes

included

Methods included from ActiveModelMethods

#errors, #persisted?, #to_model, #valid?

Constructor Details

#initialize(key = nil, mode = :eager) ⇒ JasonObject

end “flexihash” access



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/medea/jasonobject.rb', line 110

def initialize key = nil, mode = :eager
  if key
    @__id = key
    if mode == :eager
      load
    else
      @__jason_state = :ghost
    end
  else
    @__jason_state = :new
    @__jason_data = {}
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args, &block) ⇒ Object

The “Magic” component of candy (github.com/SFEley/candy), repurposed to make this a “weak object” that can take any attribute. Assigning any attribute will add it to the object’s hash (and then be POSTed to JasonDB on the next save)



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/medea/jasonobject.rb', line 97

def method_missing(name, *args, &block)
    load if @__jason_state == :ghost
    field = name.to_s
    if field =~ /(.*)=$/  # We're assigning
        self[$1] = args[0]
    elsif field =~ /(.*)\?$/  # We're asking
        (self[$1] ? true : false)
    else
        self[field]
    end
end

Class Method Details

.all(mode = :lazy) ⇒ Object

create a JasonDeferredQuery with no conditions, other than HTTP_X_CLASS=self.name if mode is set to :eager, we create the JasonDeferredQuery, invoke it’s execution and then return it



26
27
28
# File 'lib/medea/jasonobject.rb', line 26

def JasonObject.all(mode=:lazy)
  JasonDeferredQuery.new self
end

.get_by_key(key, mode = :eager) ⇒ Object

returns the JasonObject by directly querying the URL if mode is :lazy, we return a GHOST, if mode is :eager, we return a STALE JasonObject



32
33
34
# File 'lib/medea/jasonobject.rb', line 32

def JasonObject.get_by_key(key, mode=:eager)
  return self.new key, mode
end

.method_missing(name, *args, &block) ⇒ Object

here we will capture: members_of(object) (where object is an instance of a class that this class can be a member of) find_by_<property>(value) Will return a JasonDeferredQuery for this class with the appropriate data filter set



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/medea/jasonobject.rb', line 40

def JasonObject.method_missing(name, *args, &block)
  q = JasonDeferredQuery.new self
  if name.to_s =~ /^members_of$/
    #use the type and key of the first arg (being a JasonObject)
    return q.members_of args[0]
  elsif name.to_s =~ /^find_by_(.*)$/
    #use the property name from the name variable, and the value from the first arg
    q.add_data_filter $1, args[0]

    return q
  else
    #no method!
    super
  end
end

.resolve(key, mode = :lazy) ⇒ Object

the resolve method takes a key and returns the JasonObject that has that key This is useful when you have the key, but not the class



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/medea/jasonobject.rb', line 59

def JasonObject.resolve(key, mode=:lazy)
  q = JasonDeferredQuery.new nil
  q.filters[:FILTER] ||= {}
  q.filters[:FILTER][:HTTP_X_KEY] = key
  resp = JSON.parse(RestClient.get(q.to_url))
  if resp.has_key? "1"
    #this is the object, figure out its class
    resp["1"]["POST_TO"] =~ /([^\/]+)\/#{key}/
    begin
      result = Kernel.const_get($1).get_by_key key, :lazy
      if result["1"].has_key? "CONTENT"
        result.instance_variable_set(:@__jason_data, result["1"]["CONTENT"])
        result.instance_variable_set(:@__jason_state, :stale)
      end
      if mode == :eager
        result.send(:load)
      end
    rescue
      nil
    end
  end
end

Instance Method Details

#[](key) ⇒ Object



90
91
92
# File 'lib/medea/jasonobject.rb', line 90

def [](key)
  @__jason_data[key]
end

#[]=(key, value) ⇒ Object

“flexihash” access interface



83
84
85
86
87
88
# File 'lib/medea/jasonobject.rb', line 83

def []=(key, value)
  @__jason_data ||= {}
  @__jason_state = :dirty if jason_state == :stale

  @__jason_data[key] = value
end

#delete!Object



222
223
224
225
226
# File 'lib/medea/jasonobject.rb', line 222

def delete!
  url = "#{JasonDB::db_auth_url}#{self.class.name}/#{self.jason_key}"
  response = RestClient.delete url
  raise "DELETE failed!" unless response.code == 201
end

#jason_etagObject



138
139
140
# File 'lib/medea/jasonobject.rb', line 138

def jason_etag
  @__jason_etag ||= ""
end

#jason_keyObject



124
125
126
127
128
# File 'lib/medea/jasonobject.rb', line 124

def jason_key
    #Generate a random UUID for this object.
	#since jason urls must start with a letter, we'll use the first letter of the class name
    @__id ||= "#{self.class.name[0].chr.downcase}#{UUIDTools::UUID::random_create.to_s}"
end

#jason_parentObject



142
143
144
145
146
147
148
149
# File 'lib/medea/jasonobject.rb', line 142

def jason_parent
  @__jason_parent ||= nil
  if @__jason_parent == nil && @__jason_parent_key
    #key is set but parent not? load the parent
    @__jason_parent = JasonObject.resolve @__jason_parent_key
  end
  @__jason_parent
end

#jason_parent=(parent) ⇒ Object



151
152
153
154
# File 'lib/medea/jasonobject.rb', line 151

def jason_parent= parent
  @__jason_parent = parent
  @__jason_parent_key = parent.jason_key
end

#jason_parent_keyObject



156
157
158
# File 'lib/medea/jasonobject.rb', line 156

def jason_parent_key
  @__jason_parent_key ||= nil
end

#jason_parent_key=(value) ⇒ Object



160
161
162
163
164
# File 'lib/medea/jasonobject.rb', line 160

def jason_parent_key= value
  @__jason_parent_key = value
  #reset the parent here?
  @__jason_parent = nil
end

#jason_parent_listObject



166
167
168
# File 'lib/medea/jasonobject.rb', line 166

def jason_parent_list
  @__jason_parent_list ||= nil
end

#jason_parent_list=(value) ⇒ Object



170
171
172
# File 'lib/medea/jasonobject.rb', line 170

def jason_parent_list= value
  @__jason_parent_list = value
end

#jason_stateObject



134
135
136
# File 'lib/medea/jasonobject.rb', line 134

def jason_state
  @__jason_state
end

#save!Object

POSTs the current values of this object back to JasonDB on successful post, sets state to STALE and updates eTag



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/medea/jasonobject.rb', line 178

def save!
    #no changes? no save!
    return if @__jason_state == :stale or @__jason_state == :ghost


    payload = self.to_json
    post_headers = {
        :content_type => 'application/json',

        "X-KEY" => self.jason_key,
        "X-CLASS" => self.class.name
        #also want to add the eTag here!
        #may also want to add any other indexable fields that the user specifies?
    }
    post_headers["IF-MATCH"] = @__jason_etag if @__jason_state == :dirty

    if self.class.owned
      #the parent object needs to be defined!
      raise "#{self.class.name} cannot be saved without setting a parent and list!" unless self.jason_parent && self.jason_parent_list
      post_headers["X-PARENT"] = self.jason_parent.jason_key
      #url = "#{JasonDB::db_auth_url}#{self.jason_parent.class.name}/#{self.jason_parent.jason_key}/#{self.jason_parent_list}/#{self.jason_key}"
      post_headers["X-LIST"] = self.jason_parent_list
      #override the class to be the list name. Much simpler to search on.
      post_headers["X-CLASS"] = self.jason_parent_list
    end
    url = JasonDB::db_auth_url + self.class.name + "/" + self.jason_key

    #puts "Posted to JasonDB!"

    #puts "Saving to #{url}"
    response = RestClient.post url, payload, post_headers

    if response.code == 201
        #save successful!
        #store the new eTag for this object
        #puts response.raw_headers
        #@__jason_etag = response.headers[:location] + ":" + response.headers[:content_md5]
    else
        raise "POST failed! Could not save object"
    end

    @__jason_state = :stale
end

#to_jsonObject

converts the data hash (that is, @__jason_data) to JSON format



231
232
233
# File 'lib/medea/jasonobject.rb', line 231

def to_json
  JSON.generate(@__jason_data)
end

#to_sObject



130
131
132
# File 'lib/medea/jasonobject.rb', line 130

def to_s
  jason_key
end