restpack-resource 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.gem
2
+ *.db
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ script: bundle exec rake test
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ group :test do
4
+ gem 'rake', '~> 10.0.3'
5
+ gem 'rspec', :require => 'spec'
6
+ gem 'datamapper', '~> 1.2.0'
7
+ gem 'dm-pager', '~> 1.1.0'
8
+ gem 'dm-sqlite-adapter', '~> 1.2.0'
9
+ gem 'factory_girl', '~> 4.2.0'
10
+ end
11
+
12
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,95 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ restpack-resource (0.0.1)
5
+ active_support (~> 3.0.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ active_support (3.0.0)
11
+ activesupport (= 3.0.0)
12
+ activesupport (3.0.0)
13
+ addressable (2.2.8)
14
+ bcrypt-ruby (3.0.1)
15
+ data_objects (0.10.12)
16
+ addressable (~> 2.1)
17
+ datamapper (1.2.0)
18
+ dm-aggregates (~> 1.2.0)
19
+ dm-constraints (~> 1.2.0)
20
+ dm-core (~> 1.2.0)
21
+ dm-migrations (~> 1.2.0)
22
+ dm-serializer (~> 1.2.0)
23
+ dm-timestamps (~> 1.2.0)
24
+ dm-transactions (~> 1.2.0)
25
+ dm-types (~> 1.2.0)
26
+ dm-validations (~> 1.2.0)
27
+ diff-lcs (1.1.3)
28
+ dm-aggregates (1.2.0)
29
+ dm-core (~> 1.2.0)
30
+ dm-constraints (1.2.0)
31
+ dm-core (~> 1.2.0)
32
+ dm-core (1.2.0)
33
+ addressable (~> 2.2.6)
34
+ dm-do-adapter (1.2.0)
35
+ data_objects (~> 0.10.6)
36
+ dm-core (~> 1.2.0)
37
+ dm-migrations (1.2.0)
38
+ dm-core (~> 1.2.0)
39
+ dm-pager (1.1.0)
40
+ dm-aggregates (>= 0.10.1)
41
+ dm-core (>= 0.10.1)
42
+ dm-serializer (1.2.2)
43
+ dm-core (~> 1.2.0)
44
+ fastercsv (~> 1.5)
45
+ json (~> 1.6)
46
+ json_pure (~> 1.6)
47
+ multi_json (~> 1.0)
48
+ dm-sqlite-adapter (1.2.0)
49
+ dm-do-adapter (~> 1.2.0)
50
+ do_sqlite3 (~> 0.10.6)
51
+ dm-timestamps (1.2.0)
52
+ dm-core (~> 1.2.0)
53
+ dm-transactions (1.2.0)
54
+ dm-core (~> 1.2.0)
55
+ dm-types (1.2.2)
56
+ bcrypt-ruby (~> 3.0)
57
+ dm-core (~> 1.2.0)
58
+ fastercsv (~> 1.5)
59
+ json (~> 1.6)
60
+ multi_json (~> 1.0)
61
+ stringex (~> 1.4)
62
+ uuidtools (~> 2.1)
63
+ dm-validations (1.2.0)
64
+ dm-core (~> 1.2.0)
65
+ do_sqlite3 (0.10.12)
66
+ data_objects (= 0.10.12)
67
+ factory_girl (4.2.0)
68
+ activesupport (>= 3.0.0)
69
+ fastercsv (1.5.5)
70
+ json (1.7.6)
71
+ json_pure (1.7.6)
72
+ multi_json (1.5.0)
73
+ rake (10.0.3)
74
+ rspec (2.12.0)
75
+ rspec-core (~> 2.12.0)
76
+ rspec-expectations (~> 2.12.0)
77
+ rspec-mocks (~> 2.12.0)
78
+ rspec-core (2.12.2)
79
+ rspec-expectations (2.12.1)
80
+ diff-lcs (~> 1.1.3)
81
+ rspec-mocks (2.12.2)
82
+ stringex (1.5.1)
83
+ uuidtools (2.1.3)
84
+
85
+ PLATFORMS
86
+ ruby
87
+
88
+ DEPENDENCIES
89
+ datamapper (~> 1.2.0)
90
+ dm-pager (~> 1.1.0)
91
+ dm-sqlite-adapter (~> 1.2.0)
92
+ factory_girl (~> 4.2.0)
93
+ rake (~> 10.0.3)
94
+ restpack-resource!
95
+ rspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2013 Gavin Joyce and RestPack contributors
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # restpack-resource [![Build Status](https://api.travis-ci.org/RestPack/restpack-resource.png?branch=master)](https://travis-ci.org/RestPack/restpack-resource) [![Code Climate](https://codeclimate.com/github/RESTpack/restpack-resource.png)](https://codeclimate.com/github/RESTpack/restpack-resource) [![Dependency Status](https://gemnasium.com/RestPack/restpack-resource.png)](https://gemnasium.com/RestPack/restpack-resource)
2
+
3
+ RESTful resource paging, side-loading, filtering and sorting:
4
+
5
+ ```ruby
6
+ class Group
7
+ include DataMapper::Resource
8
+ include RestPack::Resource
9
+
10
+ property :id, Serial
11
+ property :name, String, :length => 128
12
+ property :created_by, Integer, :required => true
13
+ property :channel_id, Integer, :required => true
14
+ timestamps :at
15
+
16
+ validates_presence_of :name, :channel_id
17
+
18
+ has n, :invitations
19
+ has n, :memberships
20
+
21
+ resource_can_include :memberships, :invitations
22
+ resource_can_filter_by :channel_id, :created_by
23
+ resource_can_sort_by :id
24
+ end
25
+ ```
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :default => :test
4
+ task :test => :spec
5
+
6
+ begin
7
+ require "rspec/core/rake_task"
8
+
9
+ desc "Run all specs"
10
+ RSpec::Core::RakeTask.new(:spec) do |t|
11
+ t.rspec_opts = ['-cfs']
12
+ end
13
+ rescue LoadError
14
+ end
@@ -0,0 +1,22 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module RestPack
4
+ module Resource
5
+ module Filterable
6
+ def resource_filterable_by
7
+ @resource_filterable_by || []
8
+ end
9
+ def resource_filterable_by=(columns)
10
+ @resource_filterable_by = columns
11
+ end
12
+ def resource_can_filter_by(*columns)
13
+ self.resource_filterable_by += columns
14
+ end
15
+ def resource_validate_filters!(filters)
16
+ filters.keys.each do |filter|
17
+ raise InvalidFilter.new unless self.resource_filterable_by.include?(filter)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module RestPack
4
+ module Resource
5
+ module Includable
6
+ def resource_includable_associations
7
+ @resource_includable_associations || []
8
+ end
9
+ def resource_includable_associations=(associations)
10
+ @resource_includable_associations = associations
11
+ end
12
+ def resource_can_include(*associations)
13
+ self.resource_includable_associations += associations
14
+ end
15
+ def resource_validate_includes!(includes)
16
+ includes.each do |include|
17
+ unless self.resource_includable_associations.include?(include)
18
+ raise InvalidInclude, "#{self.name}.#{include} is not an includable relation"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,114 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module RestPack
4
+ module Resource
5
+ module Pageable
6
+ def paged_resource(params = {}, overrides = {})
7
+ options = build_options(params, overrides)
8
+ get_paged_resource(options)
9
+ end
10
+
11
+ protected
12
+
13
+ def get_paged_resource(options)
14
+ page = get_page(options)
15
+
16
+ paged_resource = {
17
+ :page => page.pager.current_page,
18
+ :page_count => page.pager.total_pages,
19
+ :total => page.pager.total,
20
+ :previous_page => page.pager.previous_page,
21
+ :next_page => page.pager.next_page
22
+ }
23
+
24
+ paged_resource[self.resource_collection_name] = page.map { |item| item.to_resource() }
25
+
26
+ unless page.empty?
27
+ options[:includes].each do |association|
28
+ paged_resource[association] = side_load(page, association)
29
+ end
30
+ end
31
+
32
+ paged_resource
33
+ end
34
+
35
+ def get_page(options)
36
+ order = options[:sort_by]
37
+ order = order.desc if order && options[:sort_direction] == :descending
38
+
39
+ options[:scope].all(:conditions => options[:filters]).page(options[:page], :order => order)
40
+ end
41
+
42
+ def side_load(page, association)
43
+ target_model_name = association.to_s.singularize.capitalize
44
+ relationships = self.relationships.select {|r| r.target_model.to_s == target_model_name }
45
+ raise InvalidInclude if relationships.empty?
46
+
47
+ side_loaded_entities = []
48
+
49
+ relationships.each do |relationship|
50
+ unless relationship.is_a? DataMapper::Associations::ManyToOne::Relationship
51
+ raise InvalidInclude, "#{self.name}.#{relationship.name} can't be included when paging #{self.name.pluralize.downcase}"
52
+ end
53
+ side_loaded_entities += page.map do |entity| #TODO: GJ: PERF: we can bypass datamapper associations and get by a list of ids instead
54
+ relation = entity.send(relationship.name.to_sym)
55
+ relation ? relation.to_resource : nil
56
+ end
57
+ side_loaded_entities.uniq!
58
+ side_loaded_entities.compact!
59
+ end
60
+
61
+ side_loaded_entities
62
+ end
63
+
64
+ def resource_collection_name
65
+ self.name.to_s.downcase.pluralize.to_sym
66
+ end
67
+
68
+ def build_options(params, overrides)
69
+ options = overrides.reverse_merge( #overrides take precedence over params
70
+ :page => params[:page],
71
+ :includes => params[:includes].nil? ? [] : params[:includes].split(','),
72
+ :filters => self.extract_filters_from_params(params),
73
+ :sort_by => params[:sort_by],
74
+ :sort_direction => params[:sort_direction]
75
+ )
76
+
77
+ options.reverse_merge!( #defaults
78
+ :scope => self.all,
79
+ :page => 1,
80
+ :includes => [],
81
+ :filters => {},
82
+ :sort_by => nil,
83
+ :sort_direction => :ascending
84
+ )
85
+
86
+ resource_normalise_options!(options)
87
+ resource_validate_options!(options)
88
+
89
+ options
90
+ end
91
+
92
+ def extract_filters_from_params(params)
93
+ extracted = params.extract!(*self.resource_filterable_by)
94
+ extracted.delete_if { |k, v| v.nil? }
95
+ end
96
+
97
+ private
98
+
99
+ def resource_normalise_options!(options)
100
+ options[:includes].map!{|i| i.to_sym}
101
+
102
+ [:sort_by, :sort_direction].each do |attribute|
103
+ options[attribute] = options[attribute].to_sym if options[attribute]
104
+ end
105
+ end
106
+
107
+ def resource_validate_options!(options)
108
+ self.resource_validate_includes! options[:includes]
109
+ self.resource_validate_filters! options[:filters]
110
+ self.resource_validate_sort_by! options[:sort_by]
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,20 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module RestPack
4
+ module Resource
5
+ module Sortable
6
+ def resource_sortable_by
7
+ @resource_sortable_by || []
8
+ end
9
+ def resource_sortable_by=(columns)
10
+ @resource_sortable_by = columns
11
+ end
12
+ def resource_can_sort_by(*columns)
13
+ self.resource_sortable_by += columns
14
+ end
15
+ def resource_validate_sort_by!(sort_by)
16
+ raise InvalidSortBy.new unless sort_by.nil? || self.resource_sortable_by.include?(sort_by)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ %w{pageable filterable sortable includable}.each {|m| require "restpack-resource/resource/#{m}" }
2
+
3
+ module RestPack
4
+ module Resource
5
+ class InvalidInclude < Exception; end
6
+ class InvalidFilter < Exception; end
7
+ class InvalidSortBy < Exception; end
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ base.extend(RestPack::Resource::Pageable)
12
+ base.extend(RestPack::Resource::Filterable)
13
+ base.extend(RestPack::Resource::Sortable)
14
+ base.extend(RestPack::Resource::Includable)
15
+ super
16
+ end
17
+
18
+ def to_resource(options = {})
19
+ self
20
+ end
21
+
22
+ module ClassMethods
23
+ def resource_single_resource(options = {})
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ module RestPack
2
+ module Resource
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,2 @@
1
+ require 'restpack-resource/version'
2
+ require 'restpack-resource/resource'
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'restpack-resource/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "restpack-resource"
8
+ gem.version = RestPack::Resource::VERSION
9
+ gem.authors = ["Gavin Joyce"]
10
+ gem.email = ["gavinjoyce@gmail.com"]
11
+ gem.description = %q{RESTful resource paging, side-loading, filtering and sorting}
12
+ gem.summary = %q{...}
13
+ gem.homepage = "https://github.com/RESTpack/restpack-resource"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'active_support', '~> 3.0.0'
21
+ gem.add_development_dependency 'rspec', '~> 2.12'
22
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,21 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ sequence(:name) {|n| "User #{n}" }
4
+ end
5
+
6
+ factory :artist do
7
+ sequence(:name) {|n| "Artist #{n}" }
8
+ end
9
+
10
+ factory :song do
11
+ sequence(:name) {|n| "Song #{n}" }
12
+ artist
13
+ association :creator, factory: :user
14
+ association :modifier, factory: :user
15
+ end
16
+
17
+ factory :comment do
18
+ sequence(:test) {|n| "This is comment #{n}" }
19
+ song
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe RestPack::Resource do
4
+ describe "#paged_resource" do
5
+ context "when filtering" do
6
+ context "with valid options" do
7
+ before(:each) do
8
+ @artist = create(:artist)
9
+ 3.times { create(:song, artist: @artist) }
10
+ 9.times { create(:song) }
11
+ end
12
+
13
+ it "should return the total count" do
14
+ result = Song.paged_resource()
15
+ result[:total].should == 12
16
+ end
17
+
18
+ it "should filter results" do
19
+ result = Song.paged_resource(artist_id: @artist.id)
20
+ result[:total].should == 3
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,90 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe RestPack::Resource do
4
+ describe "#paged_resource" do
5
+ context "when paging" do
6
+ context "with no data" do
7
+ before(:each) do
8
+ Song.destroy
9
+ end
10
+
11
+ it "should return an empty array" do
12
+ result = Song.paged_resource()
13
+ result.should_not == nil
14
+ result[:songs].should_not == nil
15
+ end
16
+ end
17
+
18
+ context "with data" do
19
+ before(:each) do
20
+ 16.times { create(:song) }
21
+ end
22
+
23
+ it "has a valid first page" do
24
+ result = Artist.paged_resource()
25
+ result[:page].should == 1
26
+ result[:total].should == 16
27
+ result[:page_count].should == 2
28
+ result[:previous_page].should == nil
29
+ result[:next_page].should == 2
30
+ result[:artists].size.should == 10
31
+ end
32
+
33
+ it "has a valid second page" do
34
+ result = Artist.paged_resource(page: 2)
35
+ result[:page].should == 2
36
+ result[:previous_page].should == 1
37
+ result[:next_page].should == nil
38
+ result[:artists].size.should == 6
39
+ end
40
+
41
+ context "when including relations" do
42
+ it "should not allow invalid relations" do
43
+ expect do
44
+ Artist.paged_resource(:includes => 'invalid_relations')
45
+ end.to raise_error(RestPack::Resource::InvalidInclude, "Artist.invalid_relations is not an includable relation")
46
+ end
47
+
48
+ it "should not allow includes that have not been specified with 'resource_can_include' NEW" do
49
+ expect do
50
+ Comment.paged_resource(:includes => 'songs')
51
+ end.to raise_error(RestPack::Resource::InvalidInclude, "Comment.songs is not an includable relation")
52
+ end
53
+
54
+ it "should not allow a 'has_many' include when paging" do
55
+ expect do
56
+ Artist.paged_resource(:includes => 'songs')
57
+ end.to raise_error(RestPack::Resource::InvalidInclude, "Artist.songs can't be included when paging artists")
58
+ end
59
+
60
+ it "should return related entities from a 'belongs_to' relationship" do
61
+ result = Song.paged_resource(:includes => 'artists')
62
+ result.should_not == nil
63
+ result[:artists].should_not == nil
64
+ result[:artists].size.should == 10
65
+ end
66
+
67
+ it "should allow multiple includes" do
68
+ result = Song.paged_resource(:includes => 'artists,users')
69
+ result.should_not == nil
70
+ result[:artists].should_not == nil
71
+ result[:artists].size.should == 10
72
+ result[:users].size.should == 20
73
+ end
74
+
75
+ it "should return related entities with their #to_resource representation" do
76
+ result = Song.paged_resource(:includes => 'users')
77
+ result[:users][0][:custom].should == 'This is custom data'
78
+ end
79
+
80
+ context "when specifying overrides" do
81
+ it "should give overrides precidence" do
82
+ result = Artist.paged_resource({ page: 2 }, { page: 3 })
83
+ result[:page].should == 3
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,50 @@
1
+ require './spec/spec_helper'
2
+ require './lib/restpack-resource'
3
+
4
+ describe RestPack::Resource do
5
+ context "Resource Setup" do
6
+ before :each do
7
+ class MyModel
8
+ include DataMapper::Resource
9
+ include RestPack::Resource
10
+ property :id, Serial
11
+ property :name, String
12
+ property :age, Integer
13
+ end
14
+ DataMapper.auto_migrate!
15
+ end
16
+
17
+ describe "#resource_can_include" do
18
+ it "allows association to be set" do
19
+ MyModel.resource_includable_associations.length == 0
20
+ class MyModel
21
+ resource_can_include :association1
22
+ resource_can_include :association2, :association3
23
+ end
24
+ MyModel.resource_includable_associations.should == [:association1, :association2, :association3]
25
+ end
26
+ end
27
+
28
+ describe "#resource_can_filter_by" do
29
+ it "allows filterable columns to be set" do
30
+ MyModel.resource_filterable_by.length == 0
31
+ class MyModel
32
+ resource_can_filter_by :id
33
+ resource_can_filter_by :name, :age
34
+ end
35
+ MyModel.resource_filterable_by.should == [:id, :name, :age]
36
+ end
37
+ end
38
+
39
+ describe "#resource_can_sort_by" do
40
+ it "allows sortable columns to be set" do
41
+ MyModel.resource_sortable_by.length == 0
42
+ class MyModel
43
+ resource_can_sort_by :id
44
+ resource_can_sort_by :name, :age
45
+ end
46
+ MyModel.resource_sortable_by.should == [:id, :name, :age]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,42 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe RestPack::Resource do
4
+ describe "#paged_resource" do
5
+ context "when sorting" do
6
+ context "with invalid options" do
7
+ it "should not allow invalid sort column" do
8
+ expect do
9
+ Artist.paged_resource(:sort_by => :invalid_column)
10
+ end.to raise_error(RestPack::Resource::InvalidSortBy)
11
+ end
12
+ end
13
+
14
+ context "with valid options" do
15
+ before(:each) do
16
+ 20.times { create(:artist) }
17
+ end
18
+
19
+ it "should default to ascending" do
20
+ result = Artist.paged_resource(:sort_by => :id)
21
+ result[:total].should == Artist.count
22
+ result[:artists].first[:id].should == 1
23
+ result[:artists].last[:id].should == 10
24
+ end
25
+
26
+ it "should allow sort_direction of descending" do
27
+ result = Artist.paged_resource(:sort_by => :id, :sort_direction => :descending)
28
+ result[:total].should == Artist.count
29
+ result[:artists].first[:id].should == 20
30
+ result[:artists].last[:id].should == 11
31
+ end
32
+
33
+ it "should allow sort_by and sort_direction to be string or symbol" do
34
+ result = Artist.paged_resource(:sort_by => 'id', :sort_direction => 'descending')
35
+ result[:total].should == Artist.count
36
+ result = Artist.paged_resource(:sort_by => :id, :sort_direction => :descending)
37
+ result[:total].should == Artist.count
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,99 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup(:default, :test)
4
+
5
+ require 'dm-core'
6
+ require 'dm-timestamps'
7
+ require 'dm-validations'
8
+ require 'dm-aggregates'
9
+ require 'dm-migrations'
10
+ require 'dm-types'
11
+ require 'dm-serializer'
12
+ require 'dm-pager'
13
+ require 'factory_girl'
14
+
15
+ require './lib/restpack-resource'
16
+
17
+ ENV["RACK_ENV"] ||= 'test'
18
+
19
+ DataMapper.setup(:default, "sqlite3::memory:")
20
+ DataMapper::Model.raise_on_save_failure = true
21
+ DataMapper::Pagination.defaults[:per_page] = 10
22
+
23
+
24
+ class Artist
25
+ include DataMapper::Resource
26
+ include RestPack::Resource
27
+ property :id, Serial
28
+ property :name, String
29
+ #timestamps :at #NOTE: GJ: FactoryGirl is not saving when DM timestamps are used
30
+
31
+ validates_presence_of :name
32
+ has n, :songs
33
+
34
+ resource_can_include :songs
35
+ resource_can_sort_by :id, :name
36
+ end
37
+
38
+ class Song
39
+ include DataMapper::Resource
40
+ include RestPack::Resource
41
+ property :id, Serial
42
+ property :name, String
43
+ property :artist_id, Integer
44
+ property :created_by, Integer
45
+ property :modified_by, Integer
46
+ #timestamps :at
47
+
48
+ validates_presence_of :name
49
+ belongs_to :artist
50
+ belongs_to :creator, 'User', :child_key => [ :created_by ]
51
+ belongs_to :modifier, 'User', :child_key => [ :modified_by ]
52
+ has n, :comments
53
+
54
+ resource_can_include :artists, :users
55
+ resource_can_filter_by :artist_id
56
+ end
57
+
58
+ class Comment
59
+ include DataMapper::Resource
60
+ include RestPack::Resource
61
+ property :id, Serial
62
+ property :song_id, Integer
63
+ property :text, String
64
+ #timestamps :at
65
+
66
+ validates_presence_of :text
67
+ belongs_to :song
68
+ end
69
+
70
+ class User
71
+ include DataMapper::Resource
72
+ include RestPack::Resource
73
+ property :id, Serial
74
+ property :name, String
75
+ #timestamps :at
76
+
77
+ def to_resource
78
+ {
79
+ id: id,
80
+ name: name,
81
+ custom: "This is custom data"
82
+ }
83
+ end
84
+ end
85
+
86
+ FactoryGirl.find_definitions
87
+
88
+ RSpec.configure do |config|
89
+ config.treat_symbols_as_metadata_keys_with_true_values = true
90
+ config.run_all_when_everything_filtered = true
91
+ config.filter_run :focus
92
+ config.order = 'random'
93
+
94
+ config.include FactoryGirl::Syntax::Methods
95
+
96
+ config.before(:each) do
97
+ DataMapper.auto_migrate!
98
+ end
99
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: restpack-resource
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gavin Joyce
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_support
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '2.12'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '2.12'
46
+ description: RESTful resource paging, side-loading, filtering and sorting
47
+ email:
48
+ - gavinjoyce@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - .rspec
55
+ - .travis.yml
56
+ - Gemfile
57
+ - Gemfile.lock
58
+ - LICENSE
59
+ - README.md
60
+ - Rakefile
61
+ - lib/restpack-resource.rb
62
+ - lib/restpack-resource/resource.rb
63
+ - lib/restpack-resource/resource/filterable.rb
64
+ - lib/restpack-resource/resource/includable.rb
65
+ - lib/restpack-resource/resource/pageable.rb
66
+ - lib/restpack-resource/resource/sortable.rb
67
+ - lib/restpack-resource/version.rb
68
+ - restpack-resource.gemspec
69
+ - spec/factories.rb
70
+ - spec/resource/filterable_spec.rb
71
+ - spec/resource/pageable_spec.rb
72
+ - spec/resource/resource_spec.rb
73
+ - spec/resource/sortable_spec.rb
74
+ - spec/spec_helper.rb
75
+ homepage: https://github.com/RESTpack/restpack-resource
76
+ licenses: []
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.23
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: ! '...'
99
+ test_files:
100
+ - spec/factories.rb
101
+ - spec/resource/filterable_spec.rb
102
+ - spec/resource/pageable_spec.rb
103
+ - spec/resource/resource_spec.rb
104
+ - spec/resource/sortable_spec.rb
105
+ - spec/spec_helper.rb