pragma 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 791ad06332320a27e0d54dac93e730e141e7ccf4
4
+ data.tar.gz: b46f3e7cb4147dd5b4f1e87593b979ae30b67a4c
5
+ SHA512:
6
+ metadata.gz: f9abcf4edfd280bfff4d932acfc40f4ff37856d1fa489abf4aba78ea04be9d95baca5109f6badcb615ac5b3ab916e66cc63c598c3c767d0c8e0fae0e5da03f91
7
+ data.tar.gz: 159c12e39c2f26d778b115d18543e9505eaef25eca6ddbc27cab4436e8fa74bc898be814281b7518f1b8255d58ca1a49fad28b73f22d285fb9e7dd180fd77224
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,87 @@
1
+ require: rubocop-rspec
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.3
5
+ Include:
6
+ - '**/Gemfile'
7
+ - '**/Rakefile'
8
+ Exclude:
9
+ - 'bin/*'
10
+ - 'db/**/*'
11
+ - 'vendor/bundle/**/*'
12
+ - 'spec/spec_helper.rb'
13
+ - 'spec/rails_helper.rb'
14
+ - 'spec/support/**/*'
15
+ - 'config/**/*'
16
+ - '**/Rakefile'
17
+ - '**/Gemfile'
18
+
19
+ RSpec/DescribeClass:
20
+ Exclude:
21
+ - 'spec/requests/**/*'
22
+
23
+ Style/BlockDelimiters:
24
+ Exclude:
25
+ - 'spec/**/*'
26
+
27
+ Style/AlignParameters:
28
+ EnforcedStyle: with_fixed_indentation
29
+
30
+ Style/ClosingParenthesisIndentation:
31
+ Enabled: false
32
+
33
+ Metrics/LineLength:
34
+ Max: 100
35
+ AllowURI: true
36
+
37
+ Style/FirstParameterIndentation:
38
+ Enabled: false
39
+
40
+ Style/MultilineMethodCallIndentation:
41
+ EnforcedStyle: indented
42
+
43
+ Style/IndentArray:
44
+ EnforcedStyle: consistent
45
+
46
+ Style/IndentHash:
47
+ EnforcedStyle: consistent
48
+
49
+ Style/SignalException:
50
+ EnforcedStyle: semantic
51
+
52
+ Style/BracesAroundHashParameters:
53
+ EnforcedStyle: context_dependent
54
+
55
+ Lint/EndAlignment:
56
+ AlignWith: variable
57
+ AutoCorrect: true
58
+
59
+ Style/AndOr:
60
+ EnforcedStyle: conditionals
61
+
62
+ Style/MultilineBlockChain:
63
+ Enabled: false
64
+
65
+ RSpec/NamedSubject:
66
+ Enabled: false
67
+
68
+ RSpec/ExampleLength:
69
+ Enabled: false
70
+
71
+ Style/MultilineMethodCallBraceLayout:
72
+ Enabled: false
73
+
74
+ Metrics/MethodLength:
75
+ Enabled: false
76
+
77
+ Metrics/AbcSize:
78
+ Enabled: false
79
+
80
+ Metrics/PerceivedComplexity:
81
+ Enabled: false
82
+
83
+ Metrics/CyclomaticComplexity:
84
+ Enabled: false
85
+
86
+ Metrics/ClassLength:
87
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 2.3.0
3
+ before_install:
4
+ - gem update --system
5
+ - gem update bundler
6
+ - gem cleanup bundler
7
+ branches:
8
+ only:
9
+ - master
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ - Removals
6
+ - Bugfixes
7
+ - Enhancements
8
+ - Deprecation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pragma.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Alessandro Desantis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # Pragma
2
+
3
+ [![Build Status](https://img.shields.io/travis/pragmarb/pragma.svg?maxAge=3600&style=flat-square)](https://travis-ci.org/pragmarb/pragma)
4
+ [![Dependency Status](https://img.shields.io/gemnasium/pragmarb/pragma.svg?maxAge=3600&style=flat-square)](https://gemnasium.com/github.com/pragmarb/pragma)
5
+ [![Code Climate](https://img.shields.io/codeclimate/github/pragmarb/pragma.svg?maxAge=3600&style=flat-square)](https://codeclimate.com/github/pragmarb/pragma)
6
+ [![Coveralls](https://img.shields.io/coveralls/pragmarb/pragma.svg?maxAge=3600&style=flat-square)](https://coveralls.io/github/pragmarb/pragma)
7
+
8
+ Welcome to Pragma, a pragmatic (duh!), opinionated architecture for building JSON APIs with Ruby!
9
+
10
+ You can think of this as a meta-gem that pulls in the following pieces:
11
+
12
+ - [Pragma::Operation](https://github.com/pragmarb/pragma-operation);
13
+ - [Pragma::Policy](https://github.com/pragmarb/pragma-policy);
14
+ - [Pragma::Decorator](https://github.com/pragmarb/pragma-decorator);
15
+ - [Pragma::Contract](https://github.com/pragmarb/pragma-contract).
16
+
17
+ Additionally, it also provides default CRUD operations that leverage all of the Pragma components
18
+ and will make creating new resources in your API a breeze.
19
+
20
+ Looking for a Rails integration? Check out [pragma-rails](https://github.com/pragmarb/pragma-rails)!
21
+
22
+ ## Philosophy
23
+
24
+ Pragma was created with a very specific goal in mind: to make the development of JSON APIs a matter
25
+ of hours, not days. In other words, Pragma is for JSON APIs what Rails is for web applications.
26
+
27
+ In order to achieve that goal, some ground rules were needed. Here they are.
28
+
29
+ 1. **Pragma is opinionated.** With Pragma, you don't get to make a lot of choices and that's
30
+ _exactly_ why people are using it: they want to focus on the business logic of their API rather
31
+ than the useless details. We understand this approach will not work in some cases and that's
32
+ alright. If you need more personalization, only use a subset of Pragma (see item 2) or something
33
+ else.
34
+ 2. **Pragma is modular.** Pragma is built as a set of gems (currently 4), plus some standalone
35
+ tools. You can pick one or more modules and use them in your application as you see fit. Even
36
+ though they are completely independent from each other, they nicely integrate and work best when
37
+ used together, creating an ecosystem that will dramatically speed up your design and development
38
+ process.
39
+ 3. **Pragma is not designed to be Rails-free.** This does not mean that Pragma _is not_ Rails free.
40
+ Our Rails integration is decoupled from the rest of the ecosystem and all of the gems, in their
41
+ current state, _can_ be used without Rails. However, this is just a byproduct of the project's
42
+ design: independence from Rails is not a goal of the Pragma ecosystem, so don't count on it too
43
+ much.
44
+
45
+ ## Why not Trailblazer?
46
+
47
+ [Trailblazer](https://github.com/trailblazer/trailblazer) and all of its companion projects are
48
+ awesome. They are so awesome that Pragma is built on top of them: even though we're not using
49
+ the Trailblazer gem itself yet, many of the Pragma gems are simply extensions of their Trailblazer
50
+ counterparts:
51
+
52
+ - decorators are [ROAR representers](https://github.com/apotonick/roar);
53
+ - contracts are [Reform forms](https://github.com/apotonick/reform).
54
+
55
+ Trailblazer and Pragma have different (but similar) places in the Ruby world: Trailblazer is an
56
+ architecture for building all kinds of web applications in an intelligent, rational way, while
57
+ Pragma is an architecture for building JSON APIs. We have shamelessly taken all of the flexibility
58
+ and awesomeness from the Trailblazer project and restricted it to a narrow field of work, providing
59
+ tools, helpers and integrations that could never be part of Trailblazer due to their specificity.
60
+
61
+ Thank you, guys!
62
+
63
+ ## Installation
64
+
65
+ Add this line to your application's Gemfile:
66
+
67
+ ```ruby
68
+ gem 'pragma'
69
+ ```
70
+
71
+ And then execute:
72
+
73
+ ```console
74
+ $ bundle
75
+ ```
76
+
77
+ Or install it yourself as:
78
+
79
+ ```console
80
+ $ gem install pragma
81
+ ```
82
+
83
+ ## Usage
84
+
85
+ All documentation is in the [doc](https://github.com/pragmarb/pragma/tree/master/doc) folder.
86
+
87
+ ## Contributing
88
+
89
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pragmarb/pragma.
90
+
91
+ ## License
92
+
93
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "pragma"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,50 @@
1
+ # Sensible defaults
2
+
3
+ This gem works best if you follow the recommended structure (partially borrowed from
4
+ [Trailblazer](https://github.com/trailblazer/trailblazer)) for organizing resources:
5
+
6
+ ```
7
+ └── api
8
+ └── v1
9
+ └── post
10
+ ├── contract
11
+ │   ├── create.rb
12
+ │   └── update.rb
13
+ ├── operation
14
+ │   ├── create.rb
15
+ │   ├── destroy.rb
16
+ │   ├── index.rb
17
+ │   └── update.rb
18
+ └── policy.rb
19
+ └── decorator.rb
20
+ ```
21
+
22
+ Your modules and classes would, of course, follow the same structure: `API::V1::Post::Policy`,
23
+ `API::V1::Post::Operation::Create` and so on and so forth.
24
+
25
+ If you adhere to this structure, the gem will be able to locate all of your classes without explicit
26
+ configuration (i.e. no `#policy` or `#contract` calls etc.). This will save you a lot of time and is
27
+ highly recommended, especially when used in conjunction with the provided CRUD operations.
28
+
29
+ To leverage automatic discovery, include `Pragma::Operation::Defaults` in your operation:
30
+
31
+ ```ruby
32
+ module API
33
+ module V1
34
+ module Post
35
+ module Operation
36
+ class Create < Pragma::Operation::Base
37
+ include Pragma::Operation::Defaults
38
+
39
+ def call
40
+ # You can use `decorate`, `validate` and `authorize` without having to explicitly
41
+ # specify the decorator, validator and policy classes.
42
+ #
43
+ # ...
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ ```
@@ -0,0 +1,109 @@
1
+ # CRUD operations
2
+
3
+ Pragma ships with a default set of CRUD operations which should work for most standard API
4
+ resources. You can use them as they are, modify them or simply roll your own.
5
+
6
+ ## The Index operation
7
+
8
+ Pragma provides a default implementation of the Index operation. Here's how it works:
9
+
10
+ 1. it finds all records of the model;
11
+ 2. it wraps the query in the policy to only return viewable records;
12
+ 3. it responds with 200 OK and a paginated list of decorated records.
13
+
14
+ To create an Index operation, inherit from `Pragma::Operation::Index`:
15
+
16
+ ```ruby
17
+ module API
18
+ module V1
19
+ module Post
20
+ module Operation
21
+ class Index < Pragma::Operation::Index
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ ```
28
+
29
+ To override the defaults of this operation, have a look at the [source code](https://github.com/pragmarb/pragma/blob/master/lib/pragma/operation/index.rb).
30
+
31
+ ## The Create operation
32
+
33
+ Pragma provides a default implementation of the Create operation. Here's how it works:
34
+
35
+ 1. it builds a new instance of the model;
36
+ 2. it wraps the model in the Create contract;
37
+ 3. it validates and authorizes the contract;
38
+ 4. it saves the record;
39
+ 5. it responds with 201 Created and the decorated record.
40
+
41
+ To create a Create operation (pun intended), inherit from `Pragma::Operation::Create`:
42
+
43
+ ```ruby
44
+ module API
45
+ module V1
46
+ module Post
47
+ module Operation
48
+ class Create < Pragma::Operation::Create
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ ```
55
+
56
+ To override the defaults of this operation, have a look at the [source code](https://github.com/pragmarb/pragma/blob/master/lib/pragma/operation/create.rb).
57
+
58
+ ## The Update operation
59
+
60
+ Pragma provides a default implementation of the Update operation. Here's how it works:
61
+
62
+ 1. it finds an instance of the model by ID;
63
+ 2. it wraps the model in the Update contract;
64
+ 3. it validates and authorizes the contract;
65
+ 4. it saves the record;
66
+ 5. it responds with 200 OK and the decorated record.
67
+
68
+ To create an Update operation, inherit from `Pragma::Operation::Update`:
69
+
70
+ ```ruby
71
+ module API
72
+ module V1
73
+ module Post
74
+ module Operation
75
+ class Update < Pragma::Operation::Update
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ ```
82
+
83
+ To override the defaults of this operation, have a look at the [source code](https://github.com/pragmarb/pragma/blob/master/lib/pragma/operation/update.rb).
84
+
85
+ ## The Destroy operation
86
+
87
+ Pragma provides a default implementation of the Destroy operation. Here's how it works:
88
+
89
+ 1. it finds an instance of the model by ID;
90
+ 2. it authorizes the record;
91
+ 3. it destroys the record;
92
+ 4. it responds with 204 No Content.
93
+
94
+ To create a Destroy operation, inherit from `Pragma::Operation::Destroy`:
95
+
96
+ ```ruby
97
+ module API
98
+ module V1
99
+ module Post
100
+ module Operation
101
+ class Destroy < Pragma::Operation::Destroy
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ ```
108
+
109
+ To override the defaults of this operation, have a look at the [source code](https://github.com/pragmarb/pragma/blob/master/lib/pragma/operation/destroy.rb).
data/lib/pragma.rb ADDED
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ require 'pragma/operation'
3
+ require 'pragma/policy'
4
+ require 'pragma/contract'
5
+ require 'pragma/decorator'
6
+
7
+ require 'will_paginate'
8
+ require 'will_paginate/array'
9
+
10
+ require 'pragma/version'
11
+
12
+ require 'pragma/operation/defaults'
13
+ require 'pragma/operation/index'
14
+ require 'pragma/operation/show'
15
+ require 'pragma/operation/create'
16
+ require 'pragma/operation/update'
17
+ require 'pragma/operation/destroy'
18
+
19
+ # A pragmatic architecture for building JSON APIs.
20
+ #
21
+ # @author Alessandro Desantis
22
+ module Pragma
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ module Operation
4
+ # Finds the requested record, authorizes it, updates it accordingly to the parameters and
5
+ # responds with the decorated record.
6
+ #
7
+ # @author Alessandro Desantis
8
+ class Create < Pragma::Operation::Base
9
+ include Pragma::Operation::Defaults
10
+
11
+ def call
12
+ record = build_record
13
+ contract = build_contract(record)
14
+
15
+ validate! contract
16
+ authorize! contract
17
+
18
+ contract.save
19
+
20
+ respond_with resource: decorate(record)
21
+ end
22
+
23
+ protected
24
+
25
+ # Builds the requested record.
26
+ #
27
+ # @return [Object]
28
+ def build_record
29
+ self.class.model_klass.new
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ module Operation
4
+ # Provides support for inferring decorator, policy and contract names from the class name.
5
+ #
6
+ # @author Alessandro Desantis
7
+ module Defaults
8
+ def self.included(klass)
9
+ klass.extend ClassMethods
10
+ end
11
+
12
+ module ClassMethods # :nodoc:
13
+ # Returns the decorator class for the current resource (if the inferred class exists).
14
+ #
15
+ # If the operation name is +API::V1::Post::Operation::Show+, returns
16
+ # +API::V1::Post::Decorator+.
17
+ def decorator_klass
18
+ super || (computed_decorator_klass if class_exists?(computed_decorator_klass))
19
+ end
20
+
21
+ # Returns the policy class for the current resource (if the inferred class exists).
22
+ #
23
+ # If the operation name is +API::V1::Post::Operation::Show+, returns
24
+ # +API::V1::Post::Policy+.
25
+ def policy_klass
26
+ super || (computed_policy_klass if class_exists?(computed_policy_klass))
27
+ end
28
+
29
+ # Returns the contract class for the current resource (if the inferred class exists).
30
+ #
31
+ # If the operation name is +API::V1::Post::Operation::Create+, returns
32
+ # +API::V1::Post::Contract::Create+.
33
+ def contract_klass
34
+ super || (computed_contract_klass if class_exists?(computed_contract_klass))
35
+ end
36
+
37
+ # Returns the model class for the current resource (if the inferred class exists).
38
+ #
39
+ # If the operation name is +API::V1::Post::Operation::Create+, returns +::Post+.
40
+ def model_klass
41
+ Object.const_get("::#{name.split('::')[0..-3].last}")
42
+ end
43
+
44
+ private
45
+
46
+ def computed_decorator_klass
47
+ (name.split('::')[0..-3] << 'Decorator').join('::')
48
+ end
49
+
50
+ def computed_policy_klass
51
+ (name.split('::')[0..-3] << 'Policy').join('::')
52
+ end
53
+
54
+ def computed_contract_klass
55
+ name_parts = name.split('::')
56
+ (name_parts[0..-3] << 'Decorator' << name_parts.last).join('::')
57
+ end
58
+
59
+ def class_exists?(klass)
60
+ Object.const_get(klass)
61
+ true
62
+ rescue NameError
63
+ false
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ module Operation
4
+ # Finds the requested record, authorizes it and decorates it.
5
+ #
6
+ # @author Alessandro Desantis
7
+ class Destroy < Pragma::Operation::Base
8
+ include Pragma::Operation::Defaults
9
+
10
+ def call
11
+ record = find_record
12
+ authorize! record
13
+
14
+ record.destroy!
15
+
16
+ head :no_content
17
+ end
18
+
19
+ protected
20
+
21
+ # Finds the requested record.
22
+ #
23
+ # @return [Object]
24
+ def find_record
25
+ self.class.model_klass.find(params[:id])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ module Operation
4
+ # Finds all records of the requested resource, authorizes them, paginates them and returns
5
+ # the decorated collection.
6
+ #
7
+ # @author Alessandro Desantis
8
+ class Index < Pragma::Operation::Base
9
+ include Pragma::Operation::Defaults
10
+
11
+ def call
12
+ records = authorize_collection(find_records)
13
+ records = records.paginate(page: page, per_page: per_page)
14
+
15
+ respond_with(
16
+ resource: decorate(records),
17
+ headers: {
18
+ 'Page' => records.current_page.to_i,
19
+ 'Per-Page' => records.per_page,
20
+ 'Total' => records.total_entries
21
+ },
22
+ links: {
23
+ first: build_page_url(1),
24
+ last: build_page_url(records.total_pages),
25
+ next: (build_page_url(records.next_page) if records.next_page),
26
+ prev: (build_page_url(records.previous_page) if records.previous_page)
27
+ }
28
+ )
29
+ end
30
+
31
+ protected
32
+
33
+ # Finds all the records. By default, calls +.all+ on the model class, which is inferred from
34
+ # the operation's namespace (e.g. +API::V1::Post::Operation::Index+ will retrieve all records
35
+ # of the +Post+ model).
36
+ #
37
+ # @return [Enumerable]
38
+ def find_records
39
+ self.class.model_klass.all
40
+ end
41
+
42
+ # Returns the current page number. By default, this is the +page+ parameter or 1 if the
43
+ # parameter is not present.
44
+ #
45
+ # @return [Fixnum]
46
+ def page
47
+ return 1 if !params[:page] || params[:page].empty?
48
+ params[:page].to_i
49
+ end
50
+
51
+ # Returns the number of records to include per page. By default, this is the +per_page+
52
+ # parameter, up to a maximum of {#max_per_page} records, or {#default_per_page} if the
53
+ # parameter is not present.
54
+ #
55
+ # @return [Fixnum]
56
+ #
57
+ # @see #default_per_page
58
+ # @see #max_per_page
59
+ def per_page
60
+ return default_per_page if !params[:per_page] || params[:per_page].empty?
61
+ params[:per_page].to_i > max_per_page ? max_per_page : params[:per_page].to_i
62
+ end
63
+
64
+ # Returns the default number of records per page.
65
+ #
66
+ # @return [Fixnum]
67
+ def default_per_page
68
+ 30
69
+ end
70
+
71
+ # Returns the maximum number of records per page.
72
+ #
73
+ # @return [Fixnum]
74
+ def max_per_page
75
+ 100
76
+ end
77
+
78
+ # Builds the URL to a specific page in the collection.
79
+ #
80
+ # @param page [Fixnum] a page number
81
+ #
82
+ # @return [String]
83
+ def build_page_url(page)
84
+ context.page_url_builder ||= ->(_page) { nil }
85
+ context.page_url_builder.call(page)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ module Operation
4
+ # Finds the requested record, authorizes it and decorates it.
5
+ #
6
+ # @author Alessandro Desantis
7
+ class Show < Pragma::Operation::Base
8
+ include Pragma::Operation::Defaults
9
+
10
+ def call
11
+ record = find_record
12
+ authorize! record
13
+
14
+ respond_with resource: decorate(record)
15
+ end
16
+
17
+ protected
18
+
19
+ # Finds the requested record.
20
+ #
21
+ # @return [Object]
22
+ def find_record
23
+ self.class.model_klass.find(params[:id])
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ module Operation
4
+ # Finds the requested record, authorizes it, updates it accordingly to the parameters and
5
+ # responds with the decorated record.
6
+ #
7
+ # @author Alessandro Desantis
8
+ class Update < Pragma::Operation::Base
9
+ include Pragma::Operation::Defaults
10
+
11
+ def call
12
+ record = find_record
13
+ contract = build_contract(record)
14
+
15
+ validate! contract
16
+ authorize! contract
17
+
18
+ contract.save
19
+
20
+ respond_with resource: decorate(record)
21
+ end
22
+
23
+ protected
24
+
25
+ # Finds the requested record.
26
+ #
27
+ # @return [Object]
28
+ def find_record
29
+ self.class.model_klass.find(params[:id])
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ module Pragma
3
+ VERSION = '0.1.0'
4
+ end
data/pragma.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pragma/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'pragma'
8
+ spec.version = Pragma::VERSION
9
+ spec.authors = ['Alessandro Desantis']
10
+ spec.email = ['desa.alessandro@gmail.com']
11
+
12
+ spec.summary = 'A pragmatic architecture for building JSON APIs with Ruby.'
13
+ spec.homepage = 'https://github.com/pragmarb/pragma'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'pragma-operation', '~> 1.2.1'
24
+ spec.add_dependency 'pragma-policy', '~> 0.1.0'
25
+ spec.add_dependency 'pragma-contract', '~> 0.1.0'
26
+ spec.add_dependency 'pragma-decorator', '~> 0.1.0'
27
+ spec.add_dependency 'will_paginate', '~> 3.1.5'
28
+
29
+ spec.add_development_dependency 'bundler'
30
+ spec.add_development_dependency 'rake'
31
+ spec.add_development_dependency 'rspec'
32
+ spec.add_development_dependency 'rubocop'
33
+ spec.add_development_dependency 'rubocop-rspec'
34
+ spec.add_development_dependency 'coveralls'
35
+ end
metadata ADDED
@@ -0,0 +1,220 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pragma
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alessandro Desantis
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pragma-operation
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: pragma-policy
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.1.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.1.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: pragma-contract
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pragma-decorator
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: will_paginate
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.1.5
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.1.5
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: rubocop-rspec
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: coveralls
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description:
168
+ email:
169
+ - desa.alessandro@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".gitignore"
175
+ - ".rspec"
176
+ - ".rubocop.yml"
177
+ - ".travis.yml"
178
+ - CHANGELOG.md
179
+ - Gemfile
180
+ - LICENSE.txt
181
+ - README.md
182
+ - Rakefile
183
+ - bin/console
184
+ - bin/setup
185
+ - doc/01-sensible-defaults.md
186
+ - doc/02-crud-operations.md
187
+ - lib/pragma.rb
188
+ - lib/pragma/operation/create.rb
189
+ - lib/pragma/operation/defaults.rb
190
+ - lib/pragma/operation/destroy.rb
191
+ - lib/pragma/operation/index.rb
192
+ - lib/pragma/operation/show.rb
193
+ - lib/pragma/operation/update.rb
194
+ - lib/pragma/version.rb
195
+ - pragma.gemspec
196
+ homepage: https://github.com/pragmarb/pragma
197
+ licenses:
198
+ - MIT
199
+ metadata: {}
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ required_rubygems_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ requirements: []
215
+ rubyforge_project:
216
+ rubygems_version: 2.5.2
217
+ signing_key:
218
+ specification_version: 4
219
+ summary: A pragmatic architecture for building JSON APIs with Ruby.
220
+ test_files: []