rabl-rails 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -2
- data/CHANGELOG.md +10 -0
- data/Gemfile +5 -9
- data/README.md +5 -3
- data/Rakefile +2 -2
- data/lib/rabl-rails.rb +21 -74
- data/lib/rabl-rails/compiler.rb +28 -38
- data/lib/rabl-rails/configuration.rb +48 -0
- data/lib/rabl-rails/handler.rb +3 -1
- data/lib/rabl-rails/helpers.rb +7 -0
- data/lib/rabl-rails/library.rb +43 -16
- data/lib/rabl-rails/nodes.rb +6 -0
- data/lib/rabl-rails/nodes/attribute.rb +17 -0
- data/lib/rabl-rails/nodes/child.rb +12 -0
- data/lib/rabl-rails/nodes/code.rb +19 -0
- data/lib/rabl-rails/nodes/condition.rb +14 -0
- data/lib/rabl-rails/nodes/glue.rb +25 -0
- data/lib/rabl-rails/nodes/node.rb +9 -0
- data/lib/rabl-rails/railtie.rb +0 -2
- data/lib/rabl-rails/renderer.rb +15 -13
- data/lib/rabl-rails/renderers/hash.rb +85 -0
- data/lib/rabl-rails/renderers/json.rb +9 -5
- data/lib/rabl-rails/renderers/plist.rb +6 -4
- data/lib/rabl-rails/renderers/xml.rb +6 -3
- data/lib/rabl-rails/responder.rb +1 -1
- data/lib/rabl-rails/template.rb +11 -5
- data/lib/rabl-rails/version.rb +1 -1
- data/lib/rabl-rails/visitors.rb +2 -0
- data/lib/rabl-rails/visitors/to_hash.rb +131 -0
- data/lib/rabl-rails/visitors/visitor.rb +17 -0
- data/rabl-rails.gemspec +3 -5
- data/test/helper.rb +75 -0
- data/test/renderers/test_hash_renderer.rb +90 -0
- data/test/renderers/test_json_renderer.rb +46 -0
- data/test/renderers/test_plist_renderer.rb +42 -0
- data/test/renderers/test_xml_renderer.rb +37 -0
- data/test/test_compiler.rb +283 -0
- data/test/test_configuration.rb +31 -0
- data/test/test_hash_visitor.rb +224 -0
- data/test/test_library.rb +85 -0
- data/test/{render_test.rb → test_render.rb} +18 -24
- metadata +99 -108
- data/lib/rabl-rails/condition.rb +0 -10
- data/lib/rabl-rails/renderers/base.rb +0 -171
- data/test/base_renderer_test.rb +0 -67
- data/test/cache_templates_test.rb +0 -35
- data/test/compiler_test.rb +0 -233
- data/test/deep_nesting_test.rb +0 -56
- data/test/keyword_test.rb +0 -47
- data/test/non_restful_response_test.rb +0 -35
- data/test/renderers/json_renderer_test.rb +0 -189
- data/test/renderers/plist_renderer_test.rb +0 -135
- data/test/renderers/xml_renderer_test.rb +0 -137
- data/test/test_helper.rb +0 -68
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 0.4.0
|
4
|
+
* Internal cleanup and refactor
|
5
|
+
* Remove the `allow_empty_format_in_template` option, since it has become
|
6
|
+
the default behavior.
|
7
|
+
* Remove multi_json dependency
|
8
|
+
* New options available
|
9
|
+
* replace_nil_values_with_empty_strings
|
10
|
+
* replace_empty_string_values_with_nil
|
11
|
+
* exclude_nil_values
|
12
|
+
|
3
13
|
## 0.3.4
|
4
14
|
* Add `xml_options` option to root_level (brettallred)
|
5
15
|
|
data/Gemfile
CHANGED
@@ -6,18 +6,20 @@ rails_version = ENV['RAILS_VERSION'] || 'default'
|
|
6
6
|
|
7
7
|
rails = case rails_version
|
8
8
|
when 'master'
|
9
|
-
{github: 'rails/rails'}
|
9
|
+
{ github: 'rails/rails' }
|
10
10
|
when "default"
|
11
11
|
'~> 3.2.0'
|
12
12
|
else
|
13
13
|
"~> #{rails_version}"
|
14
14
|
end
|
15
15
|
|
16
|
+
minitest_version = rails_version == '4.0.0' ? '~> 4.7' : '~> 5.4'
|
17
|
+
|
16
18
|
gem 'activesupport', rails
|
17
19
|
gem 'railties', rails
|
18
20
|
|
19
|
-
group :
|
20
|
-
gem '
|
21
|
+
group :test do
|
22
|
+
gem 'minitest', minitest_version
|
21
23
|
end
|
22
24
|
|
23
25
|
gem 'plist'
|
@@ -34,9 +36,3 @@ platforms :jruby do
|
|
34
36
|
gem 'nokogiri'
|
35
37
|
end
|
36
38
|
|
37
|
-
platforms :rbx do
|
38
|
-
gem 'rubysl', '~> 2.0'
|
39
|
-
gem 'minitest'
|
40
|
-
gem 'rubysl-test-unit'
|
41
|
-
end
|
42
|
-
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ RABL (Ruby API Builder Language) is a ruby templating system for rendering resou
|
|
4
4
|
|
5
5
|
rabl-rails is **faster** and uses **less memory** than the standard rabl gem while letting you access the same features. There are some slight changes to do on your templates to get this gem to work but it should't take you more than 5 minutes.
|
6
6
|
|
7
|
-
rabl-rails only targets **Rails 3+ application** and is compatible with mri 1.9.3
|
7
|
+
rabl-rails only targets **Rails 3.2+ application** and is compatible with mri 1.9.3+, jRuby and rubinius.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
@@ -91,12 +91,14 @@ RablRails.configure do |config|
|
|
91
91
|
# These are the default
|
92
92
|
# config.cache_templates = true
|
93
93
|
# config.include_json_root = true
|
94
|
-
# config.json_engine =
|
95
|
-
# config.xml_engine = 'LibXML'
|
94
|
+
# config.json_engine = ::Oj
|
96
95
|
# config.xml_options = { :dasherize => true, :skip_types => false }
|
97
96
|
# config.use_custom_responder = false
|
98
97
|
# config.default_responder_template = 'show'
|
99
98
|
# config.enable_jsonp_callbacks = false
|
99
|
+
# config.replace_nil_values_with_empty_strings = false
|
100
|
+
# config.replace_empty_string_values_with_nil = false
|
101
|
+
# config.exclude_nil_values = false
|
100
102
|
end
|
101
103
|
```
|
102
104
|
|
data/Rakefile
CHANGED
data/lib/rabl-rails.rb
CHANGED
@@ -1,96 +1,43 @@
|
|
1
1
|
require 'rails/railtie'
|
2
2
|
|
3
3
|
require 'active_support'
|
4
|
-
require 'active_support/core_ext/class/attribute_accessors'
|
5
4
|
|
6
5
|
require 'rabl-rails/version'
|
6
|
+
require 'rabl-rails/helpers'
|
7
7
|
require 'rabl-rails/template'
|
8
|
-
require 'rabl-rails/
|
8
|
+
require 'rabl-rails/nodes'
|
9
9
|
require 'rabl-rails/compiler'
|
10
10
|
|
11
|
+
require 'rabl-rails/visitors'
|
11
12
|
require 'rabl-rails/renderer'
|
12
|
-
|
13
13
|
require 'rabl-rails/library'
|
14
|
+
|
14
15
|
require 'rabl-rails/handler'
|
15
16
|
require 'rabl-rails/railtie'
|
16
17
|
|
17
|
-
require '
|
18
|
+
require 'rabl-rails/configuration'
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'oj'
|
22
|
+
Oj.default_options = { mode: :compat, time_format: :ruby }
|
23
|
+
rescue LoadError
|
24
|
+
require 'json'
|
25
|
+
end
|
18
26
|
|
19
27
|
module RablRails
|
20
28
|
extend Renderer
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
mattr_accessor :include_json_root
|
28
|
-
@@include_json_root = true
|
29
|
-
|
30
|
-
mattr_accessor :use_custom_responder
|
31
|
-
@@use_custom_responder = false
|
32
|
-
|
33
|
-
mattr_accessor :responder_default_template
|
34
|
-
@@responder_default_template = 'show'
|
35
|
-
|
36
|
-
mattr_reader :plist_engine
|
37
|
-
@@plist_engine = nil
|
38
|
-
|
39
|
-
mattr_accessor :include_plist_root
|
40
|
-
@@include_plist_root = nil
|
41
|
-
|
42
|
-
mattr_accessor :enable_jsonp_callbacks
|
43
|
-
@@enable_jsonp_callbacks = false
|
44
|
-
|
45
|
-
mattr_accessor :allow_empty_format_in_template
|
46
|
-
@@allow_empty_format_in_template = false
|
47
|
-
|
48
|
-
mattr_accessor :xml_options
|
49
|
-
@@xml_options = { dasherize: true, skip_types: false }
|
50
|
-
|
51
|
-
def self.configure
|
52
|
-
yield self
|
53
|
-
|
54
|
-
ActionController::Base.responder = Responder if self.use_custom_responder
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.json_engine=(name)
|
58
|
-
MultiJson.engine = name
|
59
|
-
rescue LoadError
|
60
|
-
Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{name}" as JSON engine, fallback to default)
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.json_engine
|
64
|
-
MultiJson.engine
|
65
|
-
end
|
66
|
-
|
67
|
-
def self.xml_engine=(name)
|
68
|
-
ActiveSupport::XmlMini.backend = name
|
69
|
-
rescue LoadError, NameError
|
70
|
-
Rails.logger.warn %Q(WARNING: rabl-rails could not load "#{name}" as XML engine, fallback to default)
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.xml_engine
|
74
|
-
ActiveSupport::XmlMini.backend
|
75
|
-
end
|
76
|
-
|
77
|
-
def self.plist_engine=(name)
|
78
|
-
raise "Your plist engine does not respond to #dump" unless name.respond_to?(:dump)
|
79
|
-
@@plist_engine = name
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.cache_templates?
|
83
|
-
ActionController::Base.perform_caching && @@cache_templates
|
84
|
-
end
|
30
|
+
class << self
|
31
|
+
def configure
|
32
|
+
yield configuration
|
33
|
+
end
|
85
34
|
|
86
|
-
|
87
|
-
|
88
|
-
|
35
|
+
def configuration
|
36
|
+
@_configuration ||= Configuration.new
|
37
|
+
end
|
89
38
|
|
90
|
-
|
91
|
-
|
92
|
-
elsif defined?(Nokogiri)
|
93
|
-
self.xml_engine = 'Nokogiri'
|
39
|
+
def reset_configuration
|
40
|
+
@_configuration = nil
|
94
41
|
end
|
95
42
|
end
|
96
43
|
end
|
data/lib/rabl-rails/compiler.rb
CHANGED
@@ -4,8 +4,8 @@ module RablRails
|
|
4
4
|
# representing data structure
|
5
5
|
#
|
6
6
|
class Compiler
|
7
|
-
def initialize
|
8
|
-
@
|
7
|
+
def initialize(view)
|
8
|
+
@view = view
|
9
9
|
end
|
10
10
|
|
11
11
|
#
|
@@ -41,15 +41,19 @@ module RablRails
|
|
41
41
|
# attribute :email => :super_secret
|
42
42
|
#
|
43
43
|
def attribute(*args)
|
44
|
+
node = Nodes::Attribute.new
|
45
|
+
|
44
46
|
if args.first.is_a?(Hash)
|
45
|
-
args.first.each_pair { |k, v|
|
47
|
+
args.first.each_pair { |k, v| node[v] = k }
|
46
48
|
else
|
47
49
|
options = args.extract_options!
|
48
50
|
args.each { |name|
|
49
51
|
key = options[:as] || name
|
50
|
-
|
52
|
+
node[key] = name
|
51
53
|
}
|
52
54
|
end
|
55
|
+
|
56
|
+
@template.add_node node
|
53
57
|
end
|
54
58
|
alias_method :attributes, :attribute
|
55
59
|
|
@@ -66,12 +70,14 @@ module RablRails
|
|
66
70
|
data, name = extract_data_and_name(name_or_data)
|
67
71
|
name = options[:root] if options.has_key? :root
|
68
72
|
|
69
|
-
|
70
|
-
template = Library.instance.compile_template_from_path(options[:partial])
|
71
|
-
template.
|
73
|
+
if options.key?(:partial)
|
74
|
+
template = Library.instance.compile_template_from_path(options[:partial], @view)
|
75
|
+
template.data = data
|
72
76
|
elsif block_given?
|
73
|
-
sub_compile(data) { yield }
|
77
|
+
template = sub_compile(data) { yield }
|
74
78
|
end
|
79
|
+
|
80
|
+
@template.add_node Nodes::Child.new(name, template)
|
75
81
|
end
|
76
82
|
|
77
83
|
#
|
@@ -81,7 +87,9 @@ module RablRails
|
|
81
87
|
#
|
82
88
|
def glue(data)
|
83
89
|
return unless block_given?
|
84
|
-
|
90
|
+
|
91
|
+
template = sub_compile(data) { yield }
|
92
|
+
@template.add_node Nodes::Glue.new(template)
|
85
93
|
end
|
86
94
|
|
87
95
|
#
|
@@ -93,18 +101,8 @@ module RablRails
|
|
93
101
|
# node(:role, if: ->(u) { !u.admin? }) { |u| u.role }
|
94
102
|
#
|
95
103
|
def node(name = nil, options = {}, &block)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
if condition
|
100
|
-
if condition.is_a?(Proc)
|
101
|
-
@template[name] = [condition, block]
|
102
|
-
else
|
103
|
-
@template[name] = block if condition
|
104
|
-
end
|
105
|
-
else
|
106
|
-
@template[name] = block
|
107
|
-
end
|
104
|
+
return unless block_given?
|
105
|
+
@template.add_node Nodes::Code.new(name, block, options[:if])
|
108
106
|
end
|
109
107
|
alias_method :code, :node
|
110
108
|
|
@@ -114,9 +112,9 @@ module RablRails
|
|
114
112
|
# Example:
|
115
113
|
# merge { |item| partial("specific/#{item.to_s}", object: item) }
|
116
114
|
#
|
117
|
-
def merge
|
115
|
+
def merge
|
118
116
|
return unless block_given?
|
119
|
-
node(
|
117
|
+
node(nil) { yield }
|
120
118
|
end
|
121
119
|
|
122
120
|
#
|
@@ -125,8 +123,7 @@ module RablRails
|
|
125
123
|
# extends 'users/base'
|
126
124
|
#
|
127
125
|
def extends(path)
|
128
|
-
|
129
|
-
@template.merge!(t.source)
|
126
|
+
@template.extends Library.instance.compile_template_from_path(path, @view)
|
130
127
|
end
|
131
128
|
|
132
129
|
#
|
@@ -138,7 +135,7 @@ module RablRails
|
|
138
135
|
#
|
139
136
|
def condition(proc)
|
140
137
|
return unless block_given?
|
141
|
-
@template
|
138
|
+
@template.add_node Nodes::Condition.new(proc, sub_compile(nil, true) { yield })
|
142
139
|
end
|
143
140
|
|
144
141
|
def cache(&block)
|
@@ -147,14 +144,6 @@ module RablRails
|
|
147
144
|
|
148
145
|
protected
|
149
146
|
|
150
|
-
#
|
151
|
-
# Return unique symbol starting with given name
|
152
|
-
#
|
153
|
-
def sequence(name)
|
154
|
-
@i += 1
|
155
|
-
:"_#{name}#{@i}"
|
156
|
-
end
|
157
|
-
|
158
147
|
#
|
159
148
|
# Extract data root_name and root name
|
160
149
|
# Example:
|
@@ -173,11 +162,12 @@ module RablRails
|
|
173
162
|
end
|
174
163
|
end
|
175
164
|
|
176
|
-
def sub_compile(data)
|
177
|
-
|
178
|
-
old_template, @template = @template,
|
165
|
+
def sub_compile(data, only_nodes = false)
|
166
|
+
raise unless block_given?
|
167
|
+
old_template, @template = @template, CompiledTemplate.new
|
179
168
|
yield
|
180
|
-
|
169
|
+
@template.data = data
|
170
|
+
only_nodes ? @template.nodes : @template
|
181
171
|
ensure
|
182
172
|
@template = old_template
|
183
173
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RablRails
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :json_engine, :include_json_root, :enable_jsonp_callbacks
|
4
|
+
attr_accessor :xml_options
|
5
|
+
attr_accessor :plist_engine, :include_plist_root
|
6
|
+
attr_accessor :cache_templates
|
7
|
+
attr_reader :use_custom_responder
|
8
|
+
attr_accessor :responder_default_template
|
9
|
+
attr_accessor :replace_nil_values_with_empty_strings
|
10
|
+
attr_accessor :replace_empty_string_values_with_nil
|
11
|
+
attr_accessor :exclude_nil_values
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@json_engine = defined?(::Oj) ? ::Oj : ::JSON
|
15
|
+
@include_json_root = true
|
16
|
+
@enable_jsonp_callbacks = false
|
17
|
+
|
18
|
+
@xml_options = { dasherize: true, skip_types: false }
|
19
|
+
|
20
|
+
@plist_engine = defined?(::Plist) ? ::Plist::Emit : nil
|
21
|
+
@include_plist_root = false
|
22
|
+
|
23
|
+
@cache_templates = ActionController::Base.perform_caching
|
24
|
+
|
25
|
+
@use_custom_responder = false
|
26
|
+
@responder_default_template = 'show'
|
27
|
+
|
28
|
+
@replace_nil_values_with_empty_strings = false
|
29
|
+
@replace_empty_string_values_with_nil = false
|
30
|
+
@exclude_nil_values = false
|
31
|
+
end
|
32
|
+
|
33
|
+
def use_custom_responder=(value)
|
34
|
+
@use_custom_responder = value
|
35
|
+
require 'rabl-rails/responder' if value
|
36
|
+
end
|
37
|
+
|
38
|
+
def result_flags
|
39
|
+
@result_flags ||= begin
|
40
|
+
result = 0
|
41
|
+
result |= 0b01 if @replace_nil_values_with_empty_strings
|
42
|
+
result |= 0b10 if @replace_empty_string_values_with_nil
|
43
|
+
result |= 0b100 if @exclude_nil_values
|
44
|
+
result
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/rabl-rails/handler.rb
CHANGED
data/lib/rabl-rails/library.rb
CHANGED
@@ -1,37 +1,64 @@
|
|
1
1
|
require 'singleton'
|
2
|
+
require 'monitor'
|
3
|
+
require 'thread_safe'
|
2
4
|
|
3
5
|
module RablRails
|
4
6
|
class Library
|
5
7
|
include Singleton
|
6
8
|
|
7
9
|
def initialize
|
8
|
-
@cached_templates =
|
10
|
+
@cached_templates = ThreadSafe::Cache.new
|
11
|
+
@mutex = Monitor.new
|
9
12
|
end
|
10
13
|
|
11
|
-
def
|
12
|
-
|
13
|
-
|
14
|
+
def reset_cache!
|
15
|
+
@cached_templates = ThreadSafe::Cache.new
|
16
|
+
end
|
14
17
|
|
15
|
-
|
18
|
+
def get_rendered_template(source, view, locals = nil)
|
19
|
+
compiled_template = compile_template_from_source(source, view)
|
20
|
+
format = view.params[:format] ? view.params[:format].to_s.upcase : :JSON
|
21
|
+
Renderers.const_get(format).render(compiled_template, view, locals)
|
22
|
+
end
|
16
23
|
|
17
|
-
|
18
|
-
|
24
|
+
def compile_template_from_source(source, view)
|
25
|
+
if RablRails.configuration.cache_templates
|
26
|
+
path = view.instance_variable_get(:@virtual_path)
|
27
|
+
synchronized_compile(path, source, view)
|
28
|
+
else
|
29
|
+
compile(source, view)
|
30
|
+
end
|
19
31
|
end
|
20
32
|
|
21
|
-
def
|
22
|
-
if
|
23
|
-
|
24
|
-
@cached_templates[path].dup
|
33
|
+
def compile_template_from_path(path, view)
|
34
|
+
if RablRails.configuration.cache_templates
|
35
|
+
synchronized_compile(path, nil, view)
|
25
36
|
else
|
26
|
-
|
37
|
+
source = fetch_source(path, view)
|
38
|
+
compile(source, view)
|
27
39
|
end
|
28
40
|
end
|
29
41
|
|
30
|
-
|
31
|
-
|
42
|
+
private
|
43
|
+
|
44
|
+
def synchronized_compile(path, source, view)
|
45
|
+
@cached_templates[path] || @mutex.synchronize do
|
46
|
+
# Any thread holding this lock will be compiling the template needed
|
47
|
+
# by the threads waiting. So re-check the template presence to avoid
|
48
|
+
# re-compilation
|
49
|
+
@cached_templates.fetch(path) do
|
50
|
+
source ||= fetch_source(path, view)
|
51
|
+
@cached_templates[path] = compile(source, view)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def compile(source, view)
|
57
|
+
Compiler.new(view).compile_source(source)
|
58
|
+
end
|
32
59
|
|
33
|
-
|
34
|
-
|
60
|
+
def fetch_source(path, view)
|
61
|
+
view.lookup_context.find_template(path, [], false).source
|
35
62
|
end
|
36
63
|
end
|
37
64
|
end
|