restfulie 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -13,24 +13,18 @@ h2. Why would I use restfulie?
13
13
 
14
14
  # Easy --> writing hypermedia and semantic meaningful media type aware clients
15
15
  # Small -> it's not a bloated solution with a huge list of APIs
16
- # HATEOAS --> clients you are unaware of will not bother if you change your URIs
17
- # HATEOAS --> resources that you consume will not affect your software whenever they change part of their flow or URIs
16
+ # HATEOAS --> clients you are unaware of will not bother if you change
17
+ # HATEOAS --> consumed resources will not affect your software whenever they change their flow
18
18
  # Adaptability --> clients are able to adapt to your changes
19
19
 
20
- h2. Installing
21
-
22
- For client side usage, execute:
23
-
24
- <pre>
25
- gem install activesupport
26
- gem install restfulie
27
- </pre>
20
+ h2. Documentation
28
21
 
29
- For server side usage, execute:
22
+ Appart from the simple server and client examples provided here, you can find the following links useful:
30
23
 
31
- <pre>
32
- gem install restfulie
33
- </pre>
24
+ * "RDocs":http://rdoc.info/projects/caelum/restfulie
25
+ * "Official website":http://restfulie.caelumobjects.com
26
+ * "How-tos":http://restfulie.caelumobjects.com/rails
27
+ * "Buying through Rest: Rest to the enterprise (video)":guilhermesilveira.wordpress.com/2010/04/13/buying-through-rest-applying-rest-to-the-enterprise/
34
28
 
35
29
  h2. Simple server example
36
30
 
@@ -84,6 +78,21 @@ You can view an entire application running Restfulie under *spec/integration/ord
84
78
 
85
79
  "You can also download a full example of a REST based agent and server":http://github.com/caelum/mikyung using Restfulie and Mikyung, according to the Rest Architecture Maturity Model.
86
80
 
81
+ h2. Installing
82
+
83
+ For client side usage, execute:
84
+
85
+ <pre>
86
+ gem install activesupport
87
+ gem install restfulie
88
+ </pre>
89
+
90
+ For server side usage, execute:
91
+
92
+ <pre>
93
+ gem install restfulie
94
+ </pre>
95
+
87
96
  h2. Building the project
88
97
 
89
98
  If you want to build the project and run its tests, remember to install all (client and server) required gems and:
