jwtbuilder 0.0.1 → 2.2.12.jwt
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.travis.yml +28 -0
- data/Appraisals +32 -0
- data/CHANGELOG.md +174 -0
- data/Gemfile +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +260 -0
- data/Rakefile +23 -0
- data/gemfiles/rails_3_0.gemfile +12 -0
- data/gemfiles/rails_3_1.gemfile +12 -0
- data/gemfiles/rails_3_2.gemfile +12 -0
- data/gemfiles/rails_4_0.gemfile +11 -0
- data/gemfiles/rails_4_1.gemfile +11 -0
- data/gemfiles/rails_4_2.gemfile +11 -0
- data/jbuilder.gemspec +18 -0
- data/lib/generators/rails/jbuilder_generator.rb +48 -0
- data/lib/generators/rails/scaffold_controller_generator.rb +12 -0
- data/lib/generators/rails/templates/controller.rb +84 -0
- data/lib/generators/rails/templates/index.json.jbuilder +5 -0
- data/lib/generators/rails/templates/show.json.jbuilder +3 -0
- data/lib/jbuilder/blank.rb +11 -0
- data/lib/jbuilder/dependency_tracker.rb +61 -0
- data/lib/jbuilder/errors.rb +17 -0
- data/lib/jbuilder/jbuilder.rb +7 -0
- data/lib/jbuilder/jbuilder_template.rb +145 -0
- data/lib/jbuilder/key_formatter.rb +34 -0
- data/lib/jbuilder/railtie.rb +21 -0
- data/lib/jbuilder.rb +313 -0
- data/test/jbuilder_dependency_tracker_test.rb +72 -0
- data/test/jbuilder_generator_test.rb +32 -0
- data/test/jbuilder_template_test.rb +343 -0
- data/test/jbuilder_test.rb +686 -0
- data/test/scaffold_controller_generator_test.rb +57 -0
- data/test/test_helper.rb +14 -0
- metadata +48 -8
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rails/generators/named_base'
|
2
|
+
require 'rails/generators/resource_helpers'
|
3
|
+
|
4
|
+
module Rails
|
5
|
+
module Generators
|
6
|
+
class JbuilderGenerator < NamedBase # :nodoc:
|
7
|
+
include Rails::Generators::ResourceHelpers
|
8
|
+
|
9
|
+
source_root File.expand_path('../templates', __FILE__)
|
10
|
+
|
11
|
+
argument :attributes, type: :array, default: [], banner: 'field:type field:type'
|
12
|
+
|
13
|
+
def create_root_folder
|
14
|
+
path = File.join('app/views', controller_file_path)
|
15
|
+
empty_directory path unless File.directory?(path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_view_files
|
19
|
+
%w(index show).each do |view|
|
20
|
+
filename = filename_with_extensions(view)
|
21
|
+
template filename, File.join('app/views', controller_file_path, filename)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
protected
|
27
|
+
def attributes_names
|
28
|
+
[:id] + super
|
29
|
+
end
|
30
|
+
|
31
|
+
def filename_with_extensions(name)
|
32
|
+
[name, :json, :jbuilder] * '.'
|
33
|
+
end
|
34
|
+
|
35
|
+
def attributes_list_with_timestamps
|
36
|
+
attributes_list(attributes_names + %w(created_at updated_at))
|
37
|
+
end
|
38
|
+
|
39
|
+
def attributes_list(attributes = attributes_names)
|
40
|
+
if self.attributes.any? {|attr| attr.name == 'password' && attr.type == :digest}
|
41
|
+
attributes = attributes.reject {|name| %w(password password_confirmation).include? name}
|
42
|
+
end
|
43
|
+
|
44
|
+
attributes.map { |a| ":#{a}"} * ', '
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
|
3
|
+
|
4
|
+
module Rails
|
5
|
+
module Generators
|
6
|
+
class ScaffoldControllerGenerator
|
7
|
+
source_root File.expand_path('../templates', __FILE__)
|
8
|
+
|
9
|
+
hook_for :jbuilder, default: true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
<% if namespaced? -%>
|
2
|
+
require_dependency "<%= namespaced_file_path %>/application_controller"
|
3
|
+
|
4
|
+
<% end -%>
|
5
|
+
<% module_namespacing do -%>
|
6
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
7
|
+
before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy]
|
8
|
+
|
9
|
+
# GET <%= route_url %>
|
10
|
+
# GET <%= route_url %>.json
|
11
|
+
def index
|
12
|
+
@<%= plural_table_name %> = <%= orm_class.all(class_name) %>
|
13
|
+
end
|
14
|
+
|
15
|
+
# GET <%= route_url %>/1
|
16
|
+
# GET <%= route_url %>/1.json
|
17
|
+
def show
|
18
|
+
end
|
19
|
+
|
20
|
+
# GET <%= route_url %>/new
|
21
|
+
def new
|
22
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name) %>
|
23
|
+
end
|
24
|
+
|
25
|
+
# GET <%= route_url %>/1/edit
|
26
|
+
def edit
|
27
|
+
end
|
28
|
+
|
29
|
+
# POST <%= route_url %>
|
30
|
+
# POST <%= route_url %>.json
|
31
|
+
def create
|
32
|
+
@<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
|
33
|
+
|
34
|
+
respond_to do |format|
|
35
|
+
if @<%= orm_instance.save %>
|
36
|
+
format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> }
|
37
|
+
format.json { render :show, status: :created, location: <%= "@#{singular_table_name}" %> }
|
38
|
+
else
|
39
|
+
format.html { render :new }
|
40
|
+
format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# PATCH/PUT <%= route_url %>/1
|
46
|
+
# PATCH/PUT <%= route_url %>/1.json
|
47
|
+
def update
|
48
|
+
respond_to do |format|
|
49
|
+
if @<%= orm_instance.update("#{singular_table_name}_params") %>
|
50
|
+
format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> }
|
51
|
+
format.json { render :show, status: :ok, location: <%= "@#{singular_table_name}" %> }
|
52
|
+
else
|
53
|
+
format.html { render :edit }
|
54
|
+
format.json { render json: <%= "@#{orm_instance.errors}" %>, status: :unprocessable_entity }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# DELETE <%= route_url %>/1
|
60
|
+
# DELETE <%= route_url %>/1.json
|
61
|
+
def destroy
|
62
|
+
@<%= orm_instance.destroy %>
|
63
|
+
respond_to do |format|
|
64
|
+
format.html { redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> }
|
65
|
+
format.json { head :no_content }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
# Use callbacks to share common setup or constraints between actions.
|
71
|
+
def set_<%= singular_table_name %>
|
72
|
+
@<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %>
|
73
|
+
end
|
74
|
+
|
75
|
+
# Never trust parameters from the scary internet, only allow the white list through.
|
76
|
+
def <%= "#{singular_table_name}_params" %>
|
77
|
+
<%- if attributes_names.empty? -%>
|
78
|
+
params[<%= ":#{singular_table_name}" %>]
|
79
|
+
<%- else -%>
|
80
|
+
params.require(<%= ":#{singular_table_name}" %>).permit(<%= attributes_names.map { |name| ":#{name}" }.join(', ') %>)
|
81
|
+
<%- end -%>
|
82
|
+
end
|
83
|
+
end
|
84
|
+
<% end -%>
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'jbuilder/jbuilder'
|
2
|
+
|
3
|
+
dependency_tracker = false
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'action_view'
|
7
|
+
require 'action_view/dependency_tracker'
|
8
|
+
dependency_tracker = ::ActionView::DependencyTracker
|
9
|
+
rescue LoadError
|
10
|
+
begin
|
11
|
+
require 'cache_digests'
|
12
|
+
dependency_tracker = ::CacheDigests::DependencyTracker
|
13
|
+
rescue LoadError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if dependency_tracker
|
18
|
+
class Jbuilder
|
19
|
+
module DependencyTrackerMethods
|
20
|
+
# Matches:
|
21
|
+
# json.partial! "messages/message"
|
22
|
+
# json.partial!('messages/message')
|
23
|
+
#
|
24
|
+
DIRECT_RENDERS = /
|
25
|
+
\w+\.partial! # json.partial!
|
26
|
+
\(?\s* # optional parenthesis
|
27
|
+
(['"])([^'"]+)\1 # quoted value
|
28
|
+
/x
|
29
|
+
|
30
|
+
# Matches:
|
31
|
+
# json.partial! partial: "comments/comment"
|
32
|
+
# json.comments @post.comments, partial: "comments/comment", as: :comment
|
33
|
+
# json.array! @posts, partial: "posts/post", as: :post
|
34
|
+
# = render partial: "account"
|
35
|
+
#
|
36
|
+
INDIRECT_RENDERS = /
|
37
|
+
(?::partial\s*=>|partial:) # partial: or :partial =>
|
38
|
+
\s* # optional whitespace
|
39
|
+
(['"])([^'"]+)\1 # quoted value
|
40
|
+
/x
|
41
|
+
|
42
|
+
def dependencies
|
43
|
+
direct_dependencies + indirect_dependencies + explicit_dependencies
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def direct_dependencies
|
49
|
+
source.scan(DIRECT_RENDERS).map(&:second)
|
50
|
+
end
|
51
|
+
|
52
|
+
def indirect_dependencies
|
53
|
+
source.scan(INDIRECT_RENDERS).map(&:second)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
::Jbuilder::DependencyTracker = Class.new(dependency_tracker::ERBTracker)
|
59
|
+
::Jbuilder::DependencyTracker.send :include, ::Jbuilder::DependencyTrackerMethods
|
60
|
+
dependency_tracker.register_tracker :jbuilder, ::Jbuilder::DependencyTracker
|
61
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'jbuilder/jbuilder'
|
2
|
+
|
3
|
+
class Jbuilder
|
4
|
+
class NullError < ::NoMethodError
|
5
|
+
def self.build(key)
|
6
|
+
message = "Failed to add #{key.to_s.inspect} property to null object"
|
7
|
+
new(message)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ArrayError < ::StandardError
|
12
|
+
def self.build(key)
|
13
|
+
message = "Failed to add #{key.to_s.inspect} property to an array"
|
14
|
+
new(message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'jbuilder/jbuilder'
|
2
|
+
require 'action_dispatch/http/mime_type'
|
3
|
+
require 'active_support/cache'
|
4
|
+
|
5
|
+
class JbuilderTemplate < Jbuilder
|
6
|
+
class << self
|
7
|
+
attr_accessor :template_lookup_options
|
8
|
+
end
|
9
|
+
|
10
|
+
self.template_lookup_options = { handlers: [:jbuilder] }
|
11
|
+
|
12
|
+
def initialize(context, *args)
|
13
|
+
@context = context
|
14
|
+
super(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def partial!(name_or_options, locals = {})
|
18
|
+
case name_or_options
|
19
|
+
when ::Hash
|
20
|
+
# partial! partial: 'name', foo: 'bar'
|
21
|
+
options = name_or_options
|
22
|
+
else
|
23
|
+
# partial! 'name', locals: {foo: 'bar'}
|
24
|
+
if locals.one? && (locals.keys.first == :locals)
|
25
|
+
options = locals.merge(partial: name_or_options)
|
26
|
+
else
|
27
|
+
options = { partial: name_or_options, locals: locals }
|
28
|
+
end
|
29
|
+
# partial! 'name', foo: 'bar'
|
30
|
+
as = locals.delete(:as)
|
31
|
+
options[:as] = as if as.present?
|
32
|
+
options[:collection] = locals[:collection] if locals.key?(:collection)
|
33
|
+
end
|
34
|
+
|
35
|
+
_render_partial_with_options options
|
36
|
+
end
|
37
|
+
|
38
|
+
def array!(collection = [], *attributes)
|
39
|
+
options = attributes.extract_options!
|
40
|
+
|
41
|
+
if options.key?(:partial)
|
42
|
+
partial! options[:partial], options.merge(collection: collection)
|
43
|
+
else
|
44
|
+
super
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Caches the json constructed within the block passed. Has the same signature as the `cache` helper
|
49
|
+
# method in `ActionView::Helpers::CacheHelper` and so can be used in the same way.
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
#
|
53
|
+
# json.cache! ['v1', @person], expires_in: 10.minutes do
|
54
|
+
# json.extract! @person, :name, :age
|
55
|
+
# end
|
56
|
+
def cache!(key=nil, options={})
|
57
|
+
if @context.controller.perform_caching
|
58
|
+
value = ::Rails.cache.fetch(_cache_key(key, options), options) do
|
59
|
+
_scope { yield self }
|
60
|
+
end
|
61
|
+
|
62
|
+
merge! value
|
63
|
+
else
|
64
|
+
yield
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Conditionally catches the json depending in the condition given as first parameter. Has the same
|
69
|
+
# signature as the `cache` helper method in `ActionView::Helpers::CacheHelper` and so can be used in
|
70
|
+
# the same way.
|
71
|
+
#
|
72
|
+
# Example:
|
73
|
+
#
|
74
|
+
# json.cache_if! !admin?, @person, expires_in: 10.minutes do
|
75
|
+
# json.extract! @person, :name, :age
|
76
|
+
# end
|
77
|
+
def cache_if!(condition, *args)
|
78
|
+
condition ? cache!(*args, &::Proc.new) : yield
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
def _render_partial_with_options(options)
|
84
|
+
options.reverse_merge! locals: {}
|
85
|
+
options.reverse_merge! ::JbuilderTemplate.template_lookup_options
|
86
|
+
as = options[:as]
|
87
|
+
|
88
|
+
if as && options.key?(:collection)
|
89
|
+
as = as.to_sym
|
90
|
+
collection = options.delete(:collection)
|
91
|
+
locals = options.delete(:locals)
|
92
|
+
array! collection do |member|
|
93
|
+
member_locals = locals.clone
|
94
|
+
member_locals.merge! collection: collection
|
95
|
+
member_locals.merge! as => member
|
96
|
+
_render_partial options.merge(locals: member_locals)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
_render_partial options
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def _render_partial(options)
|
104
|
+
options[:locals].merge! json: self
|
105
|
+
@context.render options
|
106
|
+
end
|
107
|
+
|
108
|
+
def _cache_key(key, options)
|
109
|
+
key = _fragment_name_with_digest(key, options)
|
110
|
+
key = url_for(key).split('://', 2).last if ::Hash === key
|
111
|
+
::ActiveSupport::Cache.expand_cache_key(key, :jbuilder)
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def _fragment_name_with_digest(key, options)
|
117
|
+
if @context.respond_to?(:cache_fragment_name)
|
118
|
+
# Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
|
119
|
+
# should be used instead.
|
120
|
+
@context.cache_fragment_name(key, options)
|
121
|
+
elsif @context.respond_to?(:fragment_name_with_digest)
|
122
|
+
# Backwards compatibility for period of time when fragment_name_with_digest was made public.
|
123
|
+
@context.fragment_name_with_digest(key)
|
124
|
+
else
|
125
|
+
key
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def _mapable_arguments?(value, *args)
|
130
|
+
return true if super
|
131
|
+
options = args.last
|
132
|
+
::Hash === options && options.key?(:as)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class JbuilderHandler
|
137
|
+
cattr_accessor :default_format
|
138
|
+
self.default_format = Mime::JSON
|
139
|
+
|
140
|
+
def self.call(template)
|
141
|
+
# this juggling is required to keep line numbers right in the error
|
142
|
+
%{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{template.source}
|
143
|
+
json.target! unless (__already_defined && __already_defined != "method")}
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'jbuilder/jbuilder'
|
2
|
+
require 'active_support/core_ext/array'
|
3
|
+
|
4
|
+
class Jbuilder
|
5
|
+
class KeyFormatter
|
6
|
+
def initialize(*args)
|
7
|
+
@format = {}
|
8
|
+
@cache = {}
|
9
|
+
|
10
|
+
options = args.extract_options!
|
11
|
+
args.each do |name|
|
12
|
+
@format[name] = []
|
13
|
+
end
|
14
|
+
options.each do |name, paramaters|
|
15
|
+
@format[name] = paramaters
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize_copy(original)
|
20
|
+
@cache = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def format(key)
|
24
|
+
@cache[key] ||= @format.inject(key.to_s) do |result, args|
|
25
|
+
func, args = args
|
26
|
+
if ::Proc === func
|
27
|
+
func.call result, *args
|
28
|
+
else
|
29
|
+
result.send func, *args
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rails/railtie'
|
2
|
+
require 'jbuilder/jbuilder_template'
|
3
|
+
|
4
|
+
class Jbuilder
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
initializer :jbuilder do |app|
|
7
|
+
ActiveSupport.on_load :action_view do
|
8
|
+
ActionView::Template.register_template_handler :jbuilder, JbuilderHandler
|
9
|
+
require 'jbuilder/dependency_tracker'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
if Rails::VERSION::MAJOR >= 4
|
14
|
+
generators do |app|
|
15
|
+
Rails::Generators.configure! app.config.generators
|
16
|
+
Rails::Generators.hidden_namespaces.uniq!
|
17
|
+
require 'generators/rails/scaffold_controller_generator'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|