pragma 0.1.0

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