@@ -92,6 +101,7 @@ If you want to build the project and run its tests, remember to install all (cli
92
101
  gem install rack-conneg
93
102
  gem install responders_backport
94
103
  gem install json_pure
104
+ gem install sqlite3-ruby
95
105
  </pre>
96
106
 
97
107
  <script type="text/javascript">
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'spec/rake/spectask'
6
6
  require 'rake/rdoctask'
7
7
 
8
8
  GEM = "restfulie"
9
- GEM_VERSION = "0.7.1"
9
+ GEM_VERSION = "0.7.2"
10
10
  SUMMARY = "Hypermedia aware resource based library in ruby (client side) and ruby on rails (server side)."
11
11
  AUTHOR = "Guilherme Silveira, Caue Guerra"
12
12
  EMAIL = "guilherme.silveira@caelum.com.br"
@@ -19,11 +19,11 @@ class Restfulie::Common::Builder::Base
19
19
  undef_method :to_json if respond_to?(:to_json)
20
20
 
21
21
  def respond_to?(symbol, include_private = false)
22
- !marshalling_class(symbol).nil? || super
22
+ marshalling_class(symbol) || super
23
23
  end
24
24
 
25
25
  def method_missing(symbol, *args)
26
- unless (marshalling = marshalling_class(symbol)).nil?
26
+ unless (marshalling = marshalling_class!(symbol)).nil?
27
27
  return builder(marshalling, *args)
28
28
  end
29
29
  super
@@ -43,16 +43,31 @@ private
43
43
  marshalling.new(@object, rules_blocks).builder_collection(@options.merge(options))
44
44
  end
45
45
 
46
+ def self.marshalling_classes(media_type)
47
+ {"application/atom+xml" => Restfulie::Common::Builder::Marshalling::Atom,
48
+ "application/xml" => Restfulie::Common::Builder::Marshalling::Xml::Marshaller,
49
+ "application/json" => Restfulie::Common::Builder::Marshalling::Json,
50
+ "atom" => Restfulie::Common::Builder::Marshalling::Atom, # test only
51
+ "xml" => Restfulie::Common::Builder::Marshalling::Xml::Marshaller # test only
52
+ }[media_type.downcase]
53
+ end
54
+
46
55
  def marshalling_class(method)
56
+ if (marshalling_name = method.to_s.match(/to_(.*)/))
57
+ marshalling = marshalling_name[1].downcase
58
+ Restfulie::Common::Builder::Base.marshalling_classes(marshalling)
59
+ end
60
+ end
61
+
62
+ def marshalling_class!(method)
47
63
  if marshalling_name = method.to_s.match(/to_(.*)/)
48
- marshalling = marshalling_name[1].downcase.capitalize.to_sym
49
- if Restfulie::Common::Builder::Marshalling.const_defined?(marshalling)
50
- begin
51
- Restfulie::Common::Builder::Marshalling.const_get(marshalling)
52
- rescue NameError
53
- raise Restfulie::Common::Error::UndefinedMarshallingError.new("Marshalling #{marshalling} not found.")
54
- end
64
+ marshalling = marshalling_name[1].downcase
65
+ if (marshaller = Restfulie::Common::Builder::Base.marshalling_classes(marshalling))
66
+ marshaller
67
+ else
68
+ raise Restfulie::Common::Error::UndefinedMarshallingError.new("Marshalling #{marshalling} not found.")
55
69
  end
56
70
  end
57
71
  end
58
- end
72
+
73
+ end
@@ -189,7 +189,7 @@ private
189
189
  end
190
190
 
191
191
  def namespace_enhance(options)
192
- if !options[:namespace].nil? && options[:namespace].kind_of?(String)
192
+ if options[:namespace] && options[:namespace].kind_of?(String)
193
193
  options[:namespace] = { :uri => options[:namespace], :eager_load => true }
194
194
  end
195
195
  options
@@ -0,0 +1,183 @@
1
+ # support xml serialization for custom attributes
2
+ module Restfulie::Common::Builder::Rules::CustomAttributes
3
+
4
+ def to_xml(writer)
5
+ custom_attributes.each do |key, value|
6
+ writer.tag!(key, value)
7
+ end
8
+ end
9
+
10
+ end
11
+
12
+ module Restfulie::Common::Builder::Marshalling::Xml
13
+ end
14
+
15
+ # Xml collection rule answering to to_xml and allowing any custom element to be inserted
16
+ # All links will be automatically inserted.
17
+ class Restfulie::Common::Builder::Marshalling::Xml::CollectionRule < Restfulie::Common::Builder::CollectionRule
18
+
19
+ include Restfulie::Common::Builder::Rules::CustomAttributes
20
+
21
+ def to_xml(writer)
22
+ super(writer)
23
+ links.each do |link|
24
+ writer.link(:rel => link.rel, :href => link.href, :type => (link.type || 'application/xml')) if link.href
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ # Xml member rule answering to to_xml and allowing any custom element to be inserted.
31
+ # All links will be automatically inserted.
32
+ class Restfulie::Common::Builder::Marshalling::Xml::MemberRule < Restfulie::Common::Builder::MemberRule
33
+
34
+ include ActionController::UrlWriter
35
+ include Restfulie::Common::Builder::Rules::CustomAttributes
36
+
37
+ def to_xml(object, writer)
38
+ super(writer)
39
+ # Transitions
40
+ links.each do |link|
41
+ atom_link = {:rel => link.rel, :href => link.href, :type => link.type}
42
+
43
+ # Self
44
+ if link.href.nil?
45
+ if link.rel == "self"
46
+ path = object
47
+ else
48
+ association = object.class.reflect_on_all_associations.find { |a| a.name.to_s == link.rel }
49
+ path = (association.macro == :has_many) ? [object, association.name] : object.send(association.name) unless association.nil?
50
+ end
51
+ atom_link[:href] = polymorphic_url(path, :host => host) rescue nil
52
+ atom_link[:type] = link.type || 'application/xml'
53
+ end
54
+ writer.link(:rel => atom_link[:rel], :href => atom_link[:href], :type => (link.type || 'application/xml')) if atom_link[:href]
55
+
56
+ end
57
+ end
58
+
59
+ private
60
+ def host
61
+ # TODO: If we split restfulie into 2 separate gems, we may need not to use Restfulie::Server
62
+ # inside Restfulie::Common
63
+ Restfulie::Server::Configuration.host
64
+ end
65
+
66
+ end
67
+
68
+ class Restfulie::Common::Builder::Marshalling::Xml::Marshaller < Restfulie::Common::Builder::Marshalling::Base
69
+ include ActionController::UrlWriter
70
+ include Restfulie::Common::Builder::Helpers
71
+ include Restfulie::Common::Error
72
+
73
+ def initialize(object, rules)
74
+ @object = object
75
+ @rules = rules
76
+ end
77
+
78
+ def builder_collection(options = {})
79
+ builder_feed(@object, @rules, options)
80
+ end
81
+
82
+ def builder_member(options = {})
83
+ options[:indent] ||= 2
84
+ options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
85
+ options[:skip_types] = true
86
+ builder_entry(@object, options[:builder], @object.class.name.underscore, @rules, options)
87
+ end
88
+
89
+ private
90
+
91
+ def builder_feed(objects, rules_blocks, options = {})
92
+ rule = Restfulie::Common::Builder::Marshalling::Xml::CollectionRule.new(rules_blocks)
93
+
94
+ rule.blocks.unshift(default_collection_rule) if options[:default_rule]
95
+ rule.apply(objects, options)
96
+
97
+ # setup code from Rails to_xml
98
+
99
+ options[:root] ||= objects.all? { |e| e.is_a?(objects.first.class) && objects.first.class.to_s != "Hash" } ? objects.first.class.to_s.underscore.pluralize : "records"
100
+ options[:children] ||= options[:root].singularize
101
+ options[:indent] ||= 2
102
+ options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
103
+
104
+ root = options.delete(:root).to_s
105
+ children = options.delete(:children)
106
+
107
+ if !options.has_key?(:dasherize) || options[:dasherize]
108
+ root = root.dasherize
109
+ end
110
+
111
+ options[:builder].instruct! unless options.delete(:skip_instruct)
112
+
113
+ opts = options.merge({ :root => children })
114
+
115
+ writer = options[:builder]
116
+
117
+ # Entries
118
+ options.delete(:values)
119
+ member_options = options.merge(rule.members_options || {})
120
+ member_options[:skip_instruct] = true
121
+ start_with_namespace(rule.namespaces, writer, root, options[:skip_types] ? {} : {}) do
122
+ rule.to_xml(writer)
123
+ yield writer if block_given?
124
+
125
+ objects.each { |e|
126
+ builder_entry(e, writer, children, rule.members_blocks || [], member_options)
127
+ }
128
+ end
129
+
130
+ end
131
+
132
+ def start_with_namespace(namespaces, writer, root, condition, &block)
133
+ spaces = condition.dup
134
+ namespaces.each do |ns|
135
+ ns.each do |key, value|
136
+ spaces["xmlns:#{ns.namespace}"] = ns.uri
137
+ end if ns.uri
138
+ end
139
+ result = writer.tag!(root, spaces) do |inner|
140
+ namespaces.each do |ns|
141
+ ns.each do |key, value|
142
+ tag = ns.namespace ? "#{key}" : "#{ns.namespace}:#{key}"
143
+ inner.tag! tag, value
144
+ end
145
+ end
146
+ block.call inner
147
+ end
148
+ end
149
+
150
+ def default_collection_rule
151
+ Proc.new do |collection_rule, objects, options|
152
+ end
153
+ end
154
+
155
+ def builder_entry(object, xml, children, rules_blocks, options)
156
+ rule = Restfulie::Common::Builder::Marshalling::Xml::MemberRule.new(rules_blocks)
157
+ options = namespace_enhance(options)
158
+
159
+ rule.blocks.unshift(default_member_rule) if options[:default_rule]
160
+ rule.apply(object, options)
161
+
162
+ start_with_namespace(rule.namespaces, xml, children, {}) do |inner_xml|
163
+ rule.to_xml(object, inner_xml)
164
+ end
165
+ end
166
+
167
+ def default_member_rule
168
+ Proc.new do |member_rule, object, options|
169
+ if options[:namespace]
170
+ member_rule.namespace(object, options[:namespace][:uri], options[:namespace])
171
+ else
172
+ member_rule.namespace(object, nil, options[:namespace] || {})
173
+ end
174
+ end
175
+ end
176
+
177
+ def namespace_enhance(options)
178
+ if !options[:namespace].nil? && options[:namespace].kind_of?(String)
179
+ options[:namespace] = { :uri => options[:namespace], :eager_load => true }
180
+ end
181
+ options
182
+ end
183
+ end
@@ -0,0 +1,24 @@
1
+ # Rules that allow any type of content shall extend this one and provide valid serialization methods. i.e.: to_xml
2
+ module Restfulie::Common::Builder::Rules::CustomAttributes
3
+
4
+ # returns true if it's a valid attribute evaluation or
5
+ def method_missing(sym, *args)
6
+ if sym.to_s.last=="=" && args.size==1
7
+ custom_attributes[sym.to_s.chop] = args[0]
8
+ elsif custom_attributes[sym.to_s]
9
+ custom_attributes[sym.to_s]
10
+ else
11
+ super(sym, *args)
12
+ end
13
+ end
14
+
15
+ def respond_to?(sym)
16
+ super(sym) || (sym.to_s.last == "=") || custom_attributes[sym.to_s]
17
+ end
18
+
19
+ private
20
+
21
+ def custom_attributes
22
+ @custom_attributes ||= {}
23
+ end
24
+ end
@@ -1,3 +1,14 @@
1
+ # Representation of a namespace. Allows any type of attribute setting and grabbing, i.e.:
2
+ #
3
+ # collection.namespace(:basket, "http://openbuy.com/basket") do |ns|
4
+ # ns.price = @basket.cost
5
+ # end
6
+ #
7
+ # or
8
+ #
9
+ # collection.describe_members(:namespaces => "http://localhost:3000/items") do |member, item|
10
+ # member.links << link( :rel => :self, :href => item_url(item))
11
+ # end
1
12
  class Restfulie::Common::Builder::Rules::Namespace < Hash
2
13
  attr_reader :namespace
3
14
  attr_reader :uri
@@ -9,7 +20,6 @@ class Restfulie::Common::Builder::Rules::Namespace < Hash
9
20
  end
10
21
 
11
22
  def uri=(value)
12
- raise Restfulie::Common::Error::NameSpaceError.new('Namespace can not be blank uri.') if value.blank?
13
23
  @uri = value
14
24
  end
15
25
 
@@ -1,5 +1,6 @@
1
1
  module Restfulie::Common::Builder::Rules; end
2
2
 
3
+ # Set of rules that can be used to describe a resource in a tokamak view.
3
4
  class Restfulie::Common::Builder::Rules::Base
4
5
  attr_accessor :blocks
5
6
  attr_accessor :links
@@ -18,7 +19,7 @@ class Restfulie::Common::Builder::Rules::Base
18
19
  end
19
20
  end
20
21
 
21
- # Use to register namespace
22
+ # Use to register a namespace
22
23
  #
23
24
  #==Example:
24
25
  #
@@ -11,6 +11,7 @@ module Restfulie::Common::Builder; end
11
11
  rules/namespace
12
12
  rules/member_rule
13
13
  rules/collection_rule
14
+ rules/custom_attributes
14
15
  ).each do |file|
