sugarcrm 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -12,14 +12,15 @@ RubyGem for interacting with SugarCRM via REST.
12
12
 
13
13
  A less clunky way to interact with SugarCRM via REST.
14
14
 
15
- I've built an abstraction layer on top of the SugarCRM REST API, instead of +get_entry("Users", "1")+ you can
16
- call +SugarCRM::User.find(1)+. There is also support for collections à la +SugarCRM::User.find(1).accounts+.
15
+ I've built an abstraction layer on top of the SugarCRM REST API, instead of get_entry("Users", "1") you can
16
+ call SugarCRM::User.find(1). There is also support for collections à la SugarCRM::User.find(1).email_addresses.
17
17
  ActiveRecord style finders are in place, with limited support for conditions and joins
18
- e.g. +SugarCRM::Contacts.find_by_title("VP of Sales")+ will work, but +SugarCRM::Contacts.find_by_title("VP of Sales", {:conditions => {:deleted => 0}})+ will not.
18
+ e.g. SugarCRM::Contacts.find_by_title("VP of Sales") will work, but SugarCRM::Contacts.find_by_title("VP of Sales", {:conditions => {:deleted => 0}}) will not.
19
19
 
20
20
  == FEATURES/PROBLEMS:
21
21
 
22
22
  * Supports all v2 API calls
23
+ * Supports saving of Module specific objects.
23
24
  * Auto-generation of Module specific objects. When a connection is established, get_available_modules is called and the resultant modules are turned into SugarCRM::Module classes.
24
25
  * If you just want to use the vanilla API, you can access the methods directly on the SugarCRM.connection object.
25
26
 
@@ -28,7 +29,10 @@ e.g. +SugarCRM::Contacts.find_by_title("VP of Sales")+ will work, but +SugarCRM:
28
29
  require 'sugarcrm'
29
30
 
30
31
  # Establish a connection
31
- SugarCRM::Base.establish_connection("http://localhost/sugarcrm", 'user', 'password', {:debug => false})
32
+ SugarCRM.connect("http://localhost/sugarcrm", 'user', 'password')
33
+
34
+ # Enable debugging on the current connection
35
+ SugarCRM.connection.debug = true
32
36
 
33
37
  # Get the logged in user
34
38
  SugarCRM.current_user
@@ -38,9 +42,24 @@ e.g. +SugarCRM::Contacts.find_by_title("VP of Sales")+ will work, but +SugarCRM:
38
42
 
39
43
  # Retrieve a User by user_name
40
44
  SugarCRM::User.find_by_user_name("admin")
45
+
46
+ # Update a User's title
47
+ u = SugarCRM::User.find_by_first_name_and_last_name("Will", "Westin")
48
+ u.title = "Sales Manager Central"
49
+ u.save
50
+
51
+ # Check if an object is valid (i.e. if it has the required fields to save)
52
+ u.valid?
53
+
54
+ # Access the errors collection
55
+ u.errors
56
+
57
+ # Delete an Account
58
+ a = SugarCRM::Account.find_by_name("JAB Funds Ltd.")
59
+ a.delete
41
60
 
42
- # Retrieve all Contacts assigned to a particular user.
43
- SugarCRM::User.find_by_user_name('sarah').contacts
61
+ # Retrieve all Email Addresses assigned to a particular user.
62
+ SugarCRM::User.find_by_user_name('sarah').email_addresses
44
63
 
45
64
  # Retrieve all email addresses on an Account
46
65
  SugarCRM::Account.find_by_name("JAB Funds Ltd.").contacts.each do |contact|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.9
1
+ 0.8.0
data/lib/sugarcrm.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'pp'
2
+ require 'set'
2
3
  require 'rubygems'
3
4
  require 'active_support/core_ext'
4
5
 
@@ -1,8 +1,10 @@
1
1
  module SugarCRM; module AssociationMethods
2
2
 
3
- # Returns an array of the module link fields
4
- def associations_from_module_link_fields
5
- self.class._module.link_fields.keys
3
+ module ClassMethods
4
+ # Returns an array of the module link fields
5
+ def associations_from_module_link_fields
6
+ self._module.link_fields.keys
7
+ end
6
8
  end
7
9
 
