add new JSONSerializedField Concern and better specs around handling of json string assignment to memory and options

Andrew Cantino 11 anos atrás
pai
commit
6a276e0d76

+ 3 - 11
app/models/agent.rb

@@ -1,4 +1,4 @@
1
-require 'json_with_indifferent_access'
1
+require 'json_serialized_field'
2 2
 require 'assignable_types'
3 3
 require 'markdown_class_attributes'
4 4
 require 'utils'
@@ -6,6 +6,7 @@ require 'utils'
6 6
 class Agent < ActiveRecord::Base
7 7
   include AssignableTypes
8 8
   include MarkdownClassAttributes
9
+  include JSONSerializedField
9 10
 
10 11
   markdown_class_attributes :description, :event_description
11 12
 
@@ -16,8 +17,7 @@ class Agent < ActiveRecord::Base
16 17
 
17 18
   attr_accessible :options, :memory, :name, :type, :schedule, :source_ids
18 19
 
19
-  serialize :options, JSONWithIndifferentAccess
20
-  serialize :memory, JSONWithIndifferentAccess
20
+  json_serialize :options, :memory
21 21
 
22 22
   validates_presence_of :name, :user
23 23
   validate :sources_are_owned
@@ -79,14 +79,6 @@ class Agent < ActiveRecord::Base
79 79
     # Implement me in your subclass to test for valid options.
80 80
   end
81 81
 
82
-  def options=(o)
83
-    self[:options] = ActiveSupport::HashWithIndifferentAccess.new(o)
84
-  end
85
-
86
-  def memory=(o)
87
-    self[:memory] = ActiveSupport::HashWithIndifferentAccess.new(o)
88
-  end
89
-
90 82
   def event_created_within?(days)
91 83
     last_event_at && last_event_at > days.to_i.days.ago
92 84
   end

+ 4 - 6
app/models/event.rb

@@ -1,11 +1,13 @@
1
-require 'json_with_indifferent_access'
1
+require 'json_serialized_field'
2 2
 
3 3
 class Event < ActiveRecord::Base
4
+  include JSONSerializedField
5
+
4 6
   attr_accessible :lat, :lng, :payload, :user_id, :user, :expires_at
5 7
 
6 8
   acts_as_mappable
7 9
 
8
-  serialize :payload, JSONWithIndifferentAccess
10
+  json_serialize :payload
9 11
 
10 12
   belongs_to :user
11 13
   belongs_to :agent, :counter_cache => true, :touch => :last_event_at
@@ -14,10 +16,6 @@ class Event < ActiveRecord::Base
14 16
     where("events.created_at > ?", timespan)
15 17
   }
16 18
 
17
-  def payload=(o)
18
-    self[:payload] = ActiveSupport::HashWithIndifferentAccess.new(o)
19
-  end
20
-
21 19
   def reemit!
22 20
     agent.create_event :payload => payload, :lat => lat, :lng => lng
23 21
   end

+ 42 - 0
lib/json_serialized_field.rb

@@ -0,0 +1,42 @@
1
+require 'json_with_indifferent_access'
2
+
3
+module JSONSerializedField
4
+  extend ActiveSupport::Concern
5
+
6
+  module ClassMethods
7
+    def json_serialize(*fields)
8
+      fields.each do |field|
9
+        class_eval <<-CODE
10
+          serialize :#{field}, JSONWithIndifferentAccess
11
+
12
+          validate :#{field}_has_no_errors
13
+
14
+          def #{field}=(input)
15
+            @#{field}_assignment_error = false
16
+            case input
17
+              when String
18
+                if input.strip.length == 0
19
+                  self[:#{field}] = ActiveSupport::HashWithIndifferentAccess.new
20
+                else
21
+                  json = JSON.parse(input) rescue nil
22
+                  if json
23
+                    self[:#{field}] = ActiveSupport::HashWithIndifferentAccess.new(json)
24
+                  else
25
+                    @#{field}_assignment_error = "was assigned invalid JSON"
26
+                  end
27
+                end
28
+              when Hash
29
+                self[:#{field}] = ActiveSupport::HashWithIndifferentAccess.new(input)
30
+              else
31
+                @#{field}_assignment_error = "cannot be set to an instance of \#{input.class}"
32
+            end
33
+          end
34
+
35
+          def #{field}_has_no_errors
36
+            errors.add(:#{field}, @#{field}_assignment_error) if @#{field}_assignment_error
37
+          end
38
+        CODE
39
+      end
40
+    end
41
+  end
42
+end

+ 1 - 1
lib/json_with_indifferent_access.rb

@@ -1,6 +1,6 @@
1 1
 class JSONWithIndifferentAccess
2 2
   def self.load(json)
3
-    ActiveSupport::HashWithIndifferentAccess.new(JSON.load(json || '{}'))
3
+    ActiveSupport::HashWithIndifferentAccess.new(JSON.parse(json || '{}'))
4 4
   end
5 5
 
6 6
   def self.dump(hash)

+ 42 - 0
spec/models/agent_spec.rb

@@ -266,6 +266,48 @@ describe Agent do
266 266
         agent.memory[:bad].should == 2
267 267
       end
268 268
 
269
+      it "should work when assigned a hash or JSON string" do
270
+        agent = Agents::SomethingSource.new(:name => "something")
271
+        agent.memory = {}
272
+        agent.memory.should == {}
273
+        agent.memory["foo"].should be_nil
274
+
275
+        agent.memory = ""
276
+        agent.memory["foo"].should be_nil
277
+        agent.memory.should == {}
278
+
279
+        agent.memory = '{"hi": "there"}'
280
+        agent.memory.should == { "hi" => "there" }
281
+
282
+        agent.memory = '{invalid}'
283
+        agent.memory.should == { "hi" => "there" }
284
+        agent.should have(1).errors_on(:memory)
285
+
286
+        agent.memory = "{}"
287
+        agent.memory["foo"].should be_nil
288
+        agent.memory.should == {}
289
+        agent.should have(0).errors_on(:memory)
290
+
291
+        agent.options = "{}"
292
+        agent.options["foo"].should be_nil
293
+        agent.options.should == {}
294
+        agent.should have(0).errors_on(:options)
295
+
296
+        agent.options = '{"hi": 2}'
297
+        agent.options["hi"].should == 2
298
+        agent.should have(0).errors_on(:options)
299
+
300
+        agent.options = '{"hi": wut}'
301
+        agent.options["hi"].should == 2
302
+        agent.should have(1).errors_on(:options)
303
+        agent.errors_on(:options).should include("was assigned invalid JSON")
304
+
305
+        agent.options = 5
306
+        agent.options["hi"].should == 2
307
+        agent.should have(1).errors_on(:options)
308
+        agent.errors_on(:options).should include("cannot be set to an instance of Fixnum")
309
+      end
310
+
269 311
       it "should not allow agents owned by other people" do
270 312
         agent = Agents::SomethingSource.new(:name => "something")
271 313
         agent.user = users(:bob)