15
16
  require File.join(File.dirname(__FILE__), 'builder', file)
16
17
  end
@@ -22,8 +22,10 @@ module Restfulie::Common::Representation
22
22
  end
23
23
  end
24
24
 
25
- def marshal(string, rel)
26
- string
25
+ def marshal(entity, rel)
26
+ return entity if entity.kind_of? String
27
+ entity.to_xml
28
+ entity
27
29
  end
28
30
 
29
31
  # transforms this content into a parameter hash for rails (server-side usage)
@@ -12,7 +12,8 @@ module Restfulie::Common::Representation
12
12
  }
13
13
 
14
14
  def unmarshal(string)
15
- JSON.parse(string)
15
+ base = JSON.parse(string)
16
+ base == {} ? {} : base.values.first
16
17
  end
17
18
 
18
19
  def marshal(entity, rel)
@@ -39,8 +39,9 @@ module Restfulie::Common::Representation
39
39
  Hash.from_xml(string).values.first
40
40
  end
41
41
 
42
- def marshal(string, rel)
43
- string
42
+ def marshal(entity, rel)
43
+ return entity if entity.kind_of? String
44
+ entity.to_xml
44
45
  end
45
46
 
46
47
  end
@@ -11,7 +11,7 @@ module Restfulie::Server::ActionView::Helpers
11
11
  #