8
10
  # Generates the association proxy methods for related modules
@@ -30,8 +32,7 @@ module SugarCRM; module AssociationMethods
30
32
  # "type"=>"link"},
31
33
  #
32
34
  def query_association(association)
33
- klass = self.class._module.link_fields[association.to_s]["module"]
34
- objects = SugarCRM.connection.get_relationships(
35
+ collection = SugarCRM.connection.get_relationships(
35
36
  self.class._module.name,
36
37
  self.id,
37
38
  association.to_s
@@ -3,13 +3,95 @@ module SugarCRM; module AttributeMethods
3
3
  module ClassMethods
4
4
  # Returns a hash of the module fields from the module
5
5
  def attributes_from_module_fields
6
- fields = {}
6
+ fields = {}.with_indifferent_access
7
7
  self._module.fields.keys.sort.each do |k|
8
8
  fields[k.to_s] = nil
9
9
  end
10
10
  fields
11
11
  end
12
12
  end
13
+
14
+ # Determines if attributes have been changed
15
+ def changed?
16
+ @modified_attributes.length > 0
17
+ end
18
+
19
+ # Is this a new record?
20
+ def new?
21
+ @id.blank?
22
+ end
23
+
24
+ # Converts the attributes hash into format recognizable by Sugar
25
+ # { :last_name => "Smith"}
26
+ # becomes
27
+ # { :last_name => {:name => "last_name", :value => "Smith"}}
28
+ def serialize_attributes
29
+ attr_hash = {}
30
+ @attributes.each_pair do |name,value|
31
+ attr_hash[name] = serialize_attribute(name,value)
32
+ end
33
+ attr_hash[:id] = serialize_id unless new?
34
+ attr_hash
35
+ end
36
+
37
+ # Converts the modified_attributes hash into format recognizable by Sugar
38
+ # { :last_name => {:old => "Smit", :new => "Smith"}}
39
+ # becomes
40
+ # { :last_name => {:name => "last_name", :value => "Smith"}}
41
+ def serialize_modified_attributes
42
+ attr_hash = {}
43
+ @modified_attributes.each_pair do |name,hash|
44
+ attr_hash[name] = serialize_attribute(name,hash[:new])
45
+ end
46
+ attr_hash[:id] = serialize_id unless new?
47
+ attr_hash
48
+ end
49
+
50
+ # Checks to see if we have all the neccessary attributes
51
+ def valid?
52
+ valid = true
53
+ self.class._module.required_fields.each do |attribute|
54
+ case attr_type_for(attribute)
55
+ when "bool"
56
+ case @attributes[attribute]
57
+ when TrueClass:
58
+ next
59
+ when FalseClass:
60
+ next
61
+ else
62
+ @errors.add "#{attribute} must be true or false"
63
+ valid = false
64
+ end
65
+ else
66
+ if @attributes[attribute].blank?
67
+ @errors.add "#{attribute} cannot be blank"
68
+ valid = false
69
+ end
70
+ end
71
+ end
72
+ valid
73
+ end
74
+
75
+ # List the required attributes for save
76
+ def required_attributes
77
+ self.class._module.required_fields
78
+ end
79
+
80
+ # Serializes the id
81
+ def serialize_id
82
+ {:name => "id", :value => @id.to_s}
83
+ end
84
+
85
+ # Un-typecasts the attribute - false becomes 0
86
+ def serialize_attribute(name,value)
87
+ attr_value = value
88
+ case attr_type_for(name)
89
+ when "bool"
90
+ attr_value = 0
91
+ attr_value = 1 if value
92
+ end
93
+ {:name => name, :value => attr_value}
94
+ end
13
95
 
14
96
  # Generates get/set methods for keys in the attributes hash
15
97
  def define_attribute_methods
@@ -52,6 +134,26 @@ module SugarCRM; module AttributeMethods
52
134
  end
53
135
 
54
136
  protected
137
+
138
+ # Returns the attribute type for a given attribute
139
+ def attr_type_for(attribute)
140
+ field = self.class._module.fields[attribute]
141
+ return false unless field
142
+ field["type"]
143
+ end
144
+
145
+ # Attempts to typecast each attribute based on the module field type
146
+ def typecast_attributes
147
+ @attributes.each_pair do |name,value|
148
+ attr_type = attr_type_for(name)
149
+ next unless attr_type
150
+ case attr_type
151
+ when "bool"
152
+ @attributes[name] = (value == "1")
153
+ end
154
+ end
155
+ @attributes
156
+ end
55
157
 
56
158
  # Wrapper around attributes hash
57
159
  def read_attribute(key)
@@ -60,6 +162,7 @@ module SugarCRM; module AttributeMethods
60
162
 
61
163
  # Wrapper around attributes hash
62
164
  def write_attribute(key, value)
165
+ @modified_attributes[key] = { :old => @attributes[key].to_s, :new => value }
63
166
  @attributes[key] = value
64
167
  end
65
168
 
data/lib/sugarcrm/base.rb CHANGED
@@ -4,7 +4,7 @@ require 'sugarcrm/association_methods'
4
4
  module SugarCRM; class Base
5
5
 
6
6
  # Unset all of the instance methods we don't need.
7
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$|^define_method$|^class$|^instance_of.$)/ }
7
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^send$|^object_id$|^define_method$|^class$|^methods$|^instance_of.$|^respond_to.$)/ }
8
8
 
