Guest User

Untitled

a guest
Jul 20th, 2018
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 65.54 KB | None | 0 0
  1.  
  2.  
  3. From 622d0ffcfa65a500da995e74f6765b3788cc3539 Mon Sep 17 00:00:00 2001
  4. From: Taryn East <git@taryneast.org>
  5. Date: Wed, 2 Dec 2009 21:50:55 +0000
  6. Subject: [PATCH] Bare essentials of the schema - an array on ARes
  7.  
  8. So, we will be embarking on the grand adventure of schema-dom for Active
  9. Resource. Begun by a very simple array being stored on the Active Resource
  10. object.
  11. This is clearly not useful for anything at this stage, but sets up the
  12. foundatin for all the rest of the funtionality.
  13. From here we can 'complicate' things by making the schema migration-like and
  14. actually using it to do useful stuff... but for now, this is the beginning.
  15. ---
  16. activeresource/lib/active_resource/base.rb | 16 ++++
  17. activeresource/test/cases/base/schema_test.rb | 115 +++++++++++++++++++++++++
  18. 2 files changed, 131 insertions(+), 0 deletions(-)
  19. create mode 100644 activeresource/test/cases/base/schema_test.rb
  20.  
  21. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  22. index 18105e8..b8aa028 100644
  23. --- a/activeresource/lib/active_resource/base.rb
  24. +++ b/activeresource/lib/active_resource/base.rb
  25. @@ -241,6 +241,15 @@ module ActiveResource
  26. cattr_accessor :logger
  27.  
  28. class << self
  29. + # Schema defines the known attributes of the current model.
  30. + #
  31. + # TODO: under construction - will add more doco as I go.
  32. + def schema=(the_schema)
  33. + @schema = the_schema
  34. + end
  35. + def schema
  36. + @schema ||= nil
  37. + end
  38. # Gets the URI of the REST resources to map for this class. The site variable is required for
  39. # Active Resource's mapping to work.
  40. def site
  41. @@ -776,6 +785,13 @@ module ActiveResource
  42. attr_accessor :attributes #:nodoc:
  43. attr_accessor :prefix_options #:nodoc:
  44.  
  45. + # If no schema has been defined for the class (see
  46. + # <tt>ActiveResource::schema=</tt>), the default automatic schema is
  47. + # generated from the current instance's attributes
  48. + def schema
  49. + self.class.schema || self.attributes.keys
  50. + end
  51. +
  52. # Constructor method for \new resources; the optional +attributes+ parameter takes a \hash
  53. # of attributes for the \new resource.
  54. #
  55. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  56. new file mode 100644
  57. index 0000000..2fb2707
  58. --- /dev/null
  59. +++ b/activeresource/test/cases/base/schema_test.rb
  60. @@ -0,0 +1,115 @@
  61. +require 'abstract_unit'
  62. +require "fixtures/person"
  63. +require "fixtures/street_address"
  64. +
  65. +########################################################################
  66. +# Testing the schema of your Active Resource models
  67. +########################################################################
  68. +class SchemaTest < ActiveModel::TestCase
  69. + def setup
  70. + @matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
  71. + @david = { :id => 2, :name => 'David' }.to_xml(:root => 'person')
  72. + @greg = { :id => 3, :name => 'Greg' }.to_xml(:root => 'person')
  73. + @addy = { :id => 1, :street => '12345 Street', :country => 'Australia' }.to_xml(:root => 'address')
  74. + @default_request_headers = { 'Content-Type' => 'application/xml' }
  75. + @rick = { :name => "Rick", :age => 25 }.to_xml(:root => "person")
  76. + @people = [{ :id => 1, :name => 'Matz' }, { :id => 2, :name => 'David' }].to_xml(:root => 'people')
  77. + @people_david = [{ :id => 2, :name => 'David' }].to_xml(:root => 'people')
  78. + @addresses = [{ :id => 1, :street => '12345 Street', :country => 'Australia' }].to_xml(:root => 'addresses')
  79. +
  80. + ActiveResource::HttpMock.respond_to do |mock|
  81. + mock.get "/people/1.xml", {}, @matz
  82. + mock.get "/people/2.xml", {}, @david
  83. + mock.get "/people/Greg.xml", {}, @greg
  84. + mock.get "/people/4.xml", {'key' => 'value'}, nil, 404
  85. + mock.get "/people/5.xml", {}, @rick
  86. + mock.put "/people/1.xml", {}, nil, 204
  87. + mock.delete "/people/1.xml", {}, nil, 200
  88. + mock.delete "/people/2.xml", {}, nil, 400
  89. + mock.get "/people/99.xml", {}, nil, 404
  90. + mock.post "/people.xml", {}, @rick, 201, 'Location' => '/people/5.xml'
  91. + mock.get "/people.xml", {}, @people
  92. + mock.get "/people/1/addresses.xml", {}, @addresses
  93. + mock.get "/people/1/addresses/1.xml", {}, @addy
  94. + mock.get "/people/1/addresses/2.xml", {}, nil, 404
  95. + mock.get "/people/2/addresses/1.xml", {}, nil, 404
  96. + mock.get "/people/Greg/addresses/1.xml", {}, @addy
  97. + mock.put "/people/1/addresses/1.xml", {}, nil, 204
  98. + mock.delete "/people/1/addresses/1.xml", {}, nil, 200
  99. + mock.post "/people/1/addresses.xml", {}, nil, 201, 'Location' => '/people/1/addresses/5'
  100. + mock.get "/people//addresses.xml", {}, nil, 404
  101. + mock.get "/people//addresses/1.xml", {}, nil, 404
  102. + mock.put "/people//addressaddresseses/1.xml", {}, nil, 404
  103. + mock.delete "/people//addresses/1.xml", {}, nil, 404
  104. + mock.post "/people//addresses.xml", {}, nil, 404
  105. + mock.head "/people/1.xml", {}, nil, 200
  106. + mock.head "/people/Greg.xml", {}, nil, 200
  107. + mock.head "/people/99.xml", {}, nil, 404
  108. + mock.head "/people/1/addresses/1.xml", {}, nil, 200
  109. + mock.head "/people/1/addresses/2.xml", {}, nil, 404
  110. + mock.head "/people/2/addresses/1.xml", {}, nil, 404
  111. + mock.head "/people/Greg/addresses/1.xml", {}, nil, 200
  112. + end
  113. +
  114. + Person.user = nil
  115. + Person.password = nil
  116. + end
  117. +
  118. + test "schema on a new model should be empty" do
  119. + assert Person.schema.blank?, "should have a blank class schema"
  120. + assert Person.new.schema.blank?, "should have a blank instance schema"
  121. + end
  122. +
  123. + test "schema on a found model should return all the attributes of that model instance" do
  124. + p = Person.find(1)
  125. + s = p.schema
  126. +
  127. + assert s.present?, "should have found a non-empty schema!"
  128. +
  129. + p.attributes.each do |the_attr, val|
  130. + assert s.include?(the_attr), "should have found attr: #{the_attr} in schema, but only had: #{s.inspect}"
  131. + end
  132. + end
  133. +
  134. + test "with two instances, default schema should match the attributes of the individual instances - even if they differ" do
  135. + matz = Person.find(1)
  136. + rick = Person.find(5)
  137. +
  138. + m_attrs = matz.attributes.keys.sort
  139. + r_attrs = rick.attributes.keys.sort
  140. +
  141. + assert_not_equal m_attrs, r_attrs, "should have different attributes on each model"
  142. +
  143. + assert_not_equal matz.schema, rick.schema, "should have had different schemas too"
  144. + end
  145. +
  146. + test "defining a schema should return it when asked" do
  147. + assert Person.schema.blank?, "should have a blank class schema"
  148. + new_schema = [:name, :age, :height, :weight]
  149. + assert_nothing_raised {
  150. + Person.schema = new_schema
  151. + assert_equal new_schema, Person.schema, "should have saved the schema on the class"
  152. + assert_equal new_schema, Person.new.schema, "should have mde the schema available to every instance"
  153. + }
  154. +
  155. + Person.schema = nil # hack to stop test bleedthrough...
  156. + end
  157. +
  158. + test "defining a schema, then fetching a model should still match the defined schema" do
  159. + # sanity checks
  160. + assert Person.schema.blank?, "should have a blank class schema"
  161. + new_schema = [:name, :age, :height, :weight]
  162. +
  163. + matz = Person.find(1)
  164. + assert !matz.schema.blank?, "should have some sort of schema on an instance variable"
  165. + assert_not_equal new_schema, matz.schema, "should not have the class-level schema until it's been added to the class!"
  166. +
  167. + assert_nothing_raised {
  168. + Person.schema = new_schema
  169. + assert_equal new_schema, matz.schema, "class-level schema should override instance-level schema"
  170. + }
  171. +
  172. + Person.schema = nil # hack to stop test bleedthrough...
  173. + end
  174. +
  175. +end
  176. --
  177. 1.5.6.3
  178.  
  179.  
  180. From 3d2558e53c970dd572e1698aa00e8f83700b38b1 Mon Sep 17 00:00:00 2001
  181. From: Taryn East <git@taryneast.org>
  182. Date: Wed, 2 Dec 2009 21:53:13 +0000
  183. Subject: [PATCH] use schema to respond_to known 'columns' with true
  184.  
  185. First actual functionality of a schema - respond_to? will recognise any
  186. schema attributes as valid methods.
  187. ---
  188. activeresource/lib/active_resource/base.rb | 2 ++
  189. activeresource/test/cases/base/schema_test.rb | 16 ++++++++++++++++
  190. 2 files changed, 18 insertions(+), 0 deletions(-)
  191.  
  192. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  193. index b8aa028..294acba 100644
  194. --- a/activeresource/lib/active_resource/base.rb
  195. +++ b/activeresource/lib/active_resource/base.rb
  196. @@ -1175,6 +1175,8 @@ module ActiveResource
  197. super
  198. elsif attributes.has_key?(method_name)
  199. true
  200. + elsif schema && schema.include?(method_name.to_sym)
  201. + true
  202. elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`)
  203. true
  204. else
  205. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  206. index 2fb2707..edd32e1 100644
  207. --- a/activeresource/test/cases/base/schema_test.rb
  208. +++ b/activeresource/test/cases/base/schema_test.rb
  209. @@ -112,4 +112,20 @@ class SchemaTest < ActiveModel::TestCase
  210. Person.schema = nil # hack to stop test bleedthrough...
  211. end
  212.  
  213. + test "should respond to all attributes in a schema" do
  214. + assert Person.schema.blank?, "should have a blank class schema"
  215. + new_schema = [:name, :age, :height, :weight, :my_new_schema_attribute ]
  216. + p = Person.new
  217. + #sanity check - should not respond to the brand-new one
  218. + assert !p.respond_do?(:my_new_schema_attribute)
  219. +
  220. + assert_nothing_raised {
  221. + Person.schema = new_schema
  222. + new_schema.each do |the_attr|
  223. + assert Person.new.respond_to?(the_attr), "shoud respond to the schema's methods, but failed on: #{the_attr}"
  224. + end
  225. + }
  226. + Person.schema = nil # hack to stop test bleedthrough...
  227. + end
  228. +
  229. end
  230. --
  231. 1.5.6.3
  232.  
  233.  
  234. From d3a18363ef73d36192aa43abdb6b20590e7887e4 Mon Sep 17 00:00:00 2001
  235. From: Taryn East <git@taryneast.org>
  236. Date: Wed, 2 Dec 2009 22:06:43 +0000
  237. Subject: [PATCH] More documentation for schema to explain.
  238.  
  239. Just a bit more commenting on what schema is and what it will do.
  240. ---
  241. activeresource/lib/active_resource/base.rb | 47 +++++++++++++++++++++++++--
  242. 1 files changed, 43 insertions(+), 4 deletions(-)
  243.  
  244. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  245. index 294acba..f02e595 100644
  246. --- a/activeresource/lib/active_resource/base.rb
  247. +++ b/activeresource/lib/active_resource/base.rb
  248. @@ -243,13 +243,52 @@ module ActiveResource
  249. class << self
  250. # Schema defines the known attributes of the current model.
  251. #
  252. - # TODO: under construction - will add more doco as I go.
  253. - def schema=(the_schema)
  254. - @schema = the_schema
  255. - end
  256. + # The schema we pass into an Active Resource is similar to an Active
  257. + # Record database schema definition. It defines the known attributes
  258. + # of a certain Active Record - even if there are currently no values
  259. + # for those attributes. It is like telling Active Resource that you
  260. + # know there will be attributes there someday.
  261. + #
  262. + # There is no need to specify a schema for your Active Resource. If
  263. + # you do not, the schema will be automatically generated on an
  264. + # instance from the attributes found there. That kind of schema is
  265. + # transient and it is possible that each instance will have a
  266. + # different 'schema' of this sort.
  267. + #
  268. + # The benefits of knowing the schema in advance, is that your instance
  269. + # method will know that they can respond to the known attributes and
  270. + # will return 'nil' when questioned (instead of MethodNotFound). This
  271. + # is helpful if you wish to have validations on those attributes.
  272. + #
  273. + #
  274. + # This section is very much under construction. More documentaion will
  275. + # be added as the functionality is expanded.
  276. + #
  277. + #
  278. + # For the moment, all you can do is pass an array of known
  279. + # attribute-names and it will be saved on the Resource. See
  280. + # <tt>schema=</tt> for an example of how this works.
  281. + #
  282. + # In future, the Resource will be far more responsive eg will return
  283. + # 'true' for a known attribute or cast the value saved to the
  284. + # appropriate type - just as Active Record currently does.
  285. def schema
  286. @schema ||= nil
  287. end
  288. + # Saves a schema to this resource - telling it what attributes are
  289. + # already known prior to fetching an object of this type form the
  290. + # remote system.
  291. + #
  292. + # example:
  293. + # class Person < ActiveResource::Base
  294. + # schema = [:name, :age, :height, :weight]
  295. + # end
  296. + # p = Person.new
  297. + # p.respond_to? :name # > true
  298. + #
  299. + def schema=(the_schema)
  300. + @schema = the_schema
  301. + end
  302. # Gets the URI of the REST resources to map for this class. The site variable is required for
  303. # Active Resource's mapping to work.
  304. def site
  305. --
  306. 1.5.6.3
  307.  
  308.  
  309. From 5bde25dc25bc65510a6876299ce8d10e417ca544 Mon Sep 17 00:00:00 2001
  310. From: Taryn East <git@taryneast.org>
  311. Date: Wed, 2 Dec 2009 22:22:42 +0000
  312. Subject: [PATCH] return nil instead of MethodNotFound if in schema
  313.  
  314. So this is why I wanted the schema all along - so that instead of exploding
  315. on a known attribute (simply because it hadn't been set yet) it should
  316. return nil - just like Active Record does.
  317. ---
  318. activeresource/lib/active_resource/base.rb | 4 ++-
  319. activeresource/test/cases/base/schema_test.rb | 33 +++++++++++++++++-------
  320. 2 files changed, 26 insertions(+), 11 deletions(-)
  321.  
  322. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  323. index f02e595..b8134cd 100644
  324. --- a/activeresource/lib/active_resource/base.rb
  325. +++ b/activeresource/lib/active_resource/base.rb
  326. @@ -1319,7 +1319,9 @@ module ActiveResource
  327. attributes[$`]
  328. end
  329. else
  330. - attributes.include?(method_name) ? attributes[method_name] : super
  331. + return attributes[method_name] if attributes.include?(method_name)
  332. + return nil if self.class.schema.include?(method_symbol.to_sym)
  333. + super
  334. end
  335. end
  336. end
  337. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  338. index edd32e1..a3517d4 100644
  339. --- a/activeresource/test/cases/base/schema_test.rb
  340. +++ b/activeresource/test/cases/base/schema_test.rb
  341. @@ -112,20 +112,33 @@ class SchemaTest < ActiveModel::TestCase
  342. Person.schema = nil # hack to stop test bleedthrough...
  343. end
  344.  
  345. - test "should respond to all attributes in a schema" do
  346. - assert Person.schema.blank?, "should have a blank class schema"
  347. - new_schema = [:name, :age, :height, :weight, :my_new_schema_attribute ]
  348. - p = Person.new
  349. - #sanity check - should not respond to the brand-new one
  350. - assert !p.respond_do?(:my_new_schema_attribute)
  351. + test "should respond positively to attributes only in a schema" do
  352. + new_attr_name = :my_new_schema_attribute
  353. + assert Person.schema.blank?, "sanity check - should have a blank class schema"
  354. +
  355. + assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet"
  356.  
  357. assert_nothing_raised {
  358. - Person.schema = new_schema
  359. - new_schema.each do |the_attr|
  360. - assert Person.new.respond_to?(the_attr), "shoud respond to the schema's methods, but failed on: #{the_attr}"
  361. - end
  362. + Person.schema = [new_attr_name]
  363. + assert Person.new.respond_to?(new_attr_name), "should respond to the schema's methods, but failed on: #{new_attr_name}"
  364. }
  365. Person.schema = nil # hack to stop test bleedthrough...
  366. end
  367.  
  368. + test "should not give method_missing for attribute only in schema" do
  369. + new_attr_name = :another_new_schema_attribute
  370. +
  371. + assert Person.schema.blank?, "sanity check - should have a blank class schema"
  372. +
  373. + assert_raises(NoMethodError, "should not have found the attribute: #{new_attr_name} as a method") do
  374. + Person.new.send(new_attr_name)
  375. + end
  376. +
  377. + Person.schema = [new_attr_name]
  378. + assert_nothing_raised do
  379. + Person.new.send(new_attr_name)
  380. + end
  381. + Person.schema = nil # hack to stop test bleedthrough...
  382. + end
  383. +
  384. end
  385. --
  386. 1.5.6.3
  387.  
  388.  
  389. From 21e069cc4ef7b1240a6389b09d47f31e46fa8bfc Mon Sep 17 00:00:00 2001
  390. From: Taryn East <git@taryneast.org>
  391. Date: Wed, 2 Dec 2009 22:36:11 +0000
  392. Subject: [PATCH] Should symbolise schema keys to normalise
  393.  
  394. Rather than hashes with indifferent access and all the inherant errors, we
  395. need to settle on either strings or symbols. I may change it all over to
  396. strings instead (as there seems to be a common theme in ARes for that), but
  397. for now - it's all symbols.
  398. ---
  399. activeresource/lib/active_resource/base.rb | 3 ++-
  400. activeresource/test/cases/base/schema_test.rb | 25 +++++++++++++++++++------
  401. 2 files changed, 21 insertions(+), 7 deletions(-)
  402.  
  403. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  404. index b8134cd..a470a39 100644
  405. --- a/activeresource/lib/active_resource/base.rb
  406. +++ b/activeresource/lib/active_resource/base.rb
  407. @@ -287,7 +287,8 @@ module ActiveResource
  408. # p.respond_to? :name # > true
  409. #
  410. def schema=(the_schema)
  411. - @schema = the_schema
  412. + @schema = nil
  413. + @schema = the_schema.map(&:to_sym) if the_schema.present?
  414. end
  415. # Gets the URI of the REST resources to map for this class. The site variable is required for
  416. # Active Resource's mapping to work.
  417. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  418. index a3517d4..9d297b5 100644
  419. --- a/activeresource/test/cases/base/schema_test.rb
  420. +++ b/activeresource/test/cases/base/schema_test.rb
  421. @@ -54,12 +54,31 @@ class SchemaTest < ActiveModel::TestCase
  422. Person.user = nil
  423. Person.password = nil
  424. end
  425. + def teardown
  426. + Person.schema = nil # hack to stop test bleedthrough...
  427. + end
  428.  
  429. test "schema on a new model should be empty" do
  430. assert Person.schema.blank?, "should have a blank class schema"
  431. assert Person.new.schema.blank?, "should have a blank instance schema"
  432. end
  433.  
  434. + test "schema should accept array of syms" do
  435. + new_schema = [:age, :name]
  436. +
  437. + assert_nothing_raised { Person.schema = new_schema }
  438. + assert_equal new_schema, Person.schema
  439. + end
  440. +
  441. + test "schema should symbolise array of strings" do
  442. + new_schema = ['name', 'age']
  443. + new_schema_syms = new_schema.map(&:to_sym)
  444. +
  445. + assert_nothing_raised { Person.schema = new_schema }
  446. + assert_equal new_schema_syms, Person.schema
  447. + end
  448. +
  449. +
  450. test "schema on a found model should return all the attributes of that model instance" do
  451. p = Person.find(1)
  452. s = p.schema
  453. @@ -91,8 +110,6 @@ class SchemaTest < ActiveModel::TestCase
  454. assert_equal new_schema, Person.schema, "should have saved the schema on the class"
  455. assert_equal new_schema, Person.new.schema, "should have mde the schema available to every instance"
  456. }
  457. -
  458. - Person.schema = nil # hack to stop test bleedthrough...
  459. end
  460.  
  461. test "defining a schema, then fetching a model should still match the defined schema" do
  462. @@ -108,8 +125,6 @@ class SchemaTest < ActiveModel::TestCase
  463. Person.schema = new_schema
  464. assert_equal new_schema, matz.schema, "class-level schema should override instance-level schema"
  465. }
  466. -
  467. - Person.schema = nil # hack to stop test bleedthrough...
  468. end
  469.  
  470. test "should respond positively to attributes only in a schema" do
  471. @@ -122,7 +137,6 @@ class SchemaTest < ActiveModel::TestCase
  472. Person.schema = [new_attr_name]
  473. assert Person.new.respond_to?(new_attr_name), "should respond to the schema's methods, but failed on: #{new_attr_name}"
  474. }
  475. - Person.schema = nil # hack to stop test bleedthrough...
  476. end
  477.  
  478. test "should not give method_missing for attribute only in schema" do
  479. @@ -138,7 +152,6 @@ class SchemaTest < ActiveModel::TestCase
  480. assert_nothing_raised do
  481. Person.new.send(new_attr_name)
  482. end
  483. - Person.schema = nil # hack to stop test bleedthrough...
  484. end
  485.  
  486. end
  487. --
  488. 1.5.6.3
  489.  
  490.  
  491.  
  492. From d67a56aea26e3993cdeec6b2d28b5a7ee46cd323 Mon Sep 17 00:00:00 2001
  493. From: taryneast <rubygirl@taryneast.org>
  494. Date: Tue, 8 Dec 2009 21:47:42 +0000
  495. Subject: [PATCH] Schema takes a hash, also added known_attributes
  496.  
  497. Schema now takes a hash, which is one step closer to being actually useful.
  498. Right now it ignores the values of the hash, but that will start to be used
  499. in the next steps.
  500.  
  501. Adding a schema now also populates a 'known_attributes' accessor - which
  502. takes over a lot of the functionality that I just had before - ie if we know
  503. about an attribute, we don't explode on method_missig, and we respond_to? it
  504. with true.
  505.  
  506. known_attributes differs from attributes only in that it also recognises
  507. attributes from the schema.
  508.  
  509. I also expanded on the comments/rdoc a little more.
  510. ---
  511. activeresource/lib/active_resource/base.rb | 88 +++++++++++++-------
  512. activeresource/test/cases/base/schema_test.rb | 111 ++++++++++++++++++++++---
  513. 2 files changed, 155 insertions(+), 44 deletions(-)
  514.  
  515. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  516. index a470a39..3585f9d 100644
  517. --- a/activeresource/lib/active_resource/base.rb
  518. +++ b/activeresource/lib/active_resource/base.rb
  519. @@ -241,55 +241,74 @@ module ActiveResource
  520. cattr_accessor :logger
  521.  
  522. class << self
  523. - # Schema defines the known attributes of the current model.
  524. + # Schema defines the known attributes of the current resource.
  525. #
  526. # The schema we pass into an Active Resource is similar to an Active
  527. - # Record database schema definition. It defines the known attributes
  528. - # of a certain Active Record - even if there are currently no values
  529. - # for those attributes. It is like telling Active Resource that you
  530. - # know there will be attributes there someday.
  531. + # Record database migration. It defines the known attributes of the
  532. + # Resource - even if there are currently no values for those
  533. + # attributes. It is like telling Active Resource that you know there
  534. + # will be attributes there someday.
  535. #
  536. # There is no need to specify a schema for your Active Resource. If
  537. - # you do not, the schema will be automatically generated on an
  538. - # instance from the attributes found there. That kind of schema is
  539. - # transient and it is possible that each instance will have a
  540. - # different 'schema' of this sort.
  541. + # you do not, the schema will be guessed from the instance attributes
  542. + # returned when the instance is fetched from the remote system (as
  543. + # before). The guessed schema is transient and it is possible that
  544. + # each instance will have a different 'schema' of this sort.
  545. #
  546. - # The benefits of knowing the schema in advance, is that your instance
  547. - # method will know that they can respond to the known attributes and
  548. - # will return 'nil' when questioned (instead of MethodNotFound). This
  549. - # is helpful if you wish to have validations on those attributes.
  550. + # The benefit of knowing the schema in advance is that your instance
  551. + # method will know that they can <tt>respond_to></tt> the known
  552. + # attributes and will return 'nil' when questioned (instead of
  553. + # MethodNotFound). This is helpful if you wish to have validations on
  554. + # those attributes (eg <tt>validates_presence_of</tt>)
  555. #
  556. #
  557. # This section is very much under construction. More documentaion will
  558. # be added as the functionality is expanded.
  559. #
  560. #
  561. - # For the moment, all you can do is pass an array of known
  562. - # attribute-names and it will be saved on the Resource. See
  563. - # <tt>schema=</tt> for an example of how this works.
  564. + # For the moment, all you can do is pass a hash of known
  565. + # attribute-names with any values and it will be saved on the
  566. + # Resource. See <tt>schema=</tt> for an example of how this works.
  567. #
  568. - # In future, the Resource will be far more responsive eg will return
  569. - # 'true' for a known attribute or cast the value saved to the
  570. - # appropriate type - just as Active Record currently does.
  571. + # The attribute-names are also saved into the Resource's
  572. + # <tt>known_attributes</tt>
  573. + #
  574. + # In future, the Resource will be far more responsive eg will cast the
  575. + # attribute's value saved to the appropriate type - just as Active
  576. + # Record currently does.
  577. def schema
  578. @schema ||= nil
  579. end
  580. - # Saves a schema to this resource - telling it what attributes are
  581. - # already known prior to fetching an object of this type form the
  582. - # remote system.
  583. +
  584. + # Saves a schema to this resource - setting the attributes that are
  585. + # known prior to fetching an instance from the remote system.
  586. #
  587. # example:
  588. # class Person < ActiveResource::Base
  589. - # schema = [:name, :age, :height, :weight]
  590. + # schema = {:name => :string, :age => :integer,
  591. + # :height => :float, :weight => :float }
  592. # end
  593. # p = Person.new
  594. - # p.respond_to? :name # > true
  595. - #
  596. + # p.respond_to? :name # => true
  597. + # p.name # => nil
  598. + #
  599. def schema=(the_schema)
  600. @schema = nil
  601. - @schema = the_schema.map(&:to_sym) if the_schema.present?
  602. + @known_attributes = []
  603. + return unless the_schema.present?
  604. +
  605. + raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash
  606. +
  607. + @schema = the_schema.with_indifferent_access
  608. + @known_attributes = the_schema.keys.map(&:to_s)
  609. + end
  610. +
  611. + # Returns the list of known attributes for this resource, gathered
  612. + # from the provided <tt>schema</tt>
  613. + def known_attributes
  614. + @known_attributes ||= []
  615. end
  616. +
  617. # Gets the URI of the REST resources to map for this class. The site variable is required for
  618. # Active Resource's mapping to work.
  619. def site
  620. @@ -829,9 +848,17 @@ module ActiveResource
  621. # <tt>ActiveResource::schema=</tt>), the default automatic schema is
  622. # generated from the current instance's attributes
  623. def schema
  624. - self.class.schema || self.attributes.keys
  625. + self.class.schema || self.attributes
  626. + end
  627. +
  628. + # This is a list of known attributes for this resource. Either
  629. + # gathered fromthe provided <tt>schema</tt>, or from the attributes
  630. + # set on this instance after it has been fetched from the remote system.
  631. + def known_attributes
  632. + self.class.known_attributes + self.attributes.keys.map(&:to_s)
  633. end
  634.  
  635. +
  636. # Constructor method for \new resources; the optional +attributes+ parameter takes a \hash
  637. # of attributes for the \new resource.
  638. #
  639. @@ -1213,9 +1240,7 @@ module ActiveResource
  640. method_name = method.to_s
  641. if attributes.nil?
  642. super
  643. - elsif attributes.has_key?(method_name)
  644. - true
  645. - elsif schema && schema.include?(method_name.to_sym)
  646. + elsif known_attributes.include?(method_name)
  647. true
  648. elsif method_name =~ /(?:=|\?)$/ && attributes.include?($`)
  649. true
  650. @@ -1321,7 +1346,8 @@ module ActiveResource
  651. end
  652. else
  653. return attributes[method_name] if attributes.include?(method_name)
  654. - return nil if self.class.schema.include?(method_symbol.to_sym)
  655. + # not set right now but we know about it
  656. + return nil if known_attributes.include?(method_name)
  657. super
  658. end
  659. end
  660. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  661. index 9d297b5..a8e9bc8 100644
  662. --- a/activeresource/test/cases/base/schema_test.rb
  663. +++ b/activeresource/test/cases/base/schema_test.rb
  664. @@ -63,30 +63,52 @@ class SchemaTest < ActiveModel::TestCase
  665. assert Person.new.schema.blank?, "should have a blank instance schema"
  666. end
  667.  
  668. - test "schema should accept array of syms" do
  669. - new_schema = [:age, :name]
  670. + test "schema should only accept a hash" do
  671. + ["blahblah",['one','two'], [:age, :name]].each do |bad_schema|
  672. + assert_raises(ArgumentError,"should only accept a hash, but accepted: #{bad_schema.inspect}") do
  673. + Person.schema = bad_schema
  674. + end
  675. + end
  676. + end
  677. +
  678. + test "schema should accept a simple hash" do
  679. + new_schema = {'age' => nil, 'name' => nil}
  680.  
  681. assert_nothing_raised { Person.schema = new_schema }
  682. assert_equal new_schema, Person.schema
  683. end
  684.  
  685. - test "schema should symbolise array of strings" do
  686. - new_schema = ['name', 'age']
  687. - new_schema_syms = new_schema.map(&:to_sym)
  688. + test "schema should accept nil and remove the schema" do
  689. + new_schema = {'age' => nil, 'name' => nil}
  690. + assert_nothing_raised { Person.schema = new_schema }
  691. + assert_equal new_schema, Person.schema # sanity check
  692. +
  693. +
  694. + assert_nothing_raised { Person.schema = nil }
  695. + assert_nil Person.schema, "should have nulled out the schema, but still had: #{Person.schema.inspect}"
  696. + end
  697. +
  698. +
  699. + test "schema should be with indifferent access" do
  700. + new_schema = {:age => nil, 'name' => nil}
  701. + new_schema_syms = new_schema.keys
  702.  
  703. assert_nothing_raised { Person.schema = new_schema }
  704. - assert_equal new_schema_syms, Person.schema
  705. + new_schema_syms.each do |col|
  706. + assert Person.new.respond_to?(col.to_s), "should respond to the schema's string key, but failed on: #{col.to_s}"
  707. + assert Person.new.respond_to?(col.to_sym), "should respond to the schema's symbol key, but failed on: #{col.to_sym}"
  708. + end
  709. end
  710.  
  711.  
  712. - test "schema on a found model should return all the attributes of that model instance" do
  713. + test "schema on a fetched resource should return all the attributes of that model instance" do
  714. p = Person.find(1)
  715. s = p.schema
  716.  
  717. assert s.present?, "should have found a non-empty schema!"
  718.  
  719. p.attributes.each do |the_attr, val|
  720. - assert s.include?(the_attr), "should have found attr: #{the_attr} in schema, but only had: #{s.inspect}"
  721. + assert s.has_key?(the_attr), "should have found attr: #{the_attr} in schema, but only had: #{s.inspect}"
  722. end
  723. end
  724.  
  725. @@ -104,7 +126,7 @@ class SchemaTest < ActiveModel::TestCase
  726.  
  727. test "defining a schema should return it when asked" do
  728. assert Person.schema.blank?, "should have a blank class schema"
  729. - new_schema = [:name, :age, :height, :weight]
  730. + new_schema = {'name' => nil, 'age' => nil, 'height' => nil, 'weight' => nil}
  731. assert_nothing_raised {
  732. Person.schema = new_schema
  733. assert_equal new_schema, Person.schema, "should have saved the schema on the class"
  734. @@ -115,7 +137,7 @@ class SchemaTest < ActiveModel::TestCase
  735. test "defining a schema, then fetching a model should still match the defined schema" do
  736. # sanity checks
  737. assert Person.schema.blank?, "should have a blank class schema"
  738. - new_schema = [:name, :age, :height, :weight]
  739. + new_schema = {'name' => nil, 'age' => nil, 'height' => nil, 'weight' => nil}
  740.  
  741. matz = Person.find(1)
  742. assert !matz.schema.blank?, "should have some sort of schema on an instance variable"
  743. @@ -127,14 +149,14 @@ class SchemaTest < ActiveModel::TestCase
  744. }
  745. end
  746.  
  747. - test "should respond positively to attributes only in a schema" do
  748. + test "should respond positively to attributes that are only in the schema" do
  749. new_attr_name = :my_new_schema_attribute
  750. assert Person.schema.blank?, "sanity check - should have a blank class schema"
  751.  
  752. assert !Person.new.respond_do?(new_attr_name), "sanity check - should not respond to the brand-new attribute yet"
  753.  
  754. assert_nothing_raised {
  755. - Person.schema = [new_attr_name]
  756. + Person.schema = {new_attr_name.to_s => nil}
  757. assert Person.new.respond_to?(new_attr_name), "should respond to the schema's methods, but failed on: #{new_attr_name}"
  758. }
  759. end
  760. @@ -148,10 +170,73 @@ class SchemaTest < ActiveModel::TestCase
  761. Person.new.send(new_attr_name)
  762. end
  763.  
  764. - Person.schema = [new_attr_name]
  765. + Person.schema = {new_attr_name.to_s => nil}
  766. assert_nothing_raised do
  767. Person.new.send(new_attr_name)
  768. end
  769. end
  770.  
  771. +
  772. + #####################################################
  773. + # Known attributes
  774. + #
  775. + # Attributes can be known even if they aren't actually 'set' on a
  776. + # particular instance.
  777. + # This will only differ from 'attributes' if a schema has been set.
  778. +
  779. + test "new model should have no known attributes" do
  780. + assert Person.known_attributes.blank?, "should have no known attributes"
  781. + assert Person.new.known_attributes.blank?, "should have no known attributes on a new instance"
  782. + end
  783. +
  784. + test "setting schema should set known attributes on class and instance" do
  785. + new_schema = {'age' => nil, 'name' => nil}
  786. +
  787. + assert_nothing_raised { Person.schema = new_schema }
  788. +
  789. + assert_equal new_schema.keys, Person.known_attributes
  790. + assert_equal new_schema.keys, Person.new.known_attributes
  791. + end
  792. +
  793. + test "known attributes on a fetched resource should return all the attributes of the instance" do
  794. + p = Person.find(1)
  795. + attrs = p.known_attributes
  796. +
  797. + assert attrs.present?, "should have found some attributes!"
  798. +
  799. + p.attributes.each do |the_attr, val|
  800. + assert attrs.include?(the_attr), "should have found attr: #{the_attr} in known attributes, but only had: #{attrs.inspect}"
  801. + end
  802. + end
  803. +
  804. + test "with two instances, known attributes should match the attributes of the individual instances - even if they differ" do
  805. + matz = Person.find(1)
  806. + rick = Person.find(5)
  807. +
  808. + m_attrs = matz.attributes.keys.sort
  809. + r_attrs = rick.attributes.keys.sort
  810. +
  811. + assert_not_equal m_attrs, r_attrs, "should have different attributes on each model"
  812. +
  813. + assert_not_equal matz.known_attributes, rick.known_attributes, "should have had different known attributes too"
  814. + end
  815. +
  816. + test "setting schema then fetching should add schema attributes to the intance attributes" do
  817. + # an attribute in common with fetched instance and one that isn't
  818. + new_schema = {'name' => nil, 'my_strange_attribute' => nil}
  819. +
  820. + assert_nothing_raised { Person.schema = new_schema }
  821. +
  822. + matz = Person.find(1)
  823. + known_attrs = matz.known_attributes
  824. +
  825. + matz.attributes.keys.each do |the_attr|
  826. + assert known_attrs.include?(the_attr), "should have found instance attr: #{the_attr} in known attributes, but only had: #{known_attrs.inspect}"
  827. + end
  828. + new_schema.keys.each do |the_attr|
  829. + assert known_attrs.include?(the_attr), "should have found schema attr: #{the_attr} in known attributes, but only had: #{known_attrs.inspect}"
  830. + end
  831. + end
  832. +
  833. +
  834. end
  835. --
  836. 1.5.6.3
  837.  
  838.  
  839. From ee5da75e80abeaa26ca1267f08d2d4f5ce30edbf Mon Sep 17 00:00:00 2001
  840. From: taryneast <rubygirl@taryneast.org>
  841. Date: Tue, 8 Dec 2009 21:52:36 +0000
  842. Subject: [PATCH] Clear the schema/known_attrs only if passed nil
  843.  
  844. I'll be adding in more argument-checking shortly. I don't want to clear the
  845. schema only to find that the given new schema is wrong. So I'll enclose the
  846. schema-nullification section and only do it if we've specificaly requested
  847. it.
  848. ---
  849. activeresource/lib/active_resource/base.rb | 8 +++++---
  850. 1 files changed, 5 insertions(+), 3 deletions(-)
  851.  
  852. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  853. index 3585f9d..2c47849 100644
  854. --- a/activeresource/lib/active_resource/base.rb
  855. +++ b/activeresource/lib/active_resource/base.rb
  856. @@ -293,9 +293,11 @@ module ActiveResource
  857. # p.name # => nil
  858. #
  859. def schema=(the_schema)
  860. - @schema = nil
  861. - @known_attributes = []
  862. - return unless the_schema.present?
  863. + unless the_schema.present?
  864. + @schema = nil
  865. + @known_attributes = []
  866. + return
  867. + end
  868.  
  869. raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash
  870.  
  871. --
  872. 1.5.6.3
  873.  
  874.  
  875. From 3d4e69210ff7adca7bfa861bf211b7e24d0b26fe Mon Sep 17 00:00:00 2001
  876. From: taryneast <rubygirl@taryneast.org>
  877. Date: Fri, 11 Dec 2009 20:30:22 +0000
  878. Subject: [PATCH] Updates to coments
  879.  
  880. Better explanation for the current state of the schema - how to use it, and
  881. where it's going shortly.
  882. ---
  883. activeresource/lib/active_resource/base.rb | 76 ++++++++++++------------
  884. activeresource/test/cases/base/schema_test.rb | 6 +-
  885. 2 files changed, 41 insertions(+), 41 deletions(-)
  886.  
  887. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  888. index 2c47849..73a0ecb 100644
  889. --- a/activeresource/lib/active_resource/base.rb
  890. +++ b/activeresource/lib/active_resource/base.rb
  891. @@ -241,56 +241,51 @@ module ActiveResource
  892. cattr_accessor :logger
  893.  
  894. class << self
  895. - # Schema defines the known attributes of the current resource.
  896. - #
  897. - # The schema we pass into an Active Resource is similar to an Active
  898. - # Record database migration. It defines the known attributes of the
  899. - # Resource - even if there are currently no values for those
  900. - # attributes. It is like telling Active Resource that you know there
  901. - # will be attributes there someday.
  902. - #
  903. - # There is no need to specify a schema for your Active Resource. If
  904. - # you do not, the schema will be guessed from the instance attributes
  905. - # returned when the instance is fetched from the remote system (as
  906. - # before). The guessed schema is transient and it is possible that
  907. - # each instance will have a different 'schema' of this sort.
  908. - #
  909. - # The benefit of knowing the schema in advance is that your instance
  910. - # method will know that they can <tt>respond_to></tt> the known
  911. - # attributes and will return 'nil' when questioned (instead of
  912. - # MethodNotFound). This is helpful if you wish to have validations on
  913. - # those attributes (eg <tt>validates_presence_of</tt>)
  914. - #
  915. - #
  916. - # This section is very much under construction. More documentaion will
  917. - # be added as the functionality is expanded.
  918. - #
  919. - #
  920. - # For the moment, all you can do is pass a hash of known
  921. - # attribute-names with any values and it will be saved on the
  922. - # Resource. See <tt>schema=</tt> for an example of how this works.
  923. - #
  924. - # The attribute-names are also saved into the Resource's
  925. - # <tt>known_attributes</tt>
  926. - #
  927. - # In future, the Resource will be far more responsive eg will cast the
  928. - # attribute's value saved to the appropriate type - just as Active
  929. - # Record currently does.
  930. - def schema
  931. + # This will shortly disappear to be replaced by the migration-style
  932. + # usage of this for defining a schema. At that point, all the doc
  933. + # currenlty on <tt>schema=</tt> will move back here...
  934. + def schema # :nodoc:
  935. @schema ||= nil
  936. end
  937.  
  938. # Saves a schema to this resource - setting the attributes that are
  939. # known prior to fetching an instance from the remote system.
  940. #
  941. + # The schema helps define the set of <tt>known_attributes</tt> of the
  942. + # current resource.
  943. + #
  944. + # There is no need to specify a schema for your Active Resource. If
  945. + # you do not, the <tt>known_attributes</tt> will be guessed from the
  946. + # instance attributes returned when an instance is fetched from the
  947. + # remote system.
  948. + #
  949. # example:
  950. # class Person < ActiveResource::Base
  951. - # schema = {:name => :string, :age => :integer,
  952. - # :height => :float, :weight => :float }
  953. + # schema = {:name => :string, :age => :integer }
  954. # end
  955. # p = Person.new
  956. # p.respond_to? :name # => true
  957. + # p.respond_to? :age # => true
  958. # p.name # => nil
  959. + # p.age # => nil
  960. + #
  961. + # j = Person.find_by_name('John') # <person><name>John</name><age>34</age><weight>65</weight></person>
  962. + # j.respond_to? :name # => true
  963. + # j.respond_to? :age # => true
  964. + # j.name # => 'John'
  965. + # j.age # => '34' # note this is a string!
  966. + # j.weight # => '65' # note this is a string!
  967. + #
  968. + # p.weight # => MethodNotFound
  969. + #
  970. + # The schema must be a hash with the key being the attribute's name,
  971. + # and the value being the attribute type.
  972. + #
  973. + # Note: at present the value doesn't do anything, but stay tuned...
  974. + # Shortly it will also *cast* the value of the returned attribute.
  975. + # ie:
  976. + # j.age # => 34 # cast to an integer
  977. + # j.weight # => '65' # still a string!
  978. #
  979. def schema=(the_schema)
  980. unless the_schema.present?
  981. @@ -307,6 +302,11 @@ module ActiveResource
  982.  
  983. # Returns the list of known attributes for this resource, gathered
  984. # from the provided <tt>schema</tt>
  985. + # Attributes that are known will cause your resource to return 'true'
  986. + # when <tt>respond_to?</tt> is called on them. A known attribute will
  987. + # return nil if not set (rather than <t>MethodNotFound</tt>); thus
  988. + # known attributes can be used with <tt>validates_presence_of</tt>
  989. + # without a getter-method.
  990. def known_attributes
  991. @known_attributes ||= []
  992. end
  993. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  994. index a8e9bc8..5374ce0 100644
  995. --- a/activeresource/test/cases/base/schema_test.rb
  996. +++ b/activeresource/test/cases/base/schema_test.rb
  997. @@ -180,9 +180,9 @@ class SchemaTest < ActiveModel::TestCase
  998. #####################################################
  999. # Known attributes
  1000. #
  1001. - # Attributes can be known even if they aren't actually 'set' on a
  1002. - # particular instance.
  1003. - # This will only differ from 'attributes' if a schema has been set.
  1004. + # Attributes can be known to be attributes even if they aren't actually
  1005. + # 'set' on a particular instance.
  1006. + # This will only differ from 'attributes' if a schema has been set.
  1007.  
  1008. test "new model should have no known attributes" do
  1009. assert Person.known_attributes.blank?, "should have no known attributes"
  1010. --
  1011. 1.5.6.3
  1012.  
  1013.  
  1014. From c3e811746f52a0a47eda51aae6864d1809448e02 Mon Sep 17 00:00:00 2001
  1015. From: taryneast <rubygirl@taryneast.org>
  1016. Date: Fri, 11 Dec 2009 21:33:17 +0000
  1017. Subject: [PATCH] schema values must be part of known set.
  1018.  
  1019. I've added a limited set of known values for schema attributes.
  1020. Anything that doesn't match that set is rejected.
  1021.  
  1022. Currently I'm starting with [:string, :integer, :float] because those will
  1023. be easy to typecast-to, which is my next step.
  1024. ---
  1025. activeresource/lib/active_resource/base.rb | 25 +++++++++++++++++++--
  1026. activeresource/test/cases/base/schema_test.rb | 28 +++++++++++++++++++++++-
  1027. 2 files changed, 48 insertions(+), 5 deletions(-)
  1028.  
  1029. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  1030. index 73a0ecb..9d3e8b6 100644
  1031. --- a/activeresource/lib/active_resource/base.rb
  1032. +++ b/activeresource/lib/active_resource/base.rb
  1033. @@ -248,6 +248,10 @@ module ActiveResource
  1034. @schema ||= nil
  1035. end
  1036.  
  1037. + # attributes can be known to be one of these types. They are easy to
  1038. + # cast to/from.
  1039. + KNOWN_ATTRIBUTE_TYPES = [:string, :integer, :float]
  1040. +
  1041. # Saves a schema to this resource - setting the attributes that are
  1042. # known prior to fetching an instance from the remote system.
  1043. #
  1044. @@ -261,7 +265,7 @@ module ActiveResource
  1045. #
  1046. # example:
  1047. # class Person < ActiveResource::Base
  1048. - # schema = {:name => :string, :age => :integer }
  1049. + # schema = {'name' => :string, 'age' => :integer }
  1050. # end
  1051. # p = Person.new
  1052. # p.respond_to? :name # => true
  1053. @@ -278,8 +282,19 @@ module ActiveResource
  1054. #
  1055. # p.weight # => MethodNotFound
  1056. #
  1057. - # The schema must be a hash with the key being the attribute's name,
  1058. - # and the value being the attribute type.
  1059. + # The schema must be a hash with the *string* key being the
  1060. + # attribute's name, and the value being the attribute type.
  1061. + #
  1062. + # Attribute-types must be one of:
  1063. + # :string, :integer, :float
  1064. + #
  1065. + # Attributes will be typecast into the type you give it (the original
  1066. + # will still be available through <tt>attribute_before_typecast(:the_attr_name)</tt>
  1067. + #
  1068. + # If an attribute does not have a type, it will not be typecast
  1069. + #
  1070. + # Note: date/times aren't yet supported... the best option is to leave
  1071. + # them as strings and cast them appropriately in your own code.
  1072. #
  1073. # Note: at present the value doesn't do anything, but stay tuned...
  1074. # Shortly it will also *cast* the value of the returned attribute.
  1075. @@ -296,6 +311,10 @@ module ActiveResource
  1076.  
  1077. raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash
  1078.  
  1079. + the_schema.each do |k,v|
  1080. + raise ArgumentError, "Unknown attribute type: #{v.inspect} for key: #{k.inspect}" unless v.nil? || KNOWN_ATTRIBUTE_TYPES.include?(v.to_sym)
  1081. + end
  1082. +
  1083. @schema = the_schema.with_indifferent_access
  1084. @known_attributes = the_schema.keys.map(&:to_s)
  1085. end
  1086. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  1087. index 5374ce0..7c5066c 100644
  1088. --- a/activeresource/test/cases/base/schema_test.rb
  1089. +++ b/activeresource/test/cases/base/schema_test.rb
  1090. @@ -64,8 +64,8 @@ class SchemaTest < ActiveModel::TestCase
  1091. end
  1092.  
  1093. test "schema should only accept a hash" do
  1094. - ["blahblah",['one','two'], [:age, :name]].each do |bad_schema|
  1095. - assert_raises(ArgumentError,"should only accept a hash, but accepted: #{bad_schema.inspect}") do
  1096. + ["blahblah", ['one','two'], [:age, :name], Person.new].each do |bad_schema|
  1097. + assert_raises(ArgumentError,"should only accept a hash (or nil), but accepted: #{bad_schema.inspect}") do
  1098. Person.schema = bad_schema
  1099. end
  1100. end
  1101. @@ -78,6 +78,30 @@ class SchemaTest < ActiveModel::TestCase
  1102. assert_equal new_schema, Person.schema
  1103. end
  1104.  
  1105. + test "schema should accept a hash with simple values" do
  1106. + new_schema = {'age' => :integer, 'name' => :string, 'height' => :float, 'mydatetime' => nil}
  1107. +
  1108. + assert_nothing_raised { Person.schema = new_schema }
  1109. + assert_equal new_schema, Person.schema
  1110. + end
  1111. +
  1112. + test "schema should accept all known attribute types as values" do
  1113. + # I'd prefer to use ActiveResource::KNOWN_ATTRIBUTE_TYPES here...
  1114. + [:string, :integer, :float].each do |the_type|
  1115. + assert_nothing_raised("should have accepted #{the_type.inspect}"){ Person.schema = {'my_key' => the_type }}
  1116. + end
  1117. + end
  1118. +
  1119. + test "schema should not accept unknown values" do
  1120. + bad_values = [ :oogle, :blob, 'thing']
  1121. +
  1122. + bad_values.each do |bad_value|
  1123. + assert_raises(ArgumentError,"should only accept a known attribute type, but accepted: #{bad_value.inspect}") do
  1124. + Person.schema = {'key' => bad_value}
  1125. + end
  1126. + end
  1127. + end
  1128. +
  1129. test "schema should accept nil and remove the schema" do
  1130. new_schema = {'age' => nil, 'name' => nil}
  1131. assert_nothing_raised { Person.schema = new_schema }
  1132. --
  1133. 1.5.6.3
  1134.  
  1135.  
  1136. From c83d9b5e379bea4234d82efa1797a316ee494f61 Mon Sep 17 00:00:00 2001
  1137. From: taryneast <rubygirl@taryneast.org>
  1138. Date: Sat, 12 Dec 2009 12:36:33 +0000
  1139. Subject: [PATCH] First attempt at Migration-style SchemaDefinition
  1140.  
  1141. Very basic and probably broken. It's also pre-test-building.
  1142. I'll get it working and matched into tests next.
  1143. ---
  1144. activeresource/lib/active_resource/base.rb | 5 ++
  1145. .../lib/active_resource/schema_definition.rb | 41 ++++++++++++++++++++
  1146. 2 files changed, 46 insertions(+), 0 deletions(-)
  1147. create mode 100644 activeresource/lib/active_resource/schema_definition.rb
  1148.  
  1149. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  1150. index 9d3e8b6..dac641e 100644
  1151. --- a/activeresource/lib/active_resource/base.rb
  1152. +++ b/activeresource/lib/active_resource/base.rb
  1153. @@ -247,6 +247,11 @@ module ActiveResource
  1154. def schema # :nodoc:
  1155. @schema ||= nil
  1156. end
  1157. + # def schema
  1158. + # schema_definition = SchemaDefinition.new
  1159. + # yield schema_definition if block_given?
  1160. + # # do stuff with what's int he schema def attrs
  1161. + # end
  1162.  
  1163. # attributes can be known to be one of these types. They are easy to
  1164. # cast to/from.
  1165. diff --git a/activeresource/lib/active_resource/schema_definition.rb b/activeresource/lib/active_resource/schema_definition.rb
  1166. new file mode 100644
  1167. index 0000000..0a635c2
  1168. --- /dev/null
  1169. +++ b/activeresource/lib/active_resource/schema_definition.rb
  1170. @@ -0,0 +1,41 @@
  1171. +require 'active_resource/exceptions'
  1172. +
  1173. +module ActiveResource
  1174. + class SchemaDefinition
  1175. + # An array of attribute definitions, representing the attributes that
  1176. + # have been defined.
  1177. + attr_accessor :attrs
  1178. +
  1179. + def initialize(base)
  1180. + @attrs = []
  1181. + end
  1182. +
  1183. + # Returns an attribute definition for the attribute with name +name+.
  1184. + def [](name)
  1185. + @attrs[name.to_s]
  1186. + end
  1187. +
  1188. + def attribute(name, type, options = {})
  1189. + the_attr = [type.to_s]
  1190. + the_attr << options[:default] if options.hs_key? :default
  1191. + @attrs[name.to_s] = the_attr
  1192. + self
  1193. + end
  1194. +
  1195. + # The following are he attribute types supported by Active Record
  1196. + # migra5tions. We should eventually support all of these
  1197. + # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type|
  1198. + %w( string integer float ).each do |attr_type|
  1199. + class_eval <<-EOV
  1200. + def #{attr_type}(*args) # def string(*args)
  1201. + options = args.extract_options! # options = args.extract_options!
  1202. + attr_names = args # attr_names = args
  1203. + #
  1204. + attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) }
  1205. + end # end
  1206. + EOV
  1207. + end
  1208. +
  1209. +
  1210. + end
  1211. +end
  1212. --
  1213. 1.5.6.3
  1214.  
  1215.  
  1216. From eff61e3ef3aca5ec37556f13f3b5cc7872f27b66 Mon Sep 17 00:00:00 2001
  1217. From: taryneast <rubygirl@taryneast.org>
  1218. Date: Mon, 14 Dec 2009 18:54:09 +0000
  1219. Subject: [PATCH] Just comments in tests.
  1220.  
  1221. To split the tests into logical blocks of things we're testing for - and
  1222. make it easy for me to find them again.
  1223. ---
  1224. activeresource/test/cases/base/schema_test.rb | 14 +++++++++++++-
  1225. 1 files changed, 13 insertions(+), 1 deletions(-)
  1226.  
  1227. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  1228. index 7c5066c..c8b9864 100644
  1229. --- a/activeresource/test/cases/base/schema_test.rb
  1230. +++ b/activeresource/test/cases/base/schema_test.rb
  1231. @@ -58,6 +58,11 @@ class SchemaTest < ActiveModel::TestCase
  1232. Person.schema = nil # hack to stop test bleedthrough...
  1233. end
  1234.  
  1235. +
  1236. + #####################################################
  1237. + # Passing in a schema directly and returning it
  1238. + ####
  1239. +
  1240. test "schema on a new model should be empty" do
  1241. assert Person.schema.blank?, "should have a blank class schema"
  1242. assert Person.new.schema.blank?, "should have a blank instance schema"
  1243. @@ -173,6 +178,13 @@ class SchemaTest < ActiveModel::TestCase
  1244. }
  1245. end
  1246.  
  1247. +
  1248. + #####################################################
  1249. + # What a schema does for us
  1250. + ####
  1251. +
  1252. + # respond_to? and method_missing effects
  1253. +
  1254. test "should respond positively to attributes that are only in the schema" do
  1255. new_attr_name = :my_new_schema_attribute
  1256. assert Person.schema.blank?, "sanity check - should have a blank class schema"
  1257. @@ -201,7 +213,7 @@ class SchemaTest < ActiveModel::TestCase
  1258. end
  1259.  
  1260.  
  1261. - #####################################################
  1262. + ########
  1263. # Known attributes
  1264. #
  1265. # Attributes can be known to be attributes even if they aren't actually
  1266. --
  1267. 1.5.6.3
  1268.  
  1269.  
  1270. From 9334d74f6ea491ca2f28e01614b748ebb73d84b4 Mon Sep 17 00:00:00 2001
  1271. From: taryneast <rubygirl@taryneast.org>
  1272. Date: Mon, 14 Dec 2009 19:46:27 +0000
  1273. Subject: [PATCH] The basics of ActiveResource.define_schema syntax
  1274.  
  1275. For now it is very simple. The SchemaDefinition roughly follows the lines of
  1276. an ActiveRecord TableDefinition from migrations.
  1277. At present all it does is store a set of attributes onto itself.
  1278. ---
  1279. activeresource/lib/active_resource/base.rb | 12 +++--
  1280. .../lib/active_resource/schema_definition.rb | 46 +++++++++-----------
  1281. activeresource/test/cases/base/schema_test.rb | 44 +++++++++++++++++++
  1282. 3 files changed, 72 insertions(+), 30 deletions(-)
  1283.  
  1284. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  1285. index dac641e..fee6a53 100644
  1286. --- a/activeresource/lib/active_resource/base.rb
  1287. +++ b/activeresource/lib/active_resource/base.rb
  1288. @@ -13,6 +13,7 @@ require 'set'
  1289. require 'uri'
  1290.  
  1291. require 'active_resource/exceptions'
  1292. +require 'active_resource/schema_definition'
  1293.  
  1294. module ActiveResource
  1295. # ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application.
  1296. @@ -247,11 +248,12 @@ module ActiveResource
  1297. def schema # :nodoc:
  1298. @schema ||= nil
  1299. end
  1300. - # def schema
  1301. - # schema_definition = SchemaDefinition.new
  1302. - # yield schema_definition if block_given?
  1303. - # # do stuff with what's int he schema def attrs
  1304. - # end
  1305. +
  1306. + def define_schema
  1307. + schema_definition = SchemaDefinition.new
  1308. + yield schema_definition if block_given?
  1309. + # do stuff with what's in the schema def attrs
  1310. + end
  1311.  
  1312. # attributes can be known to be one of these types. They are easy to
  1313. # cast to/from.
  1314. diff --git a/activeresource/lib/active_resource/schema_definition.rb b/activeresource/lib/active_resource/schema_definition.rb
  1315. index 0a635c2..0f1cfd6 100644
  1316. --- a/activeresource/lib/active_resource/schema_definition.rb
  1317. +++ b/activeresource/lib/active_resource/schema_definition.rb
  1318. @@ -1,41 +1,37 @@
  1319. require 'active_resource/exceptions'
  1320.  
  1321. -module ActiveResource
  1322. - class SchemaDefinition
  1323. +module ActiveResource # :nodoc:
  1324. + class SchemaDefinition # :nodoc:
  1325. +
  1326. # An array of attribute definitions, representing the attributes that
  1327. # have been defined.
  1328. attr_accessor :attrs
  1329.  
  1330. - def initialize(base)
  1331. - @attrs = []
  1332. - end
  1333. -
  1334. - # Returns an attribute definition for the attribute with name +name+.
  1335. - def [](name)
  1336. - @attrs[name.to_s]
  1337. + # The internals of an Active Resource SchemaDefinition are very simple -
  1338. + # unlike an Active Record TableDefinition (on which it is based).
  1339. + # It provides a set of convenience methods for people to define their
  1340. + # schema using the syntax:
  1341. + # define_schema do |s|
  1342. + # s.string :foo
  1343. + # s.integer :bar
  1344. + # end
  1345. + #
  1346. + # The schema stores the name and type of each attribute. That is then
  1347. + # read out by the define_schema method to populate the actual
  1348. + # Resource's schema
  1349. + def initialize
  1350. + @attrs = {}
  1351. end
  1352.  
  1353. def attribute(name, type, options = {})
  1354. - the_attr = [type.to_s]
  1355. - the_attr << options[:default] if options.hs_key? :default
  1356. + the_attr = type.to_s
  1357. + # TODO: add defaults
  1358. + #the_attr = [type.to_s]
  1359. + #the_attr << options[:default] if options.has_key? :default
  1360. @attrs[name.to_s] = the_attr
  1361. self
  1362. end
  1363.  
  1364. - # The following are he attribute types supported by Active Record
  1365. - # migra5tions. We should eventually support all of these
  1366. - # %w( string text integer float decimal datetime timestamp time date binary boolean ).each do |attr_type|
  1367. - %w( string integer float ).each do |attr_type|
  1368. - class_eval <<-EOV
  1369. - def #{attr_type}(*args) # def string(*args)
  1370. - options = args.extract_options! # options = args.extract_options!
  1371. - attr_names = args # attr_names = args
  1372. - #
  1373. - attr_names.each { |name| attribute(name, '#{attr_type}', options) } # attr_names.each { |name| attribute(name, 'string', options) }
  1374. - end # end
  1375. - EOV
  1376. - end
  1377. -
  1378.  
  1379. end
  1380. end
  1381. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  1382. index c8b9864..6e659fb 100644
  1383. --- a/activeresource/test/cases/base/schema_test.rb
  1384. +++ b/activeresource/test/cases/base/schema_test.rb
  1385. @@ -177,7 +177,51 @@ class SchemaTest < ActiveModel::TestCase
  1386. assert_equal new_schema, matz.schema, "class-level schema should override instance-level schema"
  1387. }
  1388. end
  1389. +
  1390. +
  1391. + #####################################################
  1392. + # Using the define_schema syntax
  1393. + ####
  1394. +
  1395. + test "should be able to use define_schema" do
  1396. + assert Person.respond_to?(:define_schema), "should at least respond to the define_schema method"
  1397. +
  1398. + assert_nothing_raised("Should allow the define_schema to take a block") do
  1399. + Person.define_schema do |s|
  1400. + assert s.kind_of?(ActiveResource::SchemaDefinition), "the 's' should be a schema definition or we're way off track..."
  1401. + end
  1402. + end
  1403. + end
  1404. +
  1405. + test "should be able to add attributes through define_schema" do
  1406. + assert_nothing_raised do
  1407. + Person.define_schema do |s|
  1408. + assert s.attribute('foo', 'string'), "should take a simple attribute"
  1409. + assert s.attrs.has_key?('foo'), "should have saved the attribute name"
  1410. + assert_equal 'string', s.attrs['foo'], "should have saved the attribute type"
  1411. + end
  1412. + end
  1413. + end
  1414.  
  1415. + test "should convert symbol attributes to strings" do
  1416. + assert_nothing_raised do
  1417. + Person.define_schema do |s|
  1418. + assert s.attribute(:foo, :integer), "should take a simple attribute as symbols"
  1419. + assert s.attrs.has_key?('foo'), "should have saved the attribute name as a string"
  1420. + assert_equal 'integer', s.attrs['foo'], "should have saved the attribute type as a string"
  1421. + end
  1422. + end
  1423. + end
  1424. +
  1425. + test "schema definition should store and return attributes" do
  1426. + assert_nothing_raised do
  1427. + Person.define_schema do |s|
  1428. + assert s.respond_to?(:attrs), "should return attributes in theory"
  1429. + s.attribute :foo, :string
  1430. + assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice")
  1431. + end
  1432. + end
  1433. + end
  1434.  
  1435. #####################################################
  1436. # What a schema does for us
  1437. --
  1438. 1.5.6.3
  1439.  
  1440.  
  1441. From db54df24f33664da0ba17fff5d5d30e4c87f69a9 Mon Sep 17 00:00:00 2001
  1442. From: taryneast <rubygirl@taryneast.org>
  1443. Date: Mon, 14 Dec 2009 19:54:46 +0000
  1444. Subject: [PATCH] Moved 'known_atribute_types' into SchemaDefinition
  1445.  
  1446. where they really belong.
  1447. ---
  1448. activeresource/lib/active_resource/base.rb | 5 +----
  1449. .../lib/active_resource/schema_definition.rb | 5 ++++-
  1450. activeresource/test/cases/base/schema_test.rb | 4 ++--
  1451. 3 files changed, 7 insertions(+), 7 deletions(-)
  1452.  
  1453. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  1454. index fee6a53..c713889 100644
  1455. --- a/activeresource/lib/active_resource/base.rb
  1456. +++ b/activeresource/lib/active_resource/base.rb
  1457. @@ -255,9 +255,6 @@ module ActiveResource
  1458. # do stuff with what's in the schema def attrs
  1459. end
  1460.  
  1461. - # attributes can be known to be one of these types. They are easy to
  1462. - # cast to/from.
  1463. - KNOWN_ATTRIBUTE_TYPES = [:string, :integer, :float]
  1464.  
  1465. # Saves a schema to this resource - setting the attributes that are
  1466. # known prior to fetching an instance from the remote system.
  1467. @@ -319,7 +316,7 @@ module ActiveResource
  1468. raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash
  1469.  
  1470. the_schema.each do |k,v|
  1471. - raise ArgumentError, "Unknown attribute type: #{v.inspect} for key: #{k.inspect}" unless v.nil? || KNOWN_ATTRIBUTE_TYPES.include?(v.to_sym)
  1472. + raise ArgumentError, "Unknown attribute type: #{v.inspect} for key: #{k.inspect}" unless v.nil? || SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.include?(v.to_sym)
  1473. end
  1474.  
  1475. @schema = the_schema.with_indifferent_access
  1476. diff --git a/activeresource/lib/active_resource/schema_definition.rb b/activeresource/lib/active_resource/schema_definition.rb
  1477. index 0f1cfd6..e53bb11 100644
  1478. --- a/activeresource/lib/active_resource/schema_definition.rb
  1479. +++ b/activeresource/lib/active_resource/schema_definition.rb
  1480. @@ -3,6 +3,10 @@ require 'active_resource/exceptions'
  1481. module ActiveResource # :nodoc:
  1482. class SchemaDefinition # :nodoc:
  1483.  
  1484. + # attributes can be known to be one of these types. They are easy to
  1485. + # cast to/from.
  1486. + KNOWN_ATTRIBUTE_TYPES = [:string, :integer, :float]
  1487. +
  1488. # An array of attribute definitions, representing the attributes that
  1489. # have been defined.
  1490. attr_accessor :attrs
  1491. @@ -32,6 +36,5 @@ module ActiveResource # :nodoc:
  1492. self
  1493. end
  1494.  
  1495. -
  1496. end
  1497. end
  1498. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  1499. index 6e659fb..b359a4a 100644
  1500. --- a/activeresource/test/cases/base/schema_test.rb
  1501. +++ b/activeresource/test/cases/base/schema_test.rb
  1502. @@ -91,8 +91,8 @@ class SchemaTest < ActiveModel::TestCase
  1503. end
  1504.  
  1505. test "schema should accept all known attribute types as values" do
  1506. - # I'd prefer to use ActiveResource::KNOWN_ATTRIBUTE_TYPES here...
  1507. - [:string, :integer, :float].each do |the_type|
  1508. + # I'd prefer to use here...
  1509. + ActiveResource::SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.each do |the_type|
  1510. assert_nothing_raised("should have accepted #{the_type.inspect}"){ Person.schema = {'my_key' => the_type }}
  1511. end
  1512. end
  1513. --
  1514. 1.5.6.3
  1515.  
  1516.  
  1517. From 9cbb6529d0a7054c54bac1c980f6d0dc53e68ebc Mon Sep 17 00:00:00 2001
  1518. From: taryneast <rubygirl@taryneast.org>
  1519. Date: Mon, 14 Dec 2009 19:59:37 +0000
  1520. Subject: [PATCH] Test for all attr types and stringify them
  1521.  
  1522. Firstly added a test that checks we can add all of the known attribute
  1523. types.
  1524. Secondly - stringified the set of known types - as this seems to be the
  1525. running theme for Active Resource.
  1526. ---
  1527. activeresource/lib/active_resource/base.rb | 2 +-
  1528. .../lib/active_resource/schema_definition.rb | 2 +-
  1529. activeresource/test/cases/base/schema_test.rb | 28 ++++++++++++++-----
  1530. 3 files changed, 22 insertions(+), 10 deletions(-)
  1531.  
  1532. diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
  1533. index c713889..85966ce 100644
  1534. --- a/activeresource/lib/active_resource/base.rb
  1535. +++ b/activeresource/lib/active_resource/base.rb
  1536. @@ -316,7 +316,7 @@ module ActiveResource
  1537. raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash
  1538.  
  1539. the_schema.each do |k,v|
  1540. - raise ArgumentError, "Unknown attribute type: #{v.inspect} for key: #{k.inspect}" unless v.nil? || SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.include?(v.to_sym)
  1541. + raise ArgumentError, "Unknown attribute type: #{v.inspect} for key: #{k.inspect}" unless v.nil? || SchemaDefinition::KNOWN_ATTRIBUTE_TYPES.include?(v.to_s)
  1542. end
  1543.  
  1544. @schema = the_schema.with_indifferent_access
  1545. diff --git a/activeresource/lib/active_resource/schema_definition.rb b/activeresource/lib/active_resource/schema_definition.rb
  1546. index e53bb11..6358909 100644
  1547. --- a/activeresource/lib/active_resource/schema_definition.rb
  1548. +++ b/activeresource/lib/active_resource/schema_definition.rb
  1549. @@ -5,7 +5,7 @@ module ActiveResource # :nodoc:
  1550.  
  1551. # attributes can be known to be one of these types. They are easy to
  1552. # cast to/from.
  1553. - KNOWN_ATTRIBUTE_TYPES = [:string, :integer, :float]
  1554. + KNOWN_ATTRIBUTE_TYPES = %w( string integer float )
  1555.  
  1556. # An array of attribute definitions, representing the attributes that
  1557. # have been defined.
  1558. diff --git a/activeresource/test/cases/base/schema_test.rb b/activeresource/test/cases/base/schema_test.rb
  1559. index b359a4a..5ce34d1 100644
  1560. --- a/activeresource/test/cases/base/schema_test.rb
  1561. +++ b/activeresource/test/cases/base/schema_test.rb
  1562. @@ -192,7 +192,17 @@ class SchemaTest < ActiveModel::TestCase
  1563. end
  1564. end
  1565. end
  1566. -
  1567. +
  1568. + test "schema definition should store and return attribute set" do
  1569. + assert_nothing_raised do
  1570. + Person.define_schema do |s|
  1571. + assert s.respond_to?(:attrs), "should return attributes in theory"
  1572. + s.attribute :foo, :string
  1573. + assert_equal({'foo' => 'string' }, s.attrs, "should return attributes in practice")
  1574. + end
  1575. + end
  1576. + end
  1577. +
  1578. test "should be able to add attributes through define_schema" do
  1579. assert_nothing_raised do
  1580. Person.define_schema do |s|
  1581. @@ -212,13 +222,15 @@ cla
Add Comment
Please, Sign In to add comment