12
12
  # in partial:
13
13
  #
14
- # member.links << link(:rel => :artistis, :href => album_artists_url(album))
14
+ # member.links << link(:rel => :artists, :href => album_artists_url(album))
15
15
  #
16
16
  # Or passing local variables assing
17
17
  #
@@ -3,13 +3,12 @@ module Restfulie::Server::ActionView::TemplateHandlers
3
3
  class Tokamak < ActionView::TemplateHandler
4
4
  include ActionView::TemplateHandlers::Compilable
5
5
 
6
- # TODO: Implement error for code not return builder
7
6
  def compile(template)
8
7
  "extend Restfulie::Common::Builder::Helpers; " +
9
8
  "extend Restfulie::Server::ActionView::Helpers; " +
10
9
  "code_block = lambda { #{template.source} };" +
11
10
  "builder = code_block.call; " +
12
- "builder.to_atom "
11
+ "builder.send \"to_\#{self.response.content_type}\" "
13
12
  end
14
13
  end
15
14
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 7
8
- - 1
9
- version: 0.7.1
8
+ - 2
9
+ version: 0.7.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Guilherme Silveira, Caue Guerra
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-21 00:00:00 -03:00
17
+ date: 2010-04-19 00:00:00 -03:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -98,8 +98,10 @@ files:
98
98
  - lib/restfulie/common/builder/marshalling/atom.rb
99
99
  - lib/restfulie/common/builder/marshalling/base.rb
100
100
  - lib/restfulie/common/builder/marshalling/json.rb
101
+ - lib/restfulie/common/builder/marshalling/xml.rb
101
102
  - lib/restfulie/common/builder/marshalling.rb
102
103
  - lib/restfulie/common/builder/rules/collection_rule.rb
104
+ - lib/restfulie/common/builder/rules/custom_attributes.rb
103
105
  - lib/restfulie/common/builder/rules/link.rb
104
106
  - lib/restfulie/common/builder/rules/links.rb
105
107
  - lib/restfulie/common/builder/rules/member_rule.rb