Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

What's a ruby without a gem?

  • Loading branch information...
commit aaf3bf7ab72e821efcfb3d694ee09a5cbf4c0a11 1 parent 65e57ea
@baphled authored
View
49 .gitignore
@@ -0,0 +1,49 @@
+# rcov generated
+coverage
+coverage.data
+
+# rdoc generated
+rdoc
+
+# yard generated
+doc
+.yardoc
+
+# bundler
+.bundle
+
+# jeweler generated
+pkg
+
+# Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
+#
+# * Create a file at ~/.gitignore
+# * Include files you want ignored
+# * Run: git config --global core.excludesfile ~/.gitignore
+#
+# After doing this, these files will be ignored in all your git projects,
+# saving you from having to 'pollute' every project you touch with them
+#
+# Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
+#
+# For MacOS:
+#
+#.DS_Store
+
+# For TextMate
+#*.tmproj
+#tmtags
+
+# For emacs:
+#*~
+#\#*
+#.\#*
+
+# For vim:
+#*.swp
+
+# For redcar:
+#.redcar
+
+# For rubinius:
+#*.rbc
View
1  .rspec
@@ -0,0 +1 @@
+--color
View
13 Gemfile
@@ -1,8 +1,15 @@
source "http://rubygems.org"
gem 'active_support'
+# Add dependencies required to use your gem here.
+# Example:
+# gem "activesupport", ">= 2.3.5"
-group 'development' do
- gem 'rspec'
+# Add dependencies to develop your gem here.
+# Include everything needed to run rake, tests, features, etc.
+group :development do
+ gem "rspec", "~> 2.8.0"
+ gem "rdoc", "~> 3.12"
+ gem "bundler", "~> 1.0.0"
+ gem "jeweler", "~> 1.8.3"
end
-
View
31 Gemfile.lock
@@ -5,18 +5,31 @@ GEM
activesupport (= 3.0.0)
activesupport (3.0.0)
diff-lcs (1.1.3)
- rspec (2.9.0)
- rspec-core (~> 2.9.0)
- rspec-expectations (~> 2.9.0)
- rspec-mocks (~> 2.9.0)
- rspec-core (2.9.0)
- rspec-expectations (2.9.1)
- diff-lcs (~> 1.1.3)
- rspec-mocks (2.9.0)
+ git (1.2.5)
+ jeweler (1.8.3)
+ bundler (~> 1.0)
+ git (>= 1.2.5)
+ rake
+ rdoc
+ json (1.6.6)
+ rake (0.9.2.2)
+ rdoc (3.12)
+ json (~> 1.4)
+ rspec (2.8.0)
+ rspec-core (~> 2.8.0)
+ rspec-expectations (~> 2.8.0)
+ rspec-mocks (~> 2.8.0)
+ rspec-core (2.8.0)
+ rspec-expectations (2.8.0)
+ diff-lcs (~> 1.1.2)
+ rspec-mocks (2.8.0)
PLATFORMS
ruby
DEPENDENCIES
active_support
- rspec
+ bundler (~> 1.0.0)
+ jeweler (~> 1.8.3)
+ rdoc (~> 3.12)
+ rspec (~> 2.8.0)
View
20 LICENSE.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2012 Yomi Colledge
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
202 README.md
@@ -1,21 +1,22 @@
+# AcceptableModel
+
Inpired by a conversation with @craigwebster and from reading @avidgrimm's
'Objects on Rails' book I've been thinking lately about APIs and how to
separate presentation logic from controllers and models in a clean way.
So we have a model, that has a few associations and accessors
-``
- class Artist
- attr_accessor :name, :albums, :songs, :debut
- def albums
- ['The coming', 'Disaster strikes']
- end
+ class Artist
+ attr_accessor :name, :albums, :songs, :debut
- def know_groups
- ['Leaders of the new school', 'Flipmode Squad']
+ def albums
+ ['The coming', 'Disaster strikes']
+ end
+
+ def know_groups
+ ['Leaders of the new school', 'Flipmode Squad']
+ end
end
- end
-``
Now it'd be cool if we could extend our output to include relationships between
information and provide a HATEOS like API without cluttering up a beautiful
@@ -31,19 +32,17 @@ models truely stay separate from our presentation logic.
So we can define an object `AcceptableModel.define 'artist'` and then you have
a Presenter like object that deals with the models presentation features
-``
- class Artists < Sinatra::Base
- get '/artists'
- AcceptableModel::Artist.all
+ class Artists < Sinatra::Base
+ get '/artists'
+ AcceptableModel::Artist.all
+ end
end
- end
- class Groups < Sinatra::Base
- get '/collecions'
- AcceptableModel::Groups.all
+ class Groups < Sinatra::Base
+ get '/collecions'
+ AcceptableModel::Groups.all
+ end
end
- end
-``
This is how we like it, our models shouldn't know about presentation logic
@@ -54,128 +53,115 @@ The cool thing about the rel attribute is that we can define our own, doing
this couldn't be easier. Re-open the defined class and simple create your own
Re-open the defined class and simple create your own
relationship.
-``
- class AcceptableModel::Artist
-
- # /partOf
- #
- # It doesn't matter whether the method returns an Array, or object as
- # long as it has an id
- #
- def part_of
- know_groups
- end
-
- #
- # /child
- #
- # The link is assumed by the name of the originating class and the
- # objects id
- #
- # =>
- # {
- # 'rel': '/child',
- # 'href': '/albums/the-coming'
- # }
- #
- def children
- albums
+
+ class AcceptableModel::Artist
+
+ # /partOf
+ #
+ # It doesn't matter whether the method returns an Array, or object as
+ # long as it has an id
+ #
+ def part_of
+ know_groups
+ end
+
+ #
+ # /child
+ #
+ # The link is assumed by the name of the originating class and the
+ # objects id
+ #
+ # =>
+ # {
+ # 'rel': '/child',
+ # 'href': '/albums/the-coming'
+ # }
+ #
+ def children
+ albums
+ end
end
- end
-``
Defining these methods exposes the objects relationships, visiting the resource
`curl -H 'Accept: application/json' -i http://localhost:9292/artists/busta-rhymes`
exposes the following response.
-``
- {
- 'name': 'Busta Rhymes',
- 'debut': '1990'
- 'albums': [
- 'name': 'The Coming',
+ {
+ 'name': 'Busta Rhymes',
+ 'debut': '1990'
+ 'albums': [
+ 'name': 'The Coming',
+ 'links': [
+ {
+ 'href': '/albums/the-coming',
+ 'rel': '/child'
+ }
+ ]
+ ],
+ 'songs': [
+ {'title': 'Gimme Some more', 'duration': '4:05'}
+ ]
'links': [
{
- 'href': '/albums/the-coming',
- 'rel': '/child'
+ 'href': '/artists/cilla_black',
+ 'rel': '/self'
+ },
+ {
+ 'href': '/collections/leaders-of-the-new-school',
+ 'rel': '/partOf'
+ },
+ {
+ 'href': '/collections/Flipmode-squad',
+ 'rel': '/partOf'
}
]
- ],
- 'songs': [
- {'title': 'Gimme Some more', 'duration': '4:05'}
- ]
- 'links': [
- {
- 'href': '/artists/cilla_black',
- 'rel': '/self'
- },
- {
- 'href': '/collections/leaders-of-the-new-school',
- 'rel': '/partOf'
- },
- {
- 'href': '/collections/Flipmode-squad',
- 'rel': '/partOf'
- }
- ]
- }
-``
+ }
All this from a few lines of code :D
AcceptableModel define a range of rel values but we should also be able to
create our own rel types, we could do this via the config method as follows:
-``
- AcceptableModel.config do |config|
- config.relationships = %w{self contains part_of parent child}
- end
-``
+ AcceptableModel.config do |config|
+ config.relationships = %w{self contains part_of parent child}
+ end
This will prefix all of our rel attribuetes with the string above
-TODO
-====
+## TODO
In true DRY fashion there is not need define a links href as they will be
looked up via our controllers.
Should be able to define associations that should include relationships
-``
- class AcceptableArtist
- rel_associations :groups
- end
-``
+ class AcceptableArtist
+ rel_associations :groups
+ end
This allows you to define which methods should be included in the response body
along with their associated links.
When calling `model.all` the output will now be as following:
-``
- {
- 'name': 'Busta Rhymes',
- 'debut': '1990'
- 'albums': [
- 'name': 'The Coming',
- 'links': [
- {
- 'href': '/albums/the-coming',
- 'rel': '/children'
- }
+ {
+ 'name': 'Busta Rhymes',
+ 'debut': '1990'
+ 'albums': [
+ 'name': 'The Coming',
+ 'links': [
+ {
+ 'href': '/albums/the-coming',
+ 'rel': '/children'
+ }
+ ]
]
- ]
- }
-``
+ }
We should also be able to easily change the rel attributes so that we can fully
customised the way they are displayed. It would be nice if we could do
something like this:
-``
- AcceptableModel.config do |config|
- config.rel_prefix = '/relations/'
- end
-``
-
+ AcceptableModel.config do |config|
+ config.rel_prefix = '/relations/'
+ end
View
49 Rakefile
@@ -0,0 +1,49 @@
+# encoding: utf-8
+
+require 'rubygems'
+require 'bundler'
+begin
+ Bundler.setup(:default, :development)
+rescue Bundler::BundlerError => e
+ $stderr.puts e.message
+ $stderr.puts "Run `bundle install` to install missing gems"
+ exit e.status_code
+end
+require 'rake'
+
+require 'jeweler'
+Jeweler::Tasks.new do |gem|
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
+ gem.name = "acceptable_model"
+ gem.homepage = "http://github.com/baphled/acceptable_model"
+ gem.license = "MIT"
+ gem.summary = %Q{TODO: one-line summary of your gem}
+ gem.description = %Q{TODO: longer description of your gem}
+ gem.email = "baphled@boodah.net"
+ gem.authors = ["Yomi Colledge"]
+ # dependencies defined in Gemfile
+end
+Jeweler::RubygemsDotOrgTasks.new
+
+require 'rspec/core'
+require 'rspec/core/rake_task'
+RSpec::Core::RakeTask.new(:spec) do |spec|
+ spec.pattern = FileList['spec/**/*_spec.rb']
+end
+
+RSpec::Core::RakeTask.new(:rcov) do |spec|
+ spec.pattern = 'spec/**/*_spec.rb'
+ spec.rcov = true
+end
+
+task :default => :spec
+
+require 'rdoc/task'
+Rake::RDocTask.new do |rdoc|
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
+
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = "acceptable_model #{version}"
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
View
12 spec/spec_helper.rb
@@ -0,0 +1,12 @@
+$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+require 'rspec'
+require 'acceptable_model'
+
+# Requires supporting files with custom matchers and macros, etc,
+# in ./support/ and its subdirectories.
+Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
+
+RSpec.configure do |config|
+
+end
Please sign in to comment.
Something went wrong with that request. Please try again.