9
9
  # This holds our connection
10
10
  cattr_accessor :connection, :instance_writer => false
@@ -21,9 +21,11 @@ module SugarCRM; class Base
21
21
 
22
22
  # Contains a list of attributes
23
23
  attr :attributes, true
24
+ attr :modified_attributes, true
24
25
  attr :associations, true
25
26
  attr :id, true
26
27
  attr :debug, true
28
+ attr :errors, true
27
29
 
28
30
  class << self # Class methods
29
31
  def establish_connection(url, user, pass, opts={})
@@ -74,7 +76,7 @@ module SugarCRM; class Base
74
76
 
75
77
  case ids.size
76
78
  when 0
77
- raise RecordNotFound, "Couldn't find #{name} without an ID"
79
+ raise RecordNotFound, "Couldn't find #{self._module.name} without an ID"
78
80
  when 1
79
81
  result = find_one(ids.first, options)
80
82
  expects_array ? [ result ] : result
@@ -124,6 +126,8 @@ module SugarCRM; class Base
124
126
  end
125
127
 
126
128
  def query_from_options(options)
129
+ # If we dont have conditions, just return an empty query
130
+ return "" unless options[:conditions]
127
131
  conditions = []
128
132
  options[:conditions].each_pair do |column, value|
129
133
  conditions << "#{self._module.table_name}.#{column} = \'#{value}\'"
@@ -282,9 +286,13 @@ module SugarCRM; class Base
282
286
  def initialize(id=nil, attributes={})
283
287
  @id = id
284
288
  @attributes = self.class.attributes_from_module_fields.merge(attributes)
285
- @associations = associations_from_module_link_fields
289
+ @modified_attributes = {}
290
+ @associations = self.class.associations_from_module_link_fields
291
+ @errors = Set.new
286
292
  define_attribute_methods
287
293
  define_association_methods
294
+ typecast_attributes
295
+ self
288
296
  end
289
297
 
290
298
  def inspect
@@ -293,16 +301,38 @@ module SugarCRM; class Base
293
301
 
294
302
  def to_s
295
303
  attrs = []
296
- @attributes.each_key do |k|
297
- attrs << "#{k}: #{attribute_for_inspect(k)}"
304
+ @attributes.keys.sort.each do |k|
305
+ attrs << "#{k}: #{attribute_for_inspect(k)}"
298
306
  end
299
307
  "#<#{self.class} #{attrs.join(", ")}>"
300
308
  end
301
309
 
310
+ # Saves the current object, checks that required fields are present.
311
+ # returns true or false
302
312
  def save
303
- response = SugarCRM.connection.set_entry(self._module.name, @attributes)
313
+ return false unless changed?
314
+ return false unless valid?
315
+ # If we get a Hash back, return true. Otherwise return false.
316
+ (SugarCRM.connection.set_entry(self.class._module.name, serialize_modified_attributes).class == Hash)
317
+ end
318
+
319
+ # Saves the current object, checks that required fields are present.
320
+ # raises an exception if a save fails
321
+ def save!
322
+ raise InvalidRecord, errors.to_a.join(", ") unless valid?
323
+ # If we get a Hash back, return true. Otherwise return false.
324
+ (SugarCRM.connection.set_entry(self.class._module.name, serialize_modified_attributes).class == Hash)
304
325
  end
