restfulie 0.7.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
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