rails-rfc6570 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0011c2277baf555923c718d4a01115c09b782073
4
+ data.tar.gz: 1d1bb93a9d2d0532c9d2701199ef56e6825cd385
5
+ SHA512:
6
+ metadata.gz: ff0b022b561c859014393657fd794529c58f182db2994df60803b4eb978244f125a47640c01092297a34cf7b2d4cc1236769d6ab9bb9fe8c6b1d8cc3f4b3e62a
7
+ data.tar.gz: 2c9a03ee10bd4d793a275e661434b8edd7aa3a1b4c40e501b02fdd963dfb06ed503abf4c6c8f6f91da580801977e094b94e728ae6a7fa255a18d922e46455aad
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jan Graichen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,119 @@
1
+ # Rails::RFC6570
2
+
3
+ Pragmatical access to your Rails routes as RFC6570 URI templates.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rails-rfc6570', '~> 0.1'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rails-rfc6570
18
+
19
+ ## Usage
20
+
21
+ **Rails::RFC6570** gives you direct access to your Rails routes as RFC6570 URI templates using the [addressable](https://github.com/sporkmonger/addressable) gem. It further patches `Addressable::Template` with a `#as_json` and `#to_s` so that you can simply pass the template objects or even partial expanded templates to your render call, decorator or serializer.
22
+
23
+ This examples print a JSON index resource just like https://api.github.com:
24
+
25
+ ```ruby
26
+ class ApplicationController < ActionController::API
27
+ def index
28
+ render json: rfc6570_routes(ignore: %w(format), path_only: false)
29
+ end
30
+ end
31
+ ```
32
+
33
+ By default the `format` placeholder is ignored and the HTTP host will be included in the URI template.
34
+
35
+ Additionally you can specify a list of query parameters in your controllers:
36
+
37
+ ```ruby
38
+ class UserController < ApplicationController
39
+
40
+ rfc6570_params index: [:query, :email, :active]
41
+ def index
42
+ # ...
43
+ end
44
+
45
+ def show
46
+ # ...
47
+ end
48
+
49
+ # ...
50
+ end
51
+ ```
52
+
53
+ Given the above and this routes
54
+
55
+ ```ruby
56
+ Rails::Application.routes.draw do
57
+ resources :users, except: [:new, :edit]
58
+ root to: 'application#index'
59
+ end
60
+ ```
61
+
62
+ the root action will return something similar to the following JSON:
63
+
64
+ ```json
65
+ {
66
+ users: "http://localhost:3000/users{?query,email,active}",
67
+ user: "http://localhost:3000/users/{id}",
68
+ root: "http://localhost:3000/"
69
+ }
70
+ ```
71
+
72
+ You can also access your RFC6570 routes pragmatically everywhere you can access Rails' URL helpers e.g. in a decorator.
73
+
74
+ You can use this to e.g. partial expand templates for nested resources:
75
+
76
+ ```ruby
77
+ module ApplicationHelpers
78
+ include Rails.application.routes.url_helpers
79
+ end
80
+
81
+ class UserDecorator < Draper::Decorator
82
+ def as_json(opts)
83
+ {
84
+ id: object.id,
85
+ self_url: user_url(object),
86
+ posts_url: user_posts_rfc6570.partial_expand(user_id: object.id),
87
+ }
88
+ end
89
+ end
90
+ ```
91
+
92
+ You can also combine **Rails::RFC6570** with [rack-link_headers](https://jgraichen/rack-link_headers) and provide Hypermedia-linking everywhere!
93
+
94
+ ```ruby
95
+ class UserController < ApplicationController
96
+ respond_to :json
97
+
98
+ def show
99
+ @user = User.find
100
+ response.link user_url(@user), rel: :self
101
+ response.link user_posts_rfc6570.partial_expand(user_id: @user.id), rel: :posts
102
+ response.link profile_rfc6570.expand(user_id: @user.id), rel: :profile
103
+
104
+ respond_with @user
105
+ end
106
+ end
107
+ ```
108
+
109
+ ## ToDos
110
+
111
+ * Still has *no* tests.
112
+
113
+ ## Contributing
114
+
115
+ 1. Fork it (http://github.com/jgraichen/rails-routes/fork)
116
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
117
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
118
+ 4. Push to the branch (`git push origin my-new-feature`)
119
+ 5. Create new Pull Request
@@ -0,0 +1,186 @@
1
+ require 'rails/routes/version'
2
+
3
+ module ActionDispatch
4
+ module Journey
5
+ module Visitors
6
+ class RFC6570 < String
7
+ def initialize(opts = {})
8
+ super()
9
+
10
+ @opts = opts
11
+ @group_depth = 0
12
+ end
13
+
14
+ def ignore
15
+ @opts.fetch(:ignore) { %w(format) }
16
+ end
17
+
18
+ def route
19
+ @route ||= @opts[:route]
20
+ end
21
+
22
+ def accept(node)
23
+ str = super
24
+
25
+ if @opts.fetch(:params, true) && route
26
+ controller = route.defaults[:controller].to_s
27
+ action = route.defaults[:action].to_s
28
+
29
+ if controller.present? && action.present?
30
+ params = Rails::Routes.params_for(controller, action)
31
+ str += '{?' + params.join(',') + '}' if params && params.any?
32
+ end
33
+ end
34
+
35
+ str
36
+ end
37
+
38
+ def symbol_name(node)
39
+ name = node.to_s.tr '*:', ''
40
+
41
+ if ignore.include?(name)
42
+ nil
43
+ else
44
+ name
45
+ end
46
+ end
47
+
48
+ def placeholder(node, prefix = nil, suffix = nil)
49
+ name = symbol_name node
50
+ if name
51
+ "{#{prefix}#{name}#{suffix}}"
52
+ else
53
+ ''
54
+ end
55
+ end
56
+
57
+ def visit_SYMBOL(node)
58
+ placeholder node
59
+ end
60
+
61
+ def binary(node)
62
+ case [node.left.type, node.right.type]
63
+ when [:DOT, :SYMBOL]
64
+ if @group_depth == 0
65
+ '.' + placeholder(node.right)
66
+ else
67
+ placeholder node.right, '.'
68
+ end
69
+ when [:SLASH, :SYMBOL]
70
+ if @group_depth == 0
71
+ '/' + placeholder(node.right)
72
+ else
73
+ placeholder(node.right, '/')
74
+ end
75
+ when [:SLASH, :STAR]
76
+ placeholder node.right, '/', '*'
77
+ when [:CAT, :STAR]
78
+ visit(node.left).to_s.gsub(/\/+$/, '') + placeholder(node.right, '/', '*')
79
+ else
80
+ super
81
+ end
82
+ end
83
+
84
+ def visit_GROUP(node)
85
+ if @group_depth >= 1
86
+ raise RuntimeError.new \
87
+ 'Cannot transform nested groups.'
88
+ else
89
+ @group_depth += 1
90
+ visit node.left
91
+ end
92
+ ensure
93
+ @group_depth -= 1
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ module Rails
101
+ module RFC6570
102
+ if defined?(::Rails::Railtie)
103
+ class Railtie < Rails::Railtie # :nodoc:
104
+ initializer 'rails-routes', :group => :all do |app|
105
+ require 'rails/routes/patches'
106
+
107
+ ActiveSupport.on_load(:action_controller) do
108
+ include Rails::RFC6570::Helper
109
+ extend Rails::RFC6570::ControllerExtension
110
+ Rails.application.routes.url_helpers.include Rails::RFC6570::UrlHelper
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ module Helper
117
+ def rfc6570_routes(opts = {})
118
+ routes = {}
119
+ Rails.application.routes.named_routes.names.each do |key|
120
+ routes[key] = rfc6570_route(key, opts)
121
+ end
122
+
123
+ routes
124
+ end
125
+
126
+ def rfc6570_route(name, opts = {})
127
+ route = Rails.application.routes.named_routes[name]
128
+ unless route
129
+ raise KeyError.new "No named routed for `#{name}'."
130
+ end
131
+
132
+ template = route.to_rfc6570(opts)
133
+
134
+ if opts.fetch(:path_only, false)
135
+ template
136
+ else
137
+ root_uri = Addressable::URI.parse(root_url)
138
+
139
+ Addressable::Template.new root_uri.join(template.pattern).to_s
140
+ end
141
+ end
142
+ end
143
+
144
+ module ControllerExtension
145
+ def rfc6570_defs
146
+ @__rfc6570_defs ||= {}
147
+ end
148
+
149
+ def rfc6570_params(defs)
150
+ rfc6570_defs.merge! defs
151
+ end
152
+
153
+ def rfc6570_params_for(defs)
154
+ rfc6570_defs[defs]
155
+ end
156
+ end
157
+
158
+ module UrlHelper
159
+ include Rails::RFC6570::Helper
160
+
161
+ def respond_to_missing?(mth, include_private = false)
162
+ if mth =~ /^(\w+)_rfc6570$/
163
+ Rails.application.routes.named_routes.names.include?($1)
164
+ else
165
+ super
166
+ end
167
+ end
168
+
169
+ def method_missing(mth, *args, &block)
170
+ if mth =~ /^(\w+)_rfc6570$/
171
+ rails_route $1
172
+ else
173
+ super
174
+ end
175
+ end
176
+ end
177
+
178
+ def params_for(controller, action)
179
+ ctr = "#{controller.camelize}Controller".constantize
180
+ ctr.rfc6570_defs[action.to_sym] if ctr.respond_to?(:rfc6570_defs)
181
+ rescue NameError
182
+ nil
183
+ end
184
+ extend self
185
+ end
186
+ end
@@ -0,0 +1,45 @@
1
+ require 'action_dispatch'
2
+ require 'action_dispatch/journey'
3
+ require 'addressable/template'
4
+
5
+ module Addressable
6
+ class Template
7
+ def to_s
8
+ pattern
9
+ end
10
+
11
+ def as_json(*)
12
+ pattern
13
+ end
14
+ end
15
+ end
16
+
17
+ module ActionDispatch
18
+ module Routing
19
+ class RouteSet
20
+ def to_rfc6570(opts = {})
21
+ routes.map{|r| r.to_rfc6570(opts) }
22
+ end
23
+
24
+ class NamedRouteCollection
25
+ def to_rfc6570(opts = {})
26
+ Hash[routes.map{|n, r| [n, r.to_rfc6570(opts)] }]
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ module Journey
33
+ class Route
34
+ def to_rfc6570(opts = {})
35
+ path.spec.to_rfc6570 opts.merge(route: self)
36
+ end
37
+ end
38
+
39
+ class Nodes::Node
40
+ def to_rfc6570(opts = {})
41
+ ::Addressable::Template.new Visitors::RFC6570.new(opts).accept(self)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ module Rails
2
+ module RFC6570
3
+ module VERSION
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ PATCH = 0
7
+ STAGE = nil
8
+
9
+ STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.').freeze
10
+
11
+ def self.to_s
12
+ STRING
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rails/rfc6570/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rails-rfc6570'
8
+ spec.version = Rails::RFC6570::VERSION
9
+ spec.authors = ['Jan Graichen']
10
+ spec.email = ['jg@altimos.de']
11
+ spec.summary = %q(Pragmatical access to your Rails routes as RFC6570 URI templates.)
12
+ spec.description = %q(Pragmatical access to your Rails routes as RFC6570 URI templates.)
13
+ spec.homepage = 'https://github.com/jgraichen/rails-rfc6570'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = Dir['**/*'].grep(/^(
17
+ (bin|lib|test|spec|features)\/|
18
+ (.*\.gemspec|.*LICENSE.*|.*README.*|.*CHANGELOG.*)
19
+ )/x)
20
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_runtime_dependency 'addressable', '~> 2.3'
25
+
26
+ spec.add_development_dependency 'bundler', '~> 1.5'
27
+ spec.add_development_dependency 'rake'
28
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rails-rfc6570
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jan Graichen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Pragmatical access to your Rails routes as RFC6570 URI templates.
56
+ email:
57
+ - jg@altimos.de
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - LICENSE.txt
63
+ - README.md
64
+ - lib/rails/rfc6570.rb
65
+ - lib/rails/rfc6570/patches.rb
66
+ - lib/rails/rfc6570/version.rb
67
+ - rails-rfc6570.gemspec
68
+ homepage: https://github.com/jgraichen/rails-rfc6570
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project:
88
+ rubygems_version: 2.2.2
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Pragmatical access to your Rails routes as RFC6570 URI templates.
92
+ test_files: []