305
326
 
327
+ def delete
328
+ return false if @id.blank?
329
+ params = {}
330
+ params[:id] = serialize_id
331
+ params[:deleted]= {:name => "deleted", :value => "1"}
332
+ (SugarCRM.connection.set_entry(self.class._module.name, params).class == Hash)
333
+ end
334
+
335
+
306
336
  # Wrapper around class attribute
307
337
  def attribute_methods_generated?
308
338
  self.class.attribute_methods_generated
@@ -316,6 +346,7 @@ module SugarCRM; class Base
316
346
  include AttributeMethods
317
347
  extend AttributeMethods::ClassMethods
318
348
  include AssociationMethods
349
+ extend AssociationMethods::ClassMethods
319
350
  end
320
351
 
321
352
  end; end
@@ -87,6 +87,10 @@ module SugarCRM; class Connection
87
87
  options[:debug] = debug
88
88
  end
89
89
 
90
+ def debug?
91
+ options[:debug]
92
+ end
93
+
90
94
  private
91
95
 
92
96
  def handle_response
@@ -3,7 +3,8 @@ module SugarCRM; class Connection
3
3
  # work with the report module.
4
4
  def get_entries(module_name, ids, opts={})
5
5
  login! unless logged_in?
6
- options = { :fields => [],
6
+ options = {
7
+ :fields => [],
7
8
  :link_fields => [],
8
9
  }.merge! opts
9
10
 
@@ -2,7 +2,9 @@ module SugarCRM; class Connection
2
2
  # Retrieves the specified number of records in a module.
3
3
  def get_entries_count(module_name, query, opts={})
4
4
  login! unless logged_in?
5
- options = {:deleted => 0}.merge! opts
5
+ options = {
6
+ :deleted => 0
7
+ }.merge! opts
6
8
 
7
9
  json = <<-EOF
8
10
  {
@@ -2,7 +2,8 @@ module SugarCRM; class Connection
2
2
  # Retrieves a single SugarBean based on the ID.
3
3
  def get_entry(module_name, id, opts={})
4
4
  login! unless logged_in?
5
- options = { :fields => [],
5
+ options = {
6
+ :fields => [],
6
7
  :link_fields => [],
7
8
  }.merge! opts
8
9
 
@@ -8,8 +8,8 @@ def get_entry_list(module_name, query, opts={})
8
8
  :offset => '',
9
9
  :fields => [],
10
10
  :link_fields => [],
11
- :max_results => '',
12
- :deleted => ''
11
+ :limit => '',
12
+ :deleted => 0
13
13
  }.merge! opts
14
14
 
15
15
  json = <<-EOF
@@ -21,7 +21,7 @@ def get_entry_list(module_name, query, opts={})
21
21
  \"offset\": \"#{options[:offset]}\"\,
22
22
  \"select_fields\": #{resolve_fields(module_name, options[:fields])}\,
23
23
  \"link_name_to_fields_array\": #{options[:link_fields].to_json}\,
24
- \"max_results\": \"#{options[:max_results]}\"\,
24
+ \"max_results\": \"#{options[:limit]}\"\,
25
25
  \"deleted\": #{options[:deleted]}
26
26
  }
27
27
  EOF
@@ -2,12 +2,13 @@ module SugarCRM; class Connection
2
2
  # Returns the ID, module name and fields for specified modules.
3
3
  # Supported modules are Accounts, Bugs, Calls, Cases, Contacts,
4
4
  # Leads, Opportunities, Projects, Project Tasks, and Quotes.
5
- def search_by_module(search_string, modules, options={})
5
+ def search_by_module(search_string, modules, ops={})
6
6
  login! unless logged_in?
7
7
 
8
- { :offset => nil,
9
- :max_results => nil,
10
- }.merge! options
8
+ options = {
9
+ :offset => nil,
10
+ :limit => nil,
11
+ }.merge! opts
11
12
 
12
13
  json = <<-EOF
13
14
  {
@@ -15,7 +16,7 @@ module SugarCRM; class Connection
15
16
  \"search_string\": \"#{search_string}\"\,
16
17
  \"modules\": \"#{modules}\"\,
17
18
  \"offset\": #{options[:offset]}\,
18
- \"max_results\": #{options[:max_results]}
19
+ \"max_results\": #{options[:limit]}
19
20
  }
20
21
  EOF
21
22
  json.gsub!(/^\s{6}/,'')
@@ -3,7 +3,12 @@ module SugarCRM; class Connection
3
3
  # FIXME: This is to work around a bug in SugarCRM 6.0
4
4
  # where no fields are returned if no fields are specified
5
5
  if fields.length == 0
6
- fields = Module.find(module_name).fields.keys
6
+ mod = Module.find(module_name)
7
+ if mod
8
+ fields = mod.fields.keys
9
+ else
10
+ fields = "id"
11
+ end
7
12
  end
8
13
  return fields.to_json
9
14
  end
@@ -22,4 +22,7 @@ module SugarCRM
22
22
 
23
23
  class RecordNotFound < RuntimeError
24
24
  end
25
+
26
+ class InvalidRecord < RuntimeError
27
+ end
25
28
  end
@@ -33,6 +33,16 @@ module SugarCRM
33
33
  @fields_registered
34
34
  end
35
35
 
36
+ def required_fields
37
+ required_fields = []
38
+ ignore_fields = ["id", "date_entered", "date_modified"]
39
+ self.fields.each_value do |field|
40
+ next if ignore_fields.include? field["name"]
41
+ required_fields << field["name"] if field["required"] == 1
42
+ end
43
+ required_fields
44
+ end
45
+
36
46
  def link_fields
37
47
  self.fields unless link_fields?
38
48
  handle_empty_array
@@ -7,6 +7,12 @@ module SugarCRM
7
7
  def self.connection=(connection)
8
8
  @@connection = connection
9
9
  end
10
+ def self.connect(url, user, pass, options={})
11
+ SugarCRM::Base.establish_connection(url, user, pass, options)
12
+ end
13
+ class << self
14
+ alias :connect! :connect
15
+ end
10
16
 
11
17
  @@modules = []
12
18
  def self.modules
@@ -8,7 +8,12 @@ module SugarCRM; class Response
8
8
  r = new(json)
9
9
  begin
10
10
  return r.to_obj
11
- rescue
11
+ rescue => e
12
+ if SugarCRM.connection.debug?
13
+ puts "Failed to process JSON:"
14
+ pp json
15
+ puts e
16
+ end
12
17
  return json
13
18
  end
14
19
  end
@@ -23,18 +28,17 @@ module SugarCRM; class Response
23
28
  # Tries to instantiate and return an object with the values
24
29
  # populated from the response
25
30
  def to_obj
31
+ # If this is not a "entry_list" response, just return
32
+ return @response unless @response["entry_list"]
33
+
26
34
  objects = []
27
35
  @response["entry_list"].each do |object|
28
36
  attributes = []
29
37
  _module = resolve_module(object)
30
38
  id = object["id"]
31
- begin
32
- attributes = flatten_name_value_list(object)
33
- rescue ArgumentError => e
34
- end
39
+ attributes = flatten_name_value_list(object)
35
40
  if SugarCRM.const_get(_module)
36
41
  if attributes.length == 0
37
- pp object
38
42
  raise AttributeParsingError, "response contains objects without attributes!"
39
43
  end
40
44
  objects << SugarCRM.const_get(_module).new(id, attributes)
@@ -53,7 +57,7 @@ module SugarCRM; class Response
53
57
  def to_json
54
58
  @response.to_json
55
59
  end
56
-
60
+
57
61
  def resolve_module(list)
58
62
  list["module_name"].classify
59
63
  end
@@ -16,5 +16,8 @@ class TestGetEntry < Test::Unit::TestCase
16
16
  should "return an object when #get_entry" do
17
17
  assert_instance_of SugarCRM::User, @response
18
18
  end
19
+ should "typecast boolean fields properly" do
20
+ assert !(@response.deleted)
21
+ end
19
22
  end
20
23
  end
data/test/helper.rb CHANGED
@@ -8,7 +8,7 @@ require 'sugarcrm'
8
8
 
9
9
  class Test::Unit::TestCase
10
10
  # Replace these with your test instance
11
- URL = "http://valet/sugarcrm6"
11
+ URL = "http://valet/sugarcrm"
12
12
  USER = "admin"
13
13
  PASS = 'letmein'
14
14
 
data/test/test_module.rb CHANGED
@@ -10,5 +10,9 @@ class TestModule < Test::Unit::TestCase
10
10
  should "respond to #fields" do
11
11
  assert_respond_to SugarCRM.modules[0], :fields
12
12
  end
13
+
14
+ should "return required fields when #required_fields" do
15
+ assert SugarCRM::User._module.required_fields.include? "user_name"
16
+ end
13
17
  end
14
18
  end
@@ -3,6 +3,11 @@ require 'helper'
3
3
  class TestSugarCRM < Test::Unit::TestCase
4
4
  context "A SugarCRM::Base instance" do
5
5
 
6
+ should "establish a connection when SugarCRM#connect!" do
7
+ SugarCRM.connect!(URL, USER, PASS)
8
+ assert SugarCRM.connection.connected?
9
+ end
10
+
6
11
  should "establish a connection when Base#establish_connection" do
7
12
  SugarCRM::Base.establish_connection(URL, USER, PASS)
8
13
  assert SugarCRM.connection.connected?
@@ -12,6 +17,10 @@ class TestSugarCRM < Test::Unit::TestCase
12
17
  assert_equal "Users", SugarCRM::User._module.name
13
18
  end
14
19
 
20
+ should "responsd to self#methods" do
21
+ assert_instance_of Array, SugarCRM::User.new.methods
22
+ end
23
+
15
24
  should "respond to self.connection" do
16
25
  assert_respond_to SugarCRM::User, :connection
17
26
  assert_instance_of SugarCRM::Connection, SugarCRM::User.connection
@@ -26,7 +35,7 @@ class TestSugarCRM < Test::Unit::TestCase
26
35
  end
27
36
 
28
37
  should "respond to self.attributes_from_modules_fields" do
29
- assert_instance_of Hash, SugarCRM::User.attributes_from_module_fields
38
+ assert_instance_of ActiveSupport::HashWithIndifferentAccess, SugarCRM::User.attributes_from_module_fields
30
39
  end
31
40
 
32
41
  should "return an instance of itself when #new" do
@@ -38,10 +47,31 @@ class TestSugarCRM < Test::Unit::TestCase
38
47
  assert SugarCRM::User.attribute_methods_generated
39
48
  end
40
49
 
41
- should "respond to attributes derived from #_module.fields" do
50
+ should "not save a record that is missing required attributes" do
42
51
  u = SugarCRM::User.new
43
52
  u.last_name = "Test"
44
- assert_equal "Test", u.last_name
53
+ assert !u.save
54
+ assert_raise SugarCRM::InvalidRecord do
55
+ u.save!
56
+ end
57
+ end
58
+
59
+ should "create, modify, and delete a record" do
60
+ #SugarCRM.connection.debug = true
61
+ u = SugarCRM::User.new
62
+ u.email1 = "abc@abc.com"
63
+ u.first_name = "Test"
64
+ u.last_name = "User"
65
+ u.system_generated_password = false
66
+ u.user_name = "test_user"
67
+ u.status = "Active"
68
+ assert_equal "Test", u.modified_attributes[:first_name][:new]
69
+ assert u.save!
70
+ m = SugarCRM::User.find_by_first_name_and_last_name("Test", "User")
71
+ m.title = "Test User"
72
+ assert m.save!
73
+ assert m.delete
74
+ #SugarCRM.connection.debug = false
45
75
  end
46
76
 
47
77
  should "return an an instance of itself when sent #find(id)" do
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sugarcrm
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 63
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 7
9
- - 9
10
- version: 0.7.9
8
+ - 8
9
+ - 0
10
+ version: 0.8.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Carl Hicks
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-14 00:00:00 -08:00
18
+ date: 2010-12-04 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency