├── .github
└── FUNDING.yml
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── coffee
└── watch.coffee
├── doc
├── logo.png
├── logo_small.png
└── top_sample.png
├── gon.gemspec
├── js
└── watch.js
├── lib
├── gon.rb
└── gon
│ ├── base.rb
│ ├── compatibility
│ └── old_rails.rb
│ ├── env_finder.rb
│ ├── escaper.rb
│ ├── global.rb
│ ├── helpers.rb
│ ├── jbuilder.rb
│ ├── jbuilder
│ └── parser.rb
│ ├── json_dumper.rb
│ ├── rabl.rb
│ ├── request.rb
│ ├── spec_helpers.rb
│ ├── version.rb
│ └── watch.rb
└── spec
├── gon
├── basic_spec.rb
├── global_spec.rb
├── jbuilder_spec.rb
├── rabl_spec.rb
├── templates_spec.rb
├── thread_spec.rb
└── watch_spec.rb
├── spec_helper.rb
└── test_data
├── _sample_partial.json.jbuilder
├── sample.json.jbuilder
├── sample.rabl
├── sample_rabl_rails.rabl
├── sample_url_helpers.json.jbuilder
├── sample_with_controller_method.json.jbuilder
├── sample_with_helpers.json.jbuilder
├── sample_with_helpers.rabl
├── sample_with_helpers_rabl_rails.rabl
├── sample_with_locals.json.jbuilder
└── sample_with_partial.json.jbuilder
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | tidelift: "rubygems/gon"
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | .bundle
3 | Gemfile.lock
4 | pkg/*
5 | tmp/*
6 | .rvmrc
7 | *.idea
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: ruby
2 | sudo: false
3 | env:
4 | - "RABL_GEM=rabl"
5 | - "RABL_GEM=rabl-rails"
6 |
7 | rvm:
8 | - 2.3.8
9 | - 2.4.10
10 | - 2.5.8
11 | - 2.6.6
12 | - 2.7.1
13 | - ruby-head
14 | - truffleruby-head
15 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 | ## [Unreleased]
4 |
5 | ## [6.4.0] - 2020-09-18
6 | ### Security
7 | - CVE-2020-25739: Enforce HTML entities escaping in gon output
8 |
9 | ## [6.3.2] - 2019-11-18
10 | ### Security
11 | - Restrict possibility of vulnerable i18n legacy verision (0.3.6.pre)
12 | installation
13 |
14 | ## [6.3.1] - 2019-11-18
15 | ### Changed
16 | - ActionView::Base and ActionController::Base should be loaded inside
17 | ActiveSupport.on_load hook. Thanks to @amatsuda
18 | - Require Ruby >= 2.2.2 (activesupport). Thanks to @nicolasleger
19 | - Update old_rails.rb to reflect GonHelpers -> ControllerHelpers name change.
20 | Thanks to @etipton
21 |
22 | ## [6.2.1] - 2018-07-11
23 | ### Changed
24 | - Update README: correct spelling mistake. Thanks to @EdwardBetts
25 | - Autoload test classes only in test env. Thanks to @wilddima
26 |
27 | ### Fixed
28 | - Fix keys cache. Thanks to @ertrzyiks
29 | - fixing tests by running with rabl and rabl-rails separately. Thanks to
30 | @dsalahutdinov
31 |
32 | ## [6.2.0] - 2017-10-04
33 | ### Added
34 | - Introduce keys cache. Thanks to @vlazar
35 | - Add possibleErrorCallback to watch params. Thanks to @etagwerker
36 |
37 | ### Changed
38 | - Update readme with PhoenixGon hex link. Thanks to @khusnetdinov
39 | - Fix code highlighting in README. Thanks to @ojab
40 | - Refactoring: use attr_reader
41 |
42 | ### Removed
43 | - Remove unnecessary json dependency.
44 | - Remove rubysl and rubinius-developer_tools gem.
45 |
46 | ## [6.1.0] - 2016-07-11
47 | ### Deprecated
48 | - env is deprecated and will be removed from Rails 5.0. Thanks to @dlupu
49 |
50 | ### Fixed
51 | - fix merging routes bug. Thanks to @strikyflo
52 | - Show what method was used in public methods error.
53 |
54 | ### Changed
55 | - Use 'need_tag' as option name to prevent calling 'tag' method. Thanks to
56 | @june29
57 | - Update README; comment out gon.clear from sample code. Thanks to
58 | @speee-nakajima
59 | - Update README; Replace the include_gon method with render_data method.
60 | - Refactoring: use attr_accessor method.
61 | - Refactoring: use attr_reader method.
62 |
63 | ## [6.0.1] - 2015-07-22
64 | ### Changed
65 | - Free dependencies
66 |
67 | ## [6.0.0] - 2015-07-22
68 | ### Added
69 | - nonce option. Thanks to @joeljackson
70 |
71 | ### Changed
72 | - Refactoring
73 | - Included rails url_helpers into jbuilder. Thanks to @razum2um
74 |
75 | ## [5.2.3] - 2014-11-03
76 | ### Added
77 | - Coffescript implementation of watch.js. Thanks to @willcosgrove
78 | - unwatchAll function in watch.js. Thanks to @willcosgrove
79 |
80 | ## [5.2.2] - 2014-10-31
81 | ### Added
82 | - support for controller helper methods in jbuilder
83 |
84 | ## [5.2.1] - 2014-10-28
85 | ### Added
86 | - merge variable feature (for merge hash-like variables instead of overriding
87 | them). Thanks to @jalkoby
88 |
89 | ### Fixed
90 | - fix for jbuilder module. Thanks to @jankovy
91 |
92 | ## [5.2.0] - 2014-08-26
93 | ### Added
94 | - namespace_check option. Thanks to @tommyh
95 | - AMD compatible version of including gon. Thanks to @vijoc
96 |
97 | ### Changed
98 | - Only inject gon into ActionController::Base-like object in spec_helper. Thanks
99 | to @kevinoconnor7
100 |
101 | ### Fixed
102 | - fix issue where include_gon would raise exception if the controller did not
103 | assign any gon variables. Thanks to @asalme
104 |
105 | ## [5.1.2] - 2014-07-22
106 | ### Changed
107 | - Clarifying helpers, dump gon#watch content to safe json before render. Thanks
108 | to @Strech
109 |
110 | ## [5.1.1] - 2014-07-17
111 | ### Added
112 | - global_root option. Thanks to @rafaelliu
113 | - MultiJson support. Thanks to @Strech
114 |
115 | ## [5.1.0] - 2014-06-29
116 | ### Fixed
117 | - Many fixes. Thanks to @Silex, @kilefritz, @irobayna, @kyrylo, @randoum,
118 | @jackquack, @tuvistavie, @Strech for awesome commits and help!
119 |
120 | ## [5.0.4] - 2014-02-13
121 | ### Fixed
122 | - Fix check for get and assign variables for Gon.global
123 |
124 | ## [5.0.3] - 2014-02-12
125 | ### Removed
126 | - Revert changes in gemspec
127 |
128 | ## [5.0.2] - 2014-02-12
129 | ### Fixed
130 | - Fix issue when there is no gon object for current thread and rendering
131 | include_gon (#108 part) (wasn't fixed) (@gregmolnar)
132 |
133 | ## [5.0.1] - 2013-12-30
134 | ### Fixed
135 | - Fix issue when there is no gon object for current thread and rendering
136 | include_gon (#108 part)
137 |
138 | ## [5.0.0] - 2013-12-26
139 | ### Changed
140 | - Gon is threadsafe now! (@razum2um)
141 | - Camelcasing with depth (@MaxSchmeling)
142 | - Optional CDATA and style refactoring (@torbjon)
143 | - jBuilder supports not only String and Hash types of locals (@steakchaser)
144 | - Using ActionDispatch::Request#uuid instead of ActionDispatch::Request#id
145 | (@sharshenov)
146 |
147 | ## [4.1.1] - 2013-06-04
148 | ### Fixed
149 | - Fixed critical XSS vulnerability https://github.com/gazay/gon/issues/84
150 | (@vadimr & @Hebo)
151 |
152 | ## [4.1.0] - 2013-04-14
153 | ### Added
154 | - rabl-rails support (@jtherrell)
155 |
156 | ### Changed
157 | - Refactored script tag generation (@toothrot)
158 | - Stop support for MRI 1.8.7
159 | - Accepting locals in jbuilder templates
160 |
161 | ## [4.0.3] - 2013-04-14
162 | !!!IMPORTANT!!! Last version with compatibility for MRI 1.8.7
163 |
164 | ### Added
165 | - new method `Gon#push` for assign variables through Hash-like objects (@topdev)
166 | ### Changed
167 | - Fixes for 1.8.7 compatibility.
168 |
169 | ## [4.0.2] - 2012-12-17
170 | ### Fixed
171 | - Fixed gon.watch in JS without callback and options
172 |
173 | ## [4.0.1] - 2012-10-25
174 | ### Added
175 | - option :locals to gon.rabl functionality
176 |
177 | ### Changed
178 | - Gon#set_variable and Gon#get_variable moved to public scope
179 |
180 | ### Removed
181 | - BlankSlate requirement (@phoet)
182 |
183 | ## [4.0.0] - 2012-07-23
184 | ### Added
185 | - gon.watch functionality (thanks to @brainopia and @kossnocorp)
186 | - Compatibility with jbuilder paths for partial! method
187 |
188 | ### Changed
189 | - Little bit refactoring - Gon now is a class
190 |
191 | ### Fixed
192 | - Fixed some bugs
193 |
194 | ## [3.0.5] - 2012-06-22
195 | ### Added
196 | - type text/javascript option (@torbjon)
197 |
198 | ### Changed
199 | - A litlle bit refactoring
200 | - Made compatible with active support json encoding for escaping script tags
201 |
202 | ### Fixed
203 | - bug for init option
204 | - clear if init true (@torbjon)
205 |
206 | ## [3.0.4] - 2012-06-02
207 | ### Fixed
208 | - Fix bug with gon clear with global variables, bump version
209 |
210 | ## [3.0.3] - 2012-05-22
211 | ### Added
212 | - init option (@torbjon)
213 |
214 | ### Changed
215 | - Include ActionView::Helpers into Gon::JBuilder
216 |
217 | ## [3.0.2] - 2012-04-28
218 | ### Added
219 | - need_tag option (@afa)
220 |
221 | ## [3.0.0] - 2012-04-17
222 | ### Added
223 | - Added Gon.global for using gon everywhere
224 |
225 | ### Changed
226 | - Almost all code refactored
227 | - Included ActionView::Helpers into Rabl::Engine
228 |
229 | ## [2.3.0] - 2012-04-09
230 | ### Changed
231 | - Don't really remember what was before this version
232 |
233 | [Unreleased]: https://github.com/gazay/gon/compare/v6.3.2...master
234 | [6.3.2]: https://github.com/gazay/gon/compare/v6.3.1...v6.3.2
235 | [6.3.1]: https://github.com/gazay/gon/compare/v6.2.1...v6.3.1
236 | [6.2.1]: https://github.com/gazay/gon/compare/v6.2.0...v6.2.1
237 | [6.2.0]: https://github.com/gazay/gon/compare/v6.1.0...v6.2.0
238 | [6.1.0]: https://github.com/gazay/gon/compare/v6.0.1...v6.1.0
239 | [6.0.1]: https://github.com/gazay/gon/compare/v6.0.0...v6.0.1
240 | [6.0.0]: https://github.com/gazay/gon/compare/v5.2.3...v6.0.0
241 | [5.2.3]: https://github.com/gazay/gon/compare/v5.2.2...v5.2.3
242 | [5.2.2]: https://github.com/gazay/gon/compare/v5.2.1...v5.2.2
243 | [5.2.1]: https://github.com/gazay/gon/compare/v5.2.0...v5.2.1
244 | [5.2.0]: https://github.com/gazay/gon/compare/v5.1.2...v5.2.0
245 | [5.1.2]: https://github.com/gazay/gon/compare/v5.1.1...v5.1.2
246 | [5.1.1]: https://github.com/gazay/gon/compare/v5.1.0...v5.1.1
247 | [5.1.0]: https://github.com/gazay/gon/compare/v5.0.4...v5.1.0
248 | [5.0.4]: https://github.com/gazay/gon/compare/v5.0.3...v5.0.4
249 | [5.0.3]: https://github.com/gazay/gon/compare/v5.0.2...v5.0.3
250 | [5.0.2]: https://github.com/gazay/gon/compare/v5.0.1...v5.0.2
251 | [5.0.1]: https://github.com/gazay/gon/compare/v5.0.0...v5.0.1
252 | [5.0.0]: https://github.com/gazay/gon/compare/v4.1.1...v5.0.0
253 | [4.1.1]: https://github.com/gazay/gon/compare/v4.1.0...v4.1.1
254 | [4.1.0]: https://github.com/gazay/gon/compare/v4.0.3...v4.1.0
255 | [4.0.3]: https://github.com/gazay/gon/compare/v4.0.2...v4.0.3
256 | [4.0.2]: https://github.com/gazay/gon/compare/v4.0.1...v4.0.2
257 | [4.0.1]: https://github.com/gazay/gon/compare/v4.0.0...v4.0.1
258 | [4.0.0]: https://github.com/gazay/gon/compare/v3.0.5...v4.0.0
259 | [3.0.5]: https://github.com/gazay/gon/compare/v3.0.4...v3.0.5
260 | [3.0.4]: https://github.com/gazay/gon/compare/v3.0.3...v3.0.4
261 | [3.0.3]: https://github.com/gazay/gon/compare/v3.0.2...v3.0.3
262 | [3.0.2]: https://github.com/gazay/gon/compare/v3.0.0...v3.0.2
263 | [3.0.0]: https://github.com/gazay/gon/compare/v2.3.0...v3.0.0
264 | [2.3.0]: https://github.com/gazay/gon/releases/tag/v2.3.0
265 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | # Specify your gem's dependencies in gon.gemspec
4 | gem ENV['RABL_GEM'] || 'rabl'
5 |
6 | gemspec
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2011-2019 Alexey Gaziev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gon gem — get your Rails variables in your js
2 |
3 | [](https://gitter.im/gazay/gon?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4 |
5 | 
6 |
7 | [](https://travis-ci.org/gazay/gon) [](https://codeclimate.com/github/gazay/gon)
8 |
9 | If you need to send some data to your js files and you don't want to do this with long way through views and parsing - use this force!
10 |
11 | Now you can easily renew data in your variables through ajax with [gon.watch](https://github.com/gazay/gon/wiki/Usage-gon-watch)!
12 |
13 | With [Jbuilder](https://github.com/rails/jbuilder), [Rabl](https://github.com/nesquena/rabl), and [Rabl-Rails](https://github.com/ccocchi/rabl-rails) support!
14 |
15 | For Sinatra available [gon-sinatra](https://github.com/gazay/gon-sinatra).
16 |
17 | For .Net MVC available port [NGon](https://github.com/brooklynDev/NGon).
18 |
19 | For elixir Phoenix available [PhoenixGon](https://github.com/khusnetdinov/phoenix_gon).
20 |
21 |
22 |
23 |
24 |
25 | ## An example of typical use
26 |
27 | ### Very good and detailed example and reasons to use is considered in [railscast](http://railscasts.com/episodes/324-passing-data-to-javascript) by Ryan Bates
28 |
29 | When you need to send some start data from your controller to your js
30 | you might be doing something like this:
31 |
32 | 1. Write this data in controller(presenter/model) to some variable
33 | 2. In view for this action you put this variable to some objects by data
34 | attributes, or write js right in view
35 | 3. Then there can be two ways in js:
36 | + if you previously wrote data in data
37 | attributes - you should parse this attributes and write data to some
38 | js variable.
39 | + if you wrote js right in view (many frontenders would shame you for
40 | that) - you just use data from this js - OK.
41 | 4. You can use your data in your js
42 |
43 | And every time when you need to send some data from action to js you do this.
44 |
45 | With gon you configure it firstly - just put in layout one tag, and add
46 | gem line to your Gemfile and do the following:
47 |
48 | 1. Write variables by
49 |
50 | ``` ruby
51 | gon.variable_name = variable_value
52 |
53 | # or new syntax
54 | gon.push({
55 | :user_id => 1,
56 | :user_role => "admin"
57 | })
58 |
59 | gon.push(any_object) # any_object with respond_to? :each_pair
60 | ```
61 |
62 | 2. In your js you get this by
63 |
64 | ``` js
65 | gon.variable_name
66 | ```
67 |
68 | 3. profit?
69 |
70 | With the `gon.watch` feature you can easily renew data in gon variables!
71 | Simply call `gon.watch` from your js file. It's super useful
72 | in modern web applications!
73 |
74 | ## Usage
75 |
76 | ### More details about configuration and usage you can find in [gon wiki](https://github.com/gazay/gon/wiki)
77 |
78 | `app/views/layouts/application.html.erb`
79 |
80 | ``` erb
81 |
82 | some title
83 | <%= Gon::Base.render_data %>
84 |
85 | ...
86 | ```
87 |
88 | For rails 3:
89 | ``` erb
90 | <%= include_gon %>
91 | ...
92 | ```
93 |
94 |
95 |
96 | You can pass some [options](https://github.com/gazay/gon/wiki/Options)
97 | to `render_data` method.
98 |
99 | You put something like this in the action of your controller:
100 |
101 | ``` ruby
102 | @your_int = 123
103 | @your_array = [1,2]
104 | @your_hash = {'a' => 1, 'b' => 2}
105 | gon.your_int = @your_int
106 | gon.your_other_int = 345 + gon.your_int
107 | gon.your_array = @your_array
108 | gon.your_array << gon.your_int
109 | gon.your_hash = @your_hash
110 |
111 | gon.all_variables # > {:your_int => 123, :your_other_int => 468, :your_array => [1, 2, 123], :your_hash => {'a' => 1, 'b' => 2}}
112 | gon.your_array # > [1, 2, 123]
113 |
114 | # gon.clear # gon.all_variables now is {}
115 | ```
116 |
117 | Access the variables from your JavaScript file:
118 |
119 | ``` js
120 | alert(gon.your_int)
121 | alert(gon.your_other_int)
122 | alert(gon.your_array)
123 | alert(gon.your_hash)
124 | ```
125 |
126 | ### AMD compatible version: `include_gon_amd`
127 |
128 | If your site uses AMD modules you can use the `include_gon_amd` helper to
129 | include the variables and watch function as a module. Options are mostly
130 | the same as for `include_gon`, except for `namespace_check`, which does
131 | nothing and `namespace`, which is used as the name of the defined module.
132 | The end result will look somewhat like the following:
133 |
134 | ```js
135 | define('yourNameSpace', [], function() {
136 | var gon = {};
137 | gon.yourVariable = yourValue;
138 | // etc...
139 |
140 | return gon;
141 | });
142 | ```
143 |
144 | A (very) simplified usage example:
145 |
146 | `app/views/layouts/application.html.erb`
147 |
148 | ```ruby
149 | include_gon_amd namespace: 'data'
150 | ```
151 |
152 | `Some JavaScript module`
153 |
154 | ```js
155 | define(['data'], function(data) {
156 | alert(data.myVariable);
157 | });
158 | ```
159 |
160 | ## gon.watch - renew your data easily!
161 |
162 | You can use gon for renewing your data without reloading pages and
163 | writing long js functions! It's really great for some live values.
164 |
165 | Supports `gon.watch.rabl` and `gon.watch.jbuilder` usage.
166 |
167 | [Instruction](https://github.com/gazay/gon/wiki/Usage-gon-watch) for
168 | usage gon.watch.
169 |
170 | ## Usage with Rabl
171 |
172 | You can write your variables assign logic to templates with [Rabl](https://github.com/nesquena/rabl).
173 | The way of writing Rabl templates is very clearly described in their repo.
174 |
175 | Profit of using Rabl with gon:
176 |
177 | 1. You can clean your controllers now!
178 | 2. Work with database objects and collections clearly and easyly
179 | 3. All power of Rabl
180 | 4. You can still be lazy and don't use common way to transfer data in js
181 | 5. And so on
182 |
183 | [Instruction](https://github.com/gazay/gon/wiki/Usage-with-rabl) for
184 | usage gon with Rabl.
185 |
186 | ## Usage with Rabl-Rails
187 | `gon.rabl` works with [rabl-rails](https://github.com/ccocchi/rabl-rails). Learn to write RABL the rabl-rails way [here](https://github.com/ccocchi/rabl-rails).
188 |
189 | Add gon and rabl-rails to your environment:
190 | ```ruby
191 | gem 'gon'
192 | gem 'rabl-rails'
193 | ```
194 | Define a rabl template using rabl-rails syntax:
195 | ```rabl
196 | #app/views/users/show.rabl
197 | object :@user
198 | attributes :id, :name, :email, :location
199 | ```
200 | Call gon.rabl in your controller
201 |
202 | ```ruby
203 | #app/controllers/users_controller.rb
204 | def show
205 | @user = User.find(params[:id])
206 | gon.rabl
207 | end
208 | ```
209 |
210 | ## Usage with Jbuilder
211 |
212 | Use gon with [Jbuilder](https://github.com/rails/jbuilder) as with [Rabl](https://guthub.com/nesquena/rabl):
213 |
214 | [Instruction](https://github.com/gazay/gon/wiki/Usage-with-jbuilder) for
215 | usage gon with Jbuilder.
216 |
217 | ## gon.global
218 |
219 | You can use gon for sending your data to js from anywhere! It's really
220 | great for some init data.
221 |
222 | [Instruction](https://github.com/gazay/gon/wiki/Usage-gon-global) for
223 | usage gon.global.
224 |
225 | ## Speed up Gon
226 |
227 | You can use any [JSON Engine](https://github.com/intridea/multi_json#supported-json-engines) you want.
228 | Gon uses `MultiJson` with autodetect mode, so all you need is just require your JSON library.
229 |
230 | ## Contributors
231 |
232 | * @gazay
233 | * @takiy33
234 |
235 | Special thanks to @brainopia, @kossnocorp and @ai.
236 |
237 | ## License
238 |
239 | The MIT License
240 |
241 | ## Security Contact
242 |
243 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
244 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler'
2 | Bundler::GemHelper.install_tasks
3 |
4 | desc 'Run all tests by default'
5 | task :default => :spec
6 |
7 | require 'rspec/core/rake_task'
8 | RSpec::Core::RakeTask.new do |t|
9 | t.rspec_opts = ["--color", '--format doc', '--require spec_helper']
10 | end
--------------------------------------------------------------------------------
/coffee/watch.coffee:
--------------------------------------------------------------------------------
1 | gon._timers = {}
2 |
3 | gon.watch = (name, possibleOptions, possibleCallback, possibleErrorCallback) ->
4 | return unless $?
5 |
6 | if typeof possibleOptions == 'object'
7 | options = {}
8 | for key, value of gon.watchedVariables[name]
9 | options[key] = value
10 | for key, value of possibleOptions
11 | options[key] = value
12 | callback = possibleCallback
13 | errorCallback = possibleErrorCallback
14 | else
15 | options = gon.watchedVariables[name]
16 | callback = possibleOptions
17 | errorCallback = possibleCallback
18 |
19 | performAjax = ->
20 | xhr = $.ajax
21 | type: options.type || 'GET'
22 | url: options.url
23 | data:
24 | _method: options.method
25 | gon_return_variable: true
26 | gon_watched_variable: name
27 |
28 | if errorCallback
29 | xhr.done(callback).fail(errorCallback);
30 | else
31 | xhr.done(callback)
32 |
33 | if options.interval
34 | timer = setInterval(performAjax, options.interval)
35 | gon._timers[name] ?= []
36 | return gon._timers[name].push
37 | timer: timer
38 | fn: callback
39 | else
40 | return performAjax()
41 |
42 | gon.unwatch = (name, fn) ->
43 | for timer, index in gon._timers[name] when timer.fn == fn
44 | clearInterval(timer.timer)
45 | gon._timers[name].splice(index, 1)
46 | return
47 |
48 | gon.unwatchAll = ->
49 | for variable, timers of gon._timers
50 | for timer in timers
51 | clearInterval(timer.timer)
52 | gon._timers = {}
53 |
--------------------------------------------------------------------------------
/doc/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gazay/gon/ab7e6113d0ba002fe4a7f2b72a423d602805c1ce/doc/logo.png
--------------------------------------------------------------------------------
/doc/logo_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gazay/gon/ab7e6113d0ba002fe4a7f2b72a423d602805c1ce/doc/logo_small.png
--------------------------------------------------------------------------------
/doc/top_sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gazay/gon/ab7e6113d0ba002fe4a7f2b72a423d602805c1ce/doc/top_sample.png
--------------------------------------------------------------------------------
/gon.gemspec:
--------------------------------------------------------------------------------
1 | lib = File.expand_path('../lib', __FILE__)
2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3 | require 'gon/version'
4 |
5 | Gem::Specification.new do |s|
6 | s.name = 'gon'
7 | s.version = Gon::VERSION
8 | s.platform = Gem::Platform::RUBY
9 | s.authors = ['gazay']
10 | s.licenses = ['MIT']
11 | s.email = ['alex.gaziev@gmail.com']
12 | s.homepage = 'https://github.com/gazay/gon'
13 | s.summary = %q{Get your Rails variables in your JS}
14 | s.description = %q{If you need to send some data to your js files and you don't want to do this with long way trough views and parsing - use this force!}
15 |
16 | s.files = `git ls-files`.split("\n")
17 | s.require_paths = ['lib']
18 | s.required_ruby_version = '>= 2.2.0'
19 | s.add_dependency 'actionpack', '>= 3.0.20'
20 | s.add_dependency 'i18n', '>= 0.7'
21 | s.add_dependency 'request_store', '>= 1.0'
22 | s.add_dependency 'multi_json'
23 | s.add_development_dependency 'rabl', '0.11.3'
24 | s.add_development_dependency 'rabl-rails'
25 | s.add_development_dependency 'rspec', '>= 3.0'
26 | s.add_development_dependency 'jbuilder'
27 | s.add_development_dependency 'railties', '>= 3.0.20'
28 | s.add_development_dependency 'rake'
29 | s.add_development_dependency 'pry'
30 | s.add_development_dependency 'pry-byebug'
31 | end
32 |
--------------------------------------------------------------------------------
/js/watch.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.7.1
2 | gon._timers = {};
3 |
4 | gon.watch = function(name, possibleOptions, possibleCallback, possibleErrorCallback) {
5 | var callback, errorCallback, key, options, performAjax, timer, value, _base, _ref;
6 | if (typeof $ === "undefined" || $ === null) {
7 | return;
8 | }
9 | if (typeof possibleOptions === 'object') {
10 | options = {};
11 | _ref = gon.watchedVariables[name];
12 | for (key in _ref) {
13 | value = _ref[key];
14 | options[key] = value;
15 | }
16 | for (key in possibleOptions) {
17 | value = possibleOptions[key];
18 | options[key] = value;
19 | }
20 | callback = possibleCallback;
21 | errorCallback = possibleErrorCallback;
22 | } else {
23 | options = gon.watchedVariables[name];
24 | callback = possibleOptions;
25 | errorCallback = possibleCallback;
26 | }
27 | performAjax = function() {
28 | var xhr;
29 | xhr = $.ajax({
30 | type: options.type || 'GET',
31 | url: options.url,
32 | data: {
33 | _method: options.method,
34 | gon_return_variable: true,
35 | gon_watched_variable: name
36 | }
37 | });
38 | if (errorCallback) {
39 | return xhr.done(callback).fail(errorCallback);
40 | } else {
41 | return xhr.done(callback);
42 | }
43 | };
44 | if (options.interval) {
45 | timer = setInterval(performAjax, options.interval);
46 | if ((_base = gon._timers)[name] == null) {
47 | _base[name] = [];
48 | }
49 | return gon._timers[name].push({
50 | timer: timer,
51 | fn: callback
52 | });
53 | } else {
54 | return performAjax();
55 | }
56 | };
57 |
58 | gon.unwatch = function(name, fn) {
59 | var _i, index, _len, _ref, timer;
60 | _ref = gon._timers[name];
61 | for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
62 | timer = _ref[index];
63 | if (!(timer.fn === fn)) {
64 | continue;
65 | }
66 | clearInterval(timer.timer);
67 | gon._timers[name].splice(index, 1);
68 | return;
69 | }
70 | };
71 |
72 | gon.unwatchAll = function() {
73 | var _i, _len, _ref, timer, timers, variable;
74 | _ref = gon._timers;
75 | for (variable in _ref) {
76 | timers = _ref[variable];
77 | for (_i = 0, _len = timers.length; _i < _len; _i++) {
78 | timer = timers[_i];
79 | clearInterval(timer.timer);
80 | }
81 | }
82 | return gon._timers = {};
83 | };
84 |
--------------------------------------------------------------------------------
/lib/gon.rb:
--------------------------------------------------------------------------------
1 | require 'request_store'
2 | require 'action_view'
3 | require 'action_controller'
4 | require 'multi_json'
5 |
6 | require 'gon/base'
7 | require 'gon/env_finder'
8 | require 'gon/global'
9 | require 'gon/watch'
10 | require 'gon/request'
11 | require 'gon/helpers'
12 | require 'gon/escaper'
13 | require 'gon/rabl'
14 | require 'gon/jbuilder'
15 | require 'gon/jbuilder/parser'
16 | require 'gon/json_dumper'
17 |
18 | # NOTE : ActionDispatch::Request#uuid appears only in Rails 3.2.1
19 | unless ActionDispatch::Request.public_instance_methods.include?(:uuid)
20 | require 'gon/compatibility/old_rails'
21 | end
22 |
23 | require 'gon/spec_helpers'
24 |
25 | class Gon
26 | class << self
27 |
28 | def global
29 | Gon::Global
30 | end
31 |
32 | def watch
33 | Gon::Watch
34 | end
35 |
36 | def method_missing(method, *args, &block)
37 | if method.to_s =~ /=$/
38 | if public_method_name?(method)
39 | raise "You can't use Gon public methods for storing data: #{method}"
40 | end
41 | if self == Gon && !current_gon
42 | raise 'Assign request-specific gon variables only through `gon` helper, not through Gon constant'
43 | end
44 |
45 | set_variable(method.to_s.delete('='), args[0])
46 | else
47 | get_variable(method.to_s)
48 | end
49 | end
50 |
51 | def get_variable(name)
52 | current_gon.gon[name]
53 | end
54 |
55 | def set_variable(name, value)
56 | current_gon.gon[name] = value
57 | end
58 |
59 | def merge_variable(name, value)
60 | old_value = all_variables[name]
61 | if value.is_a?(Hash) && old_value.is_a?(Hash)
62 | value = old_value.deep_merge(value)
63 | end
64 | set_variable(name, value)
65 | end
66 |
67 | def push(data = {}, merge = false)
68 | raise 'Object must have each_pair method' unless data.respond_to? :each_pair
69 |
70 | if merge
71 | data.each_pair do |name, value|
72 | merge_variable(name.to_s, value)
73 | end
74 | else
75 | data.each_pair do |name, value|
76 | set_variable(name.to_s, value)
77 | end
78 | end
79 | end
80 |
81 | def all_variables
82 | current_gon ? current_gon.gon : {}
83 | end
84 |
85 | def clear
86 | current_gon.clear if current_gon
87 | end
88 |
89 | def rabl(*args)
90 | data, options = Gon::Rabl.handler(args)
91 | store_builder_data 'rabl', data, options
92 | end
93 |
94 | def jbuilder(*args)
95 | ensure_template_handler_is_defined
96 | data, options = Gon::Jbuilder.handler(args)
97 | store_builder_data 'jbuilder', data, options
98 | end
99 |
100 | def inspect
101 | 'Gon'
102 | end
103 |
104 | private
105 |
106 | def current_gon
107 | RequestStore.store[:gon]
108 | end
109 |
110 | def store_builder_data(builder, data, options)
111 | if options[:as]
112 | set_variable(options[:as].to_s, data)
113 | elsif data.is_a? Hash
114 | data.each { |k, v| set_variable(k, v) }
115 | else
116 | set_variable(builder, data)
117 | end
118 | end
119 |
120 | def public_method_name?(method)
121 | public_methods.include?(method.to_s[0..-2].to_sym)
122 | end
123 |
124 | # JbuilderTemplate will not be defined if jbuilder is required
125 | # before gon. By loading jbuilder again, JbuilderTemplate will
126 | # now be defined
127 | def ensure_template_handler_is_defined
128 | load 'jbuilder.rb' unless defined?(JbuilderTemplate)
129 | end
130 |
131 | end
132 | end
133 |
--------------------------------------------------------------------------------
/lib/gon/base.rb:
--------------------------------------------------------------------------------
1 | require 'ostruct'
2 |
3 | class Gon
4 | module Base
5 | VALID_OPTION_DEFAULTS = {
6 | namespace: 'gon',
7 | camel_case: false,
8 | camel_depth: 1,
9 | watch: false,
10 | need_tag: true,
11 | type: false,
12 | cdata: true,
13 | global_root: 'global',
14 | namespace_check: false,
15 | amd: false,
16 | nonce: nil
17 | }
18 |
19 | class << self
20 |
21 | def render_data(options = {})
22 | _o = define_options(options)
23 |
24 | script = formatted_data(_o)
25 | script = Gon::Escaper.escape_unicode(script)
26 | script = Gon::Escaper.javascript_tag(script, _o.type, _o.cdata, _o.nonce) if _o.need_tag
27 |
28 | script.html_safe
29 | end
30 |
31 | private
32 |
33 | def define_options(options)
34 | _o = OpenStruct.new
35 |
36 | VALID_OPTION_DEFAULTS.each do |opt_name, default|
37 | _o.send("#{opt_name}=", options.fetch(opt_name, default))
38 | end
39 | _o.watch = options[:watch] || !Gon.watch.all_variables.empty?
40 | _o.cameled = _o.camel_case
41 |
42 | _o
43 | end
44 |
45 | def formatted_data(_o)
46 | script = ''
47 | before, after = render_wrap(_o)
48 | script << before
49 |
50 | script << gon_variables(_o.global_root).
51 | map { |key, val| render_variable(_o, key, val) }.join
52 | script << (render_watch(_o) || '')
53 |
54 | script << after
55 | script
56 | end
57 |
58 | def render_wrap(_o)
59 | if _o.amd
60 | ["define('#{_o.namespace}',[],function(){var gon={};", 'return gon;});']
61 | else
62 | before = \
63 | if _o.namespace_check
64 | "window.#{_o.namespace}=window.#{_o.namespace}||{};"
65 | else
66 | "window.#{_o.namespace}={};"
67 | end
68 | [before, '']
69 | end
70 | end
71 |
72 | def render_variable(_o, key, value)
73 | js_key = convert_key(key, _o.cameled)
74 | if _o.amd
75 | "gon['#{js_key}']=#{to_json(value, _o.camel_depth)};"
76 | else
77 | "#{_o.namespace}.#{js_key}=#{to_json(value, _o.camel_depth)};"
78 | end
79 | end
80 |
81 | def render_watch(_o)
82 | if _o.watch and Gon::Watch.all_variables.present?
83 | if _o.amd
84 | Gon.watch.render_amd
85 | else
86 | Gon.watch.render
87 | end
88 | end
89 | end
90 |
91 | def to_json(value, camel_depth)
92 | # starts at 2 because 1 is the root key which is converted in the formatted_data method
93 | Gon::JsonDumper.dump convert_hash_keys(value, 2, camel_depth)
94 | end
95 |
96 | def convert_hash_keys(value, current_depth, max_depth)
97 | return value if current_depth > (max_depth.is_a?(Symbol) ? 1000 : max_depth)
98 |
99 | case value
100 | when Hash
101 | Hash[value.map { |k, v|
102 | [ convert_key(k, true), convert_hash_keys(v, current_depth + 1, max_depth) ]
103 | }]
104 | when Enumerable
105 | value.map { |v| convert_hash_keys(v, current_depth + 1, max_depth) }
106 | else
107 | value
108 | end
109 | end
110 |
111 | def gon_variables(global_root)
112 | data = {}
113 |
114 | if Gon.global.all_variables.present?
115 | if global_root.blank?
116 | data = Gon.global.all_variables
117 | else
118 | data[global_root.to_sym] = Gon.global.all_variables
119 | end
120 | end
121 |
122 | data.merge(Gon.all_variables)
123 | end
124 |
125 | def convert_key(key, camelize)
126 | cache = RequestStore.store[:gon_keys_cache] ||= {}
127 | cache["#{key}_#{camelize}"] ||= camelize ? key.to_s.camelize(:lower) : key.to_s
128 | end
129 |
130 | end
131 | end
132 | end
133 |
--------------------------------------------------------------------------------
/lib/gon/compatibility/old_rails.rb:
--------------------------------------------------------------------------------
1 | require 'securerandom'
2 |
3 | class Gon
4 | module ControllerHelpers
5 | private
6 |
7 | # override this since ActionDispatch::Request#uuid appears only in Rails 3.2.1
8 | def gon_request_uuid
9 | @gon_request_uuid ||= SecureRandom.uuid
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/lib/gon/env_finder.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module EnvFinder
3 | ENV_CONTROLLER_KEY = 'action_controller.instance'
4 | ENV_RESPONSE_KEY = 'action_controller.rescue.response'
5 |
6 | class << self
7 |
8 | def controller_env(options = {})
9 | options[:controller] ||
10 | (
11 | current_gon &&
12 | current_gon.env[ENV_CONTROLLER_KEY] ||
13 | current_gon.env[ENV_RESPONSE_KEY].
14 | instance_variable_get('@template').
15 | instance_variable_get('@controller')
16 | )
17 | end
18 |
19 | def template_path(options, extension)
20 | if options[:template]
21 | if right_extension?(extension, options[:template])
22 | options[:template]
23 | else
24 | [options[:template], extension].join('.')
25 | end
26 | else
27 | controller = controller_env(options).controller_path
28 | action = controller_env(options).action_name
29 | "app/views/#{controller}/#{action}.json.#{extension}"
30 | end
31 | end
32 |
33 | private
34 |
35 | def right_extension?(extension, template_path)
36 | File.extname(template_path) == ".#{extension}"
37 | end
38 |
39 | def current_gon
40 | RequestStore.store[:gon]
41 | end
42 |
43 | end
44 |
45 | end
46 | end
47 |
--------------------------------------------------------------------------------
/lib/gon/escaper.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module Escaper
3 | extend ActionView::Helpers::JavaScriptHelper
4 | extend ActionView::Helpers::TagHelper
5 |
6 | class << self
7 |
8 | def escape_unicode(javascript)
9 | if javascript
10 | result = escape_line_separator(javascript)
11 | javascript.html_safe? ? result.html_safe : result
12 | end
13 | end
14 |
15 | def javascript_tag(content, type, cdata, nonce)
16 | options = {}
17 | options.merge!( { type: 'text/javascript' } ) if type
18 | options.merge!( { nonce: nonce } ) if nonce
19 |
20 | content_tag(:script, javascript_cdata_section(content, cdata).html_safe, options)
21 | end
22 |
23 | def javascript_cdata_section(content, cdata)
24 | if cdata
25 | "\n//#{cdata_section("\n#{content}\n//")}\n"
26 | else
27 | "\n#{content}\n"
28 | end
29 | end
30 |
31 | private
32 |
33 | def escape_line_separator(javascript)
34 | javascript.gsub(/\\u2028/u, '
')
35 | end
36 |
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/gon/global.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | class Global < Gon
3 | class << self
4 |
5 | def all_variables
6 | @global_vars || {}
7 | end
8 |
9 | def clear
10 | @global_vars = {}
11 | end
12 |
13 | def inspect
14 | 'Gon::Global'
15 | end
16 |
17 | def rabl(*args)
18 | data, options = Gon::Rabl.handler(args, true)
19 | store_builder_data 'rabl', data, options
20 | end
21 |
22 | def jbuilder(*args)
23 | ensure_template_handler_is_defined
24 | data, options = Gon::Jbuilder.handler(args, true)
25 | store_builder_data 'jbuilder', data, options
26 | end
27 |
28 | private
29 |
30 | def get_variable(name)
31 | @global_vars ||= {}
32 | @global_vars[name]
33 | end
34 |
35 | def set_variable(name, value)
36 | @global_vars ||= {}
37 | @global_vars[name] = value
38 | end
39 |
40 | end
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/lib/gon/helpers.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module ViewHelpers
3 | def include_gon(options = {})
4 | if variables_for_request_present?
5 | Gon::Base.render_data(options)
6 | elsif Gon.global.all_variables.present? || options[:init].present?
7 | Gon.clear
8 | Gon::Base.render_data(options)
9 | else
10 | ''
11 | end
12 | end
13 |
14 | def include_gon_amd(options={})
15 | Gon::Base.render_data(options.merge({amd: true}))
16 | end
17 |
18 | private
19 |
20 | def variables_for_request_present?
21 | current_gon && current_gon.gon
22 | end
23 |
24 | def current_gon
25 | RequestStore.store[:gon]
26 | end
27 | end
28 |
29 | module ControllerHelpers
30 | def gon
31 | if wrong_gon_request?
32 | gon_request = Request.new(request.env)
33 | gon_request.id = gon_request_uuid
34 | RequestStore.store[:gon] = gon_request
35 | end
36 | Gon
37 | end
38 |
39 | private
40 |
41 | def wrong_gon_request?
42 | current_gon.blank? || current_gon.id != gon_request_uuid
43 | end
44 |
45 | def current_gon
46 | RequestStore.store[:gon]
47 | end
48 |
49 | def gon_request_uuid
50 | request.uuid
51 | end
52 | end
53 | end
54 |
55 | ActiveSupport.on_load :action_view do
56 | ActionView::Base.send :include, Gon::ViewHelpers
57 | end
58 | ActiveSupport.on_load :action_controller do
59 | ActionController::Base.send :include, Gon::ControllerHelpers
60 | end
61 |
--------------------------------------------------------------------------------
/lib/gon/jbuilder.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module Jbuilder
3 | class << self
4 |
5 | def handler(args, global = false)
6 | options = parse_options_from args
7 | valid_options? options, global
8 |
9 | controller = Gon::EnvFinder.controller_env(options)
10 | controller_name = global ? '' : controller.controller_path
11 |
12 | parser = Gon::Jbuilder::Parser.new(
13 | template_path: Gon::EnvFinder.template_path(options, 'jbuilder'),
14 | controller: controller,
15 | controller_name: controller_name,
16 | locals: options[:locals]
17 | )
18 | data = parser.parse!
19 |
20 | [data, options]
21 | end
22 |
23 | private
24 |
25 | def valid_options?(options, global)
26 | if global && !options[:template]
27 | raise 'You should provide :template when use jbuilder with global variables'
28 | end
29 | end
30 |
31 | def parse_options_from(args)
32 | if old_api? args
33 | text = "[DEPRECATION] view_path argument is now optional. "
34 | text << "If you need to specify it, "
35 | text << "please use gon.jbuilder(:template => 'path')"
36 | warn text
37 |
38 | args.extract_options!.merge(:template => args[0])
39 | elsif new_api? args
40 | args.first
41 | else
42 | {}
43 | end
44 | end
45 |
46 | def old_api?(args)
47 | args.first.is_a? String
48 | end
49 |
50 | def new_api?(args)
51 | args.first.is_a? Hash
52 | end
53 |
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/gon/jbuilder/parser.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module Jbuilder
3 | class Parser
4 | include ::ActionView::Helpers
5 |
6 | attr_accessor :template_location, :controller, :_controller_name, :locals
7 |
8 | def initialize(parse_params)
9 | @template_location = parse_params[:template_path]
10 | @controller = parse_params[:controller]
11 | @_controller_name = parse_params[:controller_name]
12 | @locals = parse_params[:locals] || {}
13 | end
14 |
15 | def parse!
16 | assign_controller_variables controller
17 | eval_controller_helpers controller
18 | eval_controller_url_helpers controller
19 | locals['__controller'] = controller
20 | wrap_locals_in_methods locals
21 |
22 | partials = find_partials(File.readlines(template_location))
23 | source = partials.join('')
24 |
25 | parse_source source, controller
26 | end
27 |
28 | def assign_controller_variables(controller)
29 | controller.instance_variables.each do |name|
30 | self.instance_variable_set \
31 | name,
32 | controller.instance_variable_get(name)
33 | end
34 | end
35 |
36 | def eval_controller_helpers(controller)
37 | controller._helper_methods.each do |meth|
38 | self.class.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
39 | def #{meth}(*args, &blk) # def current_user(*args, &blk)
40 | __controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
41 | end # end
42 | ruby_eval
43 | end
44 | end
45 |
46 | def eval_controller_url_helpers(controller)
47 | if defined?(Rails) && Rails.respond_to?(:application)
48 | Rails.application.routes.url_helpers.instance_methods.each do |meth|
49 | self.class.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
50 | def #{meth}(*args, &blk) # def user_path(*args, &blk)
51 | __controller.send(%(#{meth}), *args, &blk) # controller.send(:user_path, *args, &blk)
52 | end # end
53 | ruby_eval
54 | end
55 | end
56 | end
57 |
58 | def wrap_locals_in_methods(locals)
59 | locals.each do |name, value|
60 | self.class.class_eval do
61 | define_method "#{name}" do
62 | return value
63 | end
64 | end
65 | end
66 | end
67 |
68 | def parse_source(source, controller)
69 | output = ::JbuilderTemplate.encode(controller) do |json|
70 | eval source
71 | end
72 | JSON.parse(output)
73 | end
74 |
75 | def parse_partial(partial_line)
76 | path = partial_line.match(/['"]([^'"]*)['"]/)[1]
77 | path = parse_path path
78 | options_hash = partial_line.match(/,(.*)/)[1]
79 |
80 | set_options_from_hash(options_hash) if options_hash.present?
81 |
82 | find_partials File.readlines(path)
83 | end
84 |
85 | def set_options_from_hash(options_hash)
86 | options = eval "{#{options_hash}}"
87 | options.each do |name, val|
88 | self.instance_variable_set("@#{name.to_s}", val)
89 | eval "def #{name}; self.instance_variable_get('@' + '#{name.to_s}'); end"
90 | end
91 | end
92 |
93 | def parse_path(path)
94 | return path if File.exists?(path)
95 | if (splitted = path.split('/')).blank?
96 | raise 'Something wrong with partial path in your jbuilder templates'
97 | elsif splitted.size == 1
98 | splitted.shift(@_controller_name)
99 | end
100 | construct_path(splitted)
101 | end
102 |
103 | def construct_path(args)
104 | last_arg = args.pop
105 | tmp_path = 'app/views/' + args.join('/')
106 | path = path_with_ext(tmp_path + "/_#{last_arg}")
107 | path || path_with_ext(tmp_path + "/#{last_arg}")
108 | end
109 |
110 | def path_with_ext(path)
111 | return path if File.exists?(path)
112 | return "#{path}.jbuilder" if File.exists?("#{path}.jbuilder")
113 | return "#{path}.json.jbuilder" if File.exists?("#{path}.json.jbuilder")
114 | end
115 |
116 | def find_partials(lines = [])
117 | lines.map do |line|
118 | if line =~ /partial!/
119 | parse_partial line
120 | else
121 | line
122 | end
123 | end.flatten
124 | end
125 |
126 | end
127 | end
128 | end
129 |
--------------------------------------------------------------------------------
/lib/gon/json_dumper.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module JsonDumper
3 | # Taken from ERB::Util
4 | JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
5 | JSON_ESCAPE = {
6 | "&" => '\u0026',
7 | ">" => '\u003e',
8 | "<" => '\u003c',
9 | "\u2028" => '\u2028',
10 | "\u2029" => '\u2029'
11 | }
12 |
13 | def self.dump(object)
14 | dumped_json = MultiJson.dump object,
15 | mode: :compat, escape_mode: :xss_safe, time_format: :ruby
16 | escape(dumped_json)
17 | end
18 |
19 | def self.escape(json)
20 | json.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/gon/rabl.rb:
--------------------------------------------------------------------------------
1 | require 'action_view'
2 |
3 | begin
4 | require 'rabl' # use rabl gem if it's available
5 | rescue LoadError
6 | end
7 | begin
8 | require 'rabl-rails' # use rabl-rails gem if it's available
9 | rescue LoadError
10 | end
11 |
12 | class Gon
13 | module Rabl
14 | class << self
15 |
16 | def handler(args, global = false)
17 | options = parse_options_from args, global
18 | if global && !options[:template]
19 | raise 'You should provide :template when use rabl with global variables'
20 | end
21 |
22 | data = parse_rabl \
23 | Gon::EnvFinder.template_path(options, 'rabl'),
24 | Gon::EnvFinder.controller_env(options),
25 | options[:locals]
26 |
27 | [data, options]
28 | end
29 |
30 | private
31 |
32 | def parse_rabl(rabl_path, controller, locals)
33 | if defined? ::Rabl
34 | parse_with_rabl rabl_path, controller, locals
35 | elsif defined? ::RablRails
36 | parse_with_rabl_rails rabl_path, controller, locals
37 | else
38 | raise 'rabl or rabl-rails must be required in order to use gon.rabl'
39 | end
40 | end
41 |
42 | def parse_with_rabl(rabl_path, controller, locals)
43 | locals ||= {}
44 | source = File.read(rabl_path)
45 | include_helpers
46 | rabl_engine = ::Rabl::Engine.new(source, :format => 'json', :template => rabl_path)
47 | output = rabl_engine.render(controller, locals)
48 | JSON.parse(output)
49 | end
50 |
51 | def parse_with_rabl_rails(rabl_path, controller, locals)
52 | locals ||= {}
53 | source = File.read(rabl_path)
54 | original_formats = controller.formats
55 | controller.formats = [:json]
56 | view_context = controller.send(:view_context)
57 | locals.each { |k, v| view_context.assigns[k.to_s] = v }
58 | output = RablRails::Library.instance.get_rendered_template(source, view_context)
59 | controller.formats = original_formats
60 | JSON.parse(output)
61 | end
62 |
63 | def parse_options_from(args, global)
64 | if old_api? args
65 | unless global
66 | text = "[DEPRECATION] view_path argument is now optional. "
67 | text << "If you need to specify it, "
68 | text << "please use gon.rabl(:template => 'path')"
69 | warn text
70 | end
71 |
72 | args.extract_options!.merge(:template => args[0])
73 | elsif new_api? args
74 | args.first
75 | else
76 | {}
77 | end
78 | end
79 |
80 | def include_helpers
81 | unless ::Rabl::Engine.include? ::ActionView::Helpers
82 | ::Rabl::Engine.send(:include, ::ActionView::Helpers)
83 | end
84 | end
85 |
86 | def old_api?(args)
87 | args.first.is_a? String
88 | end
89 |
90 | def new_api?(args)
91 | args.first.is_a? Hash
92 | end
93 |
94 | end
95 | end
96 | end
97 |
--------------------------------------------------------------------------------
/lib/gon/request.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | class Request
3 | attr_reader :env, :gon
4 | attr_accessor :id
5 |
6 | def initialize(environment)
7 | @env = environment
8 | @gon = {}
9 | end
10 |
11 | def clear
12 | @gon = {}
13 | end
14 |
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/gon/spec_helpers.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | module SpecHelper
3 | module Rails
4 | extend ActiveSupport::Concern
5 |
6 | module ClassMethods
7 | module GonSession
8 | def process(*, **)
9 | # preload threadlocal & store controller instance
10 | if controller.is_a? ActionController::Base
11 | controller.gon
12 | Gon.send(:current_gon).env[Gon::EnvFinder::ENV_CONTROLLER_KEY] =
13 | controller
14 | end
15 | super
16 | end
17 | end
18 |
19 | def new(*)
20 | super.extend(GonSession)
21 | end
22 | end
23 | end
24 | end
25 | end
26 |
27 | if ENV['RAILS_ENV'] == 'test' && defined?(ActionController::TestCase::Behavior)
28 | ActionController::TestCase::Behavior.send :include, Gon::SpecHelper::Rails
29 | end
30 |
31 |
--------------------------------------------------------------------------------
/lib/gon/version.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | VERSION = '6.4.0'
3 | end
4 |
--------------------------------------------------------------------------------
/lib/gon/watch.rb:
--------------------------------------------------------------------------------
1 | class Gon
2 | class Watch < Gon
3 | class << self
4 |
5 | JS_FUNCTION = File.read(File.expand_path('../../../js/watch.js', __FILE__))
6 |
7 | def render
8 | JS_FUNCTION + "window.gon.watchedVariables=#{Gon::JsonDumper.dump all_variables};"
9 | end
10 |
11 | def render_amd
12 | JS_FUNCTION + "gon.watchedVariables=#{Gon::JsonDumper.dump all_variables};"
13 | end
14 |
15 | def all_variables
16 | @watch_variables || {}
17 | end
18 |
19 | def clear
20 | @watch_variables = {}
21 | end
22 |
23 | private
24 |
25 | def set_variable(name, value)
26 | if return_variable?(name)
27 | return_variable value
28 | elsif Gon.send(:current_gon)
29 | variable = {}
30 | @watch_variables ||= {}
31 | env = Gon.send(:current_gon).env
32 | variable['url'] = env['ORIGINAL_FULLPATH'] || env['REQUEST_URI']
33 | variable['method'] = env['REQUEST_METHOD']
34 | variable['name'] = name
35 |
36 | @watch_variables[name] = variable
37 | super
38 | end
39 | end
40 |
41 | def return_variable?(variable)
42 | controller = Gon::EnvFinder.controller_env
43 | params = controller.params
44 | variable = variable.to_s.gsub('=', '')
45 |
46 | controller.request.xhr? &&
47 | params[:gon_return_variable] &&
48 | params[:gon_watched_variable] == variable
49 | end
50 |
51 | def return_variable(value)
52 | controller = Gon::EnvFinder.controller_env
53 | controller.render json: Gon::Escaper.escape_unicode(Gon::JsonDumper.dump value)
54 | end
55 |
56 | end
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/spec/gon/basic_spec.rb:
--------------------------------------------------------------------------------
1 | describe Gon do
2 |
3 | before(:each) do
4 | Gon.clear
5 | end
6 |
7 | describe '#all_variables' do
8 |
9 | it 'returns all variables in hash' do
10 | Gon.a = 1
11 | Gon.b = 2
12 | Gon.c = Gon.a + Gon.b
13 | expect(Gon.c).to eq(3)
14 | expect(Gon.all_variables).to eq({ 'a' => 1, 'b' => 2, 'c' => 3 })
15 | end
16 |
17 | it 'supports all data types' do
18 | Gon.int = 1
19 | Gon.float = 1.1
20 | Gon.string = 'string'
21 | Gon.symbol = :symbol
22 | Gon.array = [1, 'string']
23 | Gon.hash_var = { :a => 1, :b => '2' }
24 | Gon.hash_w_array = { :a => [2, 3] }
25 | Gon.klass = Hash
26 | end
27 |
28 | it 'can be filled with dynamic named variables' do
29 | check = {}
30 | 3.times do |i|
31 | Gon.set_variable("variable#{i}", i)
32 | check["variable#{i}"] = i
33 | end
34 |
35 | expect(Gon.all_variables).to eq(check)
36 | end
37 |
38 | it 'can set and get variable with dynamic name' do
39 | var_name = "variable#{rand}"
40 |
41 | Gon.set_variable(var_name, 1)
42 | expect(Gon.get_variable(var_name)).to eq(1)
43 | end
44 |
45 | it 'can be support new push syntax' do
46 | Gon.push({ :int => 1, :string => 'string' })
47 | expect(Gon.all_variables).to eq({ 'int' => 1, 'string' => 'string' })
48 | end
49 |
50 | it 'push with wrong object' do
51 | expect {
52 | Gon.push(String.new('string object'))
53 | }.to raise_error('Object must have each_pair method')
54 | end
55 |
56 | describe "#merge_variable" do
57 | it 'deep merges the same key' do
58 | Gon.merge_variable(:foo, { bar: { tar: 12 }, car: 23 })
59 | Gon.merge_variable(:foo, { bar: { dar: 21 }, car: 12 })
60 | expect(Gon.get_variable(:foo)).to eq(bar: { tar: 12, dar: 21 }, car: 12)
61 | end
62 |
63 | it 'merges on push with a flag' do
64 | Gon.push(foo: { bar: 1 })
65 | Gon.push({ foo: { tar: 1 } }, :merge)
66 | expect(Gon.get_variable("foo")).to eq(bar: 1, tar: 1)
67 | end
68 |
69 | context 'overrides key' do
70 | specify "the previous value wasn't hash" do
71 | Gon.merge_variable(:foo, 2)
72 | Gon.merge_variable(:foo, { a: 1 })
73 | expect(Gon.get_variable(:foo)).to eq(a: 1)
74 | end
75 |
76 | specify "the new value isn't a hash" do
77 | Gon.merge_variable(:foo, { a: 1 })
78 | Gon.merge_variable(:foo, 2)
79 | expect(Gon.get_variable(:foo)).to eq(2)
80 | end
81 | end
82 | end
83 |
84 | end
85 |
86 | describe '#include_gon' do
87 |
88 | before(:each) do
89 | Gon::Request.
90 | instance_variable_set(:@request_id, request.object_id)
91 | expect(ActionView::Base.instance_methods).to include(:include_gon)
92 | @base = ActionView::Base.new(nil,{}, nil)
93 | @base.request = request
94 | end
95 |
96 | it 'outputs correct js with an integer' do
97 | Gon.int = 1
98 | expect(@base.include_gon).to eq(wrap_script(
99 | 'window.gon={};' +
100 | 'gon.int=1;'))
101 | end
102 |
103 | it 'outputs correct js with a string' do
104 | Gon.str = %q(a'b"c)
105 | expect(@base.include_gon).to eq(wrap_script(
106 | 'window.gon={};' +
107 | %q(gon.str="a'b\"c";))
108 | )
109 | end
110 |
111 | it 'outputs correct js with a script string' do
112 | Gon.str = %q()
113 | escaped_str = "\\u003c/script\\u003e\\u003cscript\\u003ealert('!')\\u003c/script\\u003e"
114 | expect(@base.include_gon).to eq(wrap_script(
115 | 'window.gon={};' +
116 | %Q(gon.str="#{escaped_str}";))
117 | )
118 | end
119 |
120 | it 'outputs correct js with an integer and type' do
121 | Gon.int = 1
122 | expect(@base.include_gon(type: true)).to eq('')
128 | end
129 |
130 | it 'outputs correct js with an integer, camel-case and namespace' do
131 | Gon.int_cased = 1
132 | expect(@base.include_gon(camel_case: true, namespace: 'camel_cased')).to eq(
133 | wrap_script('window.camel_cased={};' +
134 | 'camel_cased.intCased=1;')
135 | )
136 | end
137 |
138 | it 'outputs correct js with camel_depth = :recursive' do
139 | Gon.test_hash = { test_depth_one: { test_depth_two: 1 } }
140 | expect(@base.include_gon(camel_case: true, camel_depth: :recursive)).to eq(
141 | wrap_script('window.gon={};' +
142 | 'gon.testHash={"testDepthOne":{"testDepthTwo":1}};')
143 | )
144 | end
145 |
146 | it 'outputs correct js with camel_depth = 2' do
147 | Gon.test_hash = { test_depth_one: { test_depth_two: 1 } }
148 | expect(@base.include_gon(camel_case: true, camel_depth: 2)).to eq(
149 | wrap_script('window.gon={};' +
150 | 'gon.testHash={"testDepthOne":{"test_depth_two":1}};')
151 | )
152 | end
153 |
154 | it 'outputs correct js for an array with camel_depth = :recursive' do
155 | Gon.test_hash = { test_depth_one: [{ test_depth_two: 1 }, { test_depth_two: 2 }] }
156 | expect(@base.include_gon(camel_case: true, camel_depth: :recursive)).to eq( \
157 | wrap_script('window.gon={};' +
158 | 'gon.testHash={"testDepthOne":[{"testDepthTwo":1},{"testDepthTwo":2}]};')
159 | )
160 | end
161 |
162 | it 'outputs correct key with camel_case option set alternately ' do
163 | Gon.test_hash = 1
164 | @base.include_gon(camel_case: true)
165 |
166 | expect(@base.include_gon(camel_case: false)).to eq(
167 | wrap_script('window.gon={};' +
168 | 'gon.test_hash=1;')
169 | )
170 | end
171 |
172 | it 'outputs correct js with an integer and without tag' do
173 | Gon.int = 1
174 | expect(@base.include_gon(need_tag: false)).to eq( \
175 | 'window.gon={};' +
176 | 'gon.int=1;'
177 | )
178 | end
179 |
180 | it 'outputs correct js without variables, without tag and gon init if before there was data' do
181 | Gon::Request.
182 | instance_variable_set(:@request_id, 123)
183 | Gon::Request.instance_variable_set(:@request_env, { 'gon' => { :a => 1 } })
184 | expect(@base.include_gon(need_tag: false, init: true)).to eq( \
185 | 'window.gon={};'
186 | )
187 | end
188 |
189 | it 'outputs correct js without variables, without tag and gon init' do
190 | expect(@base.include_gon(need_tag: false, init: true)).to eq( \
191 | 'window.gon={};'
192 | )
193 | end
194 |
195 | it 'outputs correct js without variables, without tag, gon init and an integer' do
196 | Gon.int = 1
197 | expect(@base.include_gon(need_tag: false, init: true)).to eq( \
198 | 'window.gon={};' +
199 | 'gon.int=1;'
200 | )
201 | end
202 |
203 | it 'outputs correct js without cdata, without type, gon init and an integer' do
204 | Gon.int = 1
205 | expect(@base.include_gon(cdata: false, type: false)).to eq(
206 | wrap_script(
207 | "\n" +
208 | 'window.gon={};' +
209 | 'gon.int=1;' +
210 | "\n", false)
211 | )
212 | end
213 |
214 | it 'outputs correct js with type text/javascript' do
215 | expect(@base.include_gon(need_type: true, init: true)).to eq(wrap_script('window.gon={};'))
216 | end
217 |
218 | it 'outputs correct js with namespace check' do
219 | expect(@base.include_gon(namespace_check: true)).to eq(wrap_script('window.gon=window.gon||{};'))
220 | end
221 |
222 | it 'outputs correct js without namespace check' do
223 | expect(@base.include_gon(namespace_check: false)).to eq(wrap_script('window.gon={};'))
224 | end
225 |
226 | context "without a current_gon instance" do
227 |
228 | before(:each) do
229 | RequestStore.store[:gon] = nil
230 | allow(Gon).to receive(:current_gon).and_return(nil)
231 | end
232 |
233 | it "does not raise an exception" do
234 | expect { @base.include_gon }.to_not raise_error
235 | end
236 |
237 | it 'outputs correct js' do
238 | expect(@base.include_gon).to eq("")
239 | end
240 |
241 | it 'outputs correct js with init' do
242 | expect(@base.include_gon(init: true)).to eq(wrap_script('window.gon={};'))
243 | end
244 |
245 | end
246 |
247 | end
248 |
249 | describe '#include_gon_amd' do
250 |
251 | before(:each) do
252 | Gon::Request.
253 | instance_variable_set(:@request_id, request.object_id)
254 | @base = ActionView::Base.new(nil, {}, nil)
255 | @base.request = request
256 | end
257 |
258 | it 'is included in ActionView::Base as a helper' do
259 | expect(ActionView::Base.instance_methods).to include(:include_gon_amd)
260 | end
261 |
262 | it 'outputs correct js without variables' do
263 | expect(@base.include_gon_amd).to eq( wrap_script( \
264 | 'define(\'gon\',[],function(){'+
265 | 'var gon={};return gon;'+
266 | '});')
267 | )
268 | end
269 |
270 | it 'outputs correct js with an integer' do
271 | Gon.int = 1
272 |
273 | expect(@base.include_gon_amd).to eq( wrap_script(
274 | 'define(\'gon\',[],function(){'+
275 | 'var gon={};gon[\'int\']=1;return gon;'+
276 | '});')
277 | )
278 | end
279 |
280 | it 'outputs correct module name when given a namespace' do
281 | expect(@base.include_gon_amd(namespace: 'data')).to eq(wrap_script(
282 | 'define(\'data\',[],function(){'+
283 | 'var gon={};return gon;'+
284 | '});')
285 | )
286 | end
287 | end
288 |
289 | it 'returns exception if try to set public method as variable' do
290 | expect { Gon.all_variables = 123 }.to raise_error(RuntimeError)
291 | expect { Gon.rabl = 123 }.to raise_error(RuntimeError)
292 | end
293 |
294 | describe '#check_for_rabl_and_jbuilder' do
295 |
296 | let(:controller) { ActionController::Base.new }
297 |
298 | it 'should be able to handle constants array (symbols)' do
299 | allow(Gon).to receive(:constants) { Gon.constants }
300 | expect { Gon.rabl :template => 'spec/test_data/sample.rabl', :controller => controller }.not_to raise_error
301 | expect { Gon.jbuilder :template => 'spec/test_data/sample.json.jbuilder', :controller => controller }.not_to raise_error
302 | end
303 | end
304 | end
305 |
--------------------------------------------------------------------------------
/spec/gon/global_spec.rb:
--------------------------------------------------------------------------------
1 | describe Gon::Global do
2 |
3 | before(:each) do
4 | Gon::Global.clear
5 | Gon::Request.instance_variable_set(:@request_env, nil)
6 | end
7 |
8 | describe '#all_variables' do
9 |
10 | it 'returns all variables in hash' do
11 | Gon.global.a = 1
12 | Gon.global.b = 2
13 | Gon.global.c = Gon.global.a + Gon.global.b
14 | expect(Gon.global.c).to eq(3)
15 | expect(Gon.global.all_variables).to eq({ 'a' => 1, 'b' => 2, 'c' => 3 })
16 | end
17 |
18 | it 'supports all data types' do
19 | Gon.global.int = 1
20 | Gon.global.float = 1.1
21 | Gon.global.string = 'string'
22 | Gon.global.symbol = :symbol
23 | Gon.global.array = [1, 'string']
24 | Gon.global.hash_var = { :a => 1, :b => '2' }
25 | Gon.global.hash_w_array = { :a => [2, 3] }
26 | Gon.global.klass = Hash
27 | end
28 |
29 | end
30 |
31 | describe '#include_gon' do
32 |
33 | before(:each) do
34 | Gon.clear
35 | expect(ActionView::Base.instance_methods).to include(:include_gon)
36 | @base = ActionView::Base.new(nil, {}, nil)
37 | @base.request = request
38 | end
39 |
40 | it 'outputs correct js with an integer' do
41 | Gon.global.int = 1
42 | expect(@base.include_gon).to eq("")
48 | end
49 |
50 | it 'outputs correct js with an integer and integer in Gon' do
51 | Gon.int = 1
52 | Gon.global.int = 1
53 | expect(@base.include_gon).to eq("")
60 | end
61 |
62 | it 'outputs correct js with a string' do
63 | Gon.global.str = %q(a'b"c)
64 | expect(@base.include_gon).to eq("")
70 | end
71 |
72 | it 'outputs correct js with a script string' do
73 | Gon.global.str = %q()
74 | escaped_str = "\\u003c/script\\u003e\\u003cscript\\u003ealert('!')\\u003c/script\\u003e"
75 | expect(@base.include_gon).to eq("")
81 | end
82 |
83 | it 'outputs correct js with a unicode line separator' do
84 | Gon.global.str = "\u2028"
85 | expect(@base.include_gon).to eq("")
91 | end
92 |
93 | it 'outputs locally overridden value' do
94 | Gon.str = 'local value'
95 | Gon.global.str = 'global value'
96 | expect(@base.include_gon(global_root: '')).to eq("")
102 | end
103 |
104 | it "includes the tag attributes in the script tag" do
105 | Gon.global.int = 1
106 | expect(@base.include_gon(nonce: 'test')).to eq("")
112 | end
113 |
114 | end
115 |
116 | it 'returns exception if try to set public method as variable' do
117 | expect { Gon.global.all_variables = 123 }.to raise_error(RuntimeError)
118 | expect { Gon.global.rabl = 123 }.to raise_error(RuntimeError)
119 | end
120 |
121 | context 'with jbuilder and rabl' do
122 |
123 | before :each do
124 | controller.instance_variable_set('@objects', objects)
125 | end
126 |
127 | let(:controller) { ActionController::Base.new }
128 | let(:objects) { [1, 2] }
129 |
130 | it 'works fine with rabl' do
131 | Gon.global.rabl :template => 'spec/test_data/sample.rabl', :controller => controller
132 | expect(Gon.global.objects.length).to eq(2)
133 | end
134 |
135 | it 'works fine with jbuilder' do
136 | Gon.global.jbuilder :template => 'spec/test_data/sample.json.jbuilder', :controller => controller
137 | expect(Gon.global.objects.length).to eq(2)
138 | end
139 |
140 | it 'should throw exception, if use rabl or jbuilder without :template' do
141 | expect { Gon.global.rabl }.to raise_error(RuntimeError)
142 | expect { Gon.global.jbuilder }.to raise_error(RuntimeError)
143 | end
144 |
145 | end
146 | end
147 |
--------------------------------------------------------------------------------
/spec/gon/jbuilder_spec.rb:
--------------------------------------------------------------------------------
1 | describe Gon do
2 |
3 | describe '.jbuilder' do
4 | context 'render jbuilder templates' do
5 |
6 | before do
7 | Gon.clear
8 | controller.instance_variable_set('@objects', objects)
9 | end
10 |
11 | let(:controller) { ActionController::Base.new }
12 | let(:objects) { [1, 2] }
13 |
14 | it 'render json from jbuilder template' do
15 | Gon.jbuilder :template => 'spec/test_data/sample.json.jbuilder', :controller => controller
16 | expect(Gon.objects.length).to eq(2)
17 | end
18 |
19 | it 'render json from jbuilder template with locals' do
20 | Gon.jbuilder :template => 'spec/test_data/sample_with_locals.json.jbuilder',
21 | :controller => controller,
22 | :locals => { :some_local => 1234, :some_complex_local => OpenStruct.new(:id => 1234) }
23 | expect(Gon.some_local).to eq(1234)
24 | expect(Gon.some_complex_local_id).to eq(1234)
25 | end
26 |
27 | it 'render json from jbuilder template with locals' do
28 | Gon.jbuilder :template => 'spec/test_data/sample_with_helpers.json.jbuilder', :controller => controller
29 | expect(Gon.date).to eq('about 6 hours')
30 | end
31 |
32 | it 'render json from jbuilder template with controller methods' do
33 | class << controller
34 | def private_controller_method
35 | 'gon test helper works'
36 | end
37 | helper_method :private_controller_method
38 | private :private_controller_method
39 | end
40 |
41 | Gon.jbuilder :template => 'spec/test_data/sample_with_controller_method.json.jbuilder', :controller => controller
42 | expect(Gon.data_from_method).to eq('gon test helper works')
43 | end
44 |
45 | it 'render json from jbuilder template with a partial' do
46 | controller.view_paths << 'spec/test_data'
47 | Gon.jbuilder :template => 'spec/test_data/sample_with_partial.json.jbuilder', :controller => controller
48 | expect(Gon.objects.length).to eq(2)
49 | end
50 |
51 | context 'within Rails' do
52 | before do
53 | module ::Rails
54 | end
55 |
56 | allow(Rails).to receive_message_chain("application.routes.url_helpers.instance_methods") { [:user_path] }
57 | controller.instance_variable_set('@user_id', 1)
58 | end
59 |
60 | after do
61 | Object.send(:remove_const, :Rails)
62 | end
63 |
64 | it 'includes url_helpers' do
65 | expect(controller).to receive(:user_path) { |id| "/users/#{id}" }
66 | Gon.jbuilder :template => 'spec/test_data/sample_url_helpers.json.jbuilder', :controller => controller
67 | expect(Gon.url).to eq '/users/1'
68 | end
69 | end
70 |
71 | end
72 |
73 | end
74 |
75 | end
76 |
--------------------------------------------------------------------------------
/spec/gon/rabl_spec.rb:
--------------------------------------------------------------------------------
1 | describe Gon do
2 | describe '.rabl' do
3 |
4 | before :each do
5 | Gon.clear
6 | controller.instance_variable_set('@objects', objects)
7 | end
8 |
9 | let(:controller) { ActionController::Base.new }
10 | let(:objects) { [1, 2] }
11 |
12 | context 'render template with deprecation' do
13 | it 'still works' do
14 | Gon.rabl 'spec/test_data/sample.rabl', :controller => controller
15 | expect(Gon.objects.length).to eq(2)
16 | end
17 | end
18 |
19 | context 'option locals' do
20 | it 'works without locals object properly' do
21 | Gon.rabl(
22 | :template => 'spec/test_data/sample.rabl',
23 | :controller => controller
24 | )
25 | expect(Gon.objects.map { |it| it['object']['inspect'] }).to eq(%w(1 2))
26 | end
27 |
28 | it 'works with different locals object' do
29 | Gon.rabl(
30 | :template => 'spec/test_data/sample.rabl',
31 | :controller => controller,
32 | :locals => { :objects => [3, 4] }
33 | )
34 | expect(Gon.objects.map { |it| it['object']['inspect'] }).to eq(%w(3 4))
35 | end
36 | end
37 |
38 | it 'works if rabl is included' do
39 | Gon.rabl :template => 'spec/test_data/sample.rabl', :controller => controller
40 | expect(Gon.objects.length).to eq(2)
41 | end
42 |
43 | it 'works with ActionView::Helpers' do
44 | Gon.rabl :template => 'spec/test_data/sample_with_helpers.rabl', :controller => controller
45 | expect(Gon.objects.first['object']['time_ago']).to eq('about 6 hours')
46 | end
47 |
48 | it 'raise exception if rabl is not included' do
49 | Gon.send :remove_const, 'Rabl'
50 | expect { Gon.rabl :template => 'spec/test_data/sample.rabl', :controller => controller }.to raise_error(NameError)
51 | load 'rabl.rb'
52 | load 'gon/rabl.rb'
53 | end
54 |
55 | context '.template_path' do
56 | context 'template is specified' do
57 |
58 | it 'add the extension if not included in the template name' do
59 | expect(Gon::EnvFinder.send(:template_path, { :template => 'spec/test_data/sample' }, 'rabl')).to eql('spec/test_data/sample.rabl')
60 | end
61 |
62 | it 'return the specified template' do
63 | expect(Gon::EnvFinder.send(:template_path, { :template => 'spec/test_data/sample.rabl' }, 'rabl')).to eql('spec/test_data/sample.rabl')
64 | end
65 |
66 | end
67 |
68 | context 'template is not specified' do
69 |
70 | before do
71 | Gon.clear
72 | controller.instance_variable_set('@objects', objects)
73 | controller.action_name = 'show'
74 | end
75 |
76 | let(:controller) { ActionController::Base.new }
77 | let(:objects) { [1, 2] }
78 |
79 | context 'the action doesn as a template at a different format' do
80 | it 'return the same template as the action with rabl extension' do
81 | expect(Gon::EnvFinder.send(:template_path, { :controller => controller }, 'rabl')).to eql('app/views/action_controller/base/show.json.rabl')
82 | end
83 | end
84 |
85 | end
86 | end
87 |
88 | end
89 |
90 | end
91 |
--------------------------------------------------------------------------------
/spec/gon/templates_spec.rb:
--------------------------------------------------------------------------------
1 | describe Gon do
2 |
3 | describe '.template_path' do
4 | context 'template is specified' do
5 |
6 | it 'add the extension if not included in the template name' do
7 | expect(Gon::EnvFinder.send(:template_path, { :template => 'spec/test_data/sample' }, 'jbuilder')).to eql('spec/test_data/sample.jbuilder')
8 | end
9 |
10 | it 'return the specified template' do
11 | expect(Gon::EnvFinder.send(:template_path, { :template => 'spec/test_data/sample.jbuilder' }, 'jbuilder')).to eql('spec/test_data/sample.jbuilder')
12 | end
13 |
14 | end
15 |
16 | context 'template is not specified' do
17 |
18 | before do
19 | Gon.clear
20 | controller.instance_variable_set('@objects', objects)
21 | controller.action_name = 'show'
22 | end
23 |
24 | let(:controller) { ActionController::Base.new }
25 | let(:objects) { [1, 2] }
26 |
27 | context 'the action doesn as a template at a different format' do
28 | it 'return the same template as the action with rabl extension' do
29 | expect(Gon::EnvFinder.send(:template_path, { :controller => controller }, 'jbuilder')).to eql('app/views/action_controller/base/show.json.jbuilder')
30 | end
31 | end
32 |
33 | end
34 | end
35 |
36 | end
37 |
--------------------------------------------------------------------------------
/spec/gon/thread_spec.rb:
--------------------------------------------------------------------------------
1 | class GonTestWorker
2 | include Gon::ControllerHelpers
3 |
4 | def request
5 | @request ||= ActionDispatch::TestRequest.create
6 | end
7 |
8 | def env
9 | request.env
10 | end
11 |
12 | def execute
13 | gon.clear
14 | gon.a ||= 1
15 | gon.a += 1
16 | end
17 |
18 | def value
19 | gon.a
20 | end
21 | end
22 |
23 | describe 'threading behaviour' do
24 | before do
25 | allow(Gon).to receive(:current_gon).and_call_original
26 | end
27 |
28 | it 'is threadsafe' do
29 | threads = []
30 | 10.times do
31 | threads << Thread.new do
32 | gtw = GonTestWorker.new
33 | gtw.execute
34 | expect(gtw.value).to eq 2
35 | end
36 | end
37 | threads.each(&:join)
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/spec/gon/watch_spec.rb:
--------------------------------------------------------------------------------
1 | describe Gon::Watch do
2 |
3 | let(:controller) { ActionController::Base.new }
4 | let(:request) { ActionDispatch::Request.new({}) }
5 |
6 | before :each do
7 | controller.request = request
8 | controller.params = {}
9 | env = {}
10 | env['ORIGINAL_FULLPATH'] = '/foo'
11 | env['REQUEST_METHOD'] = 'GET'
12 |
13 | Gon::Watch.clear
14 | Gon.send(:current_gon).instance_variable_set(:@env, env)
15 | Gon.send(:current_gon).env['action_controller.instance'] = controller
16 | Gon.clear
17 | end
18 |
19 | it 'should add variables to Gon#all_variables hash' do
20 | Gon.a = 1
21 | Gon.watch.b = 2
22 | expect(Gon.all_variables).to eq({ 'a' => 1, 'b' => 2 })
23 | end
24 |
25 | describe '#all_variables' do
26 |
27 | it 'should generate array with current request url, method type and variable names' do
28 | Gon.watch.a = 1
29 | expect(Gon.watch.all_variables).to eq({ 'a' => { 'url' => '/foo', 'method' => 'GET', 'name' => 'a' } })
30 | end
31 |
32 | end
33 |
34 | describe '#render' do
35 |
36 | it 'should render function with variables in gon namespace' do
37 | Gon.watch.a = 1
38 | expect(Gon.watch.render).to match(/gon\.watch\s=/)
39 | expect(Gon.watch.render).to match(/gon\.watchedVariables/)
40 | end
41 |
42 | end
43 |
44 | describe 'Render concrete variable' do
45 | before do
46 | env = Gon.send(:current_gon).env
47 | env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
48 |
49 | allow(controller).to receive_messages(request: ActionDispatch::Request.new(env))
50 | Gon.send(:current_gon).env['action_controller.instance'] = controller
51 | end
52 |
53 | context 'when request variable is json safe content' do
54 | before do
55 | allow(controller).to receive_messages(params: {
56 | gon_return_variable: true,
57 | gon_watched_variable: 'safety'})
58 | end
59 |
60 | it 'should return value of variable if called right request' do
61 | expect(controller).to receive(:render).with(json: '12345')
62 | Gon.watch.safety = 12345
63 | end
64 | end
65 |
66 | context 'when request variable is json unsafe content' do
67 | let(:expected) { %Q{"\\u003cscript\\u003e'\\"\\u003c/script\\u003e
Dangerous"} }
68 |
69 | before do
70 | allow(controller).to receive_messages(params: {
71 | gon_return_variable: true,
72 | gon_watched_variable: 'danger'})
73 | end
74 |
75 | it 'should return value of variable if called right request' do
76 | expect(controller).to receive(:render).with(json: expected)
77 | Gon.watch.danger = %Q{\u2028Dangerous}
78 | end
79 | end
80 | end
81 | end
82 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rails/railtie'
2 | # We don't require rails for specs, but jbuilder works only in rails.
3 | # And it checks version of rails. I've decided to configure jbuilder for rails v4
4 | module Rails
5 | module VERSION
6 | MAJOR = 4
7 | end
8 |
9 | def self.version
10 | '4.2.0'
11 | end
12 | end
13 |
14 | require 'gon'
15 |
16 | require 'jbuilder'
17 |
18 | RSpec.configure do |config|
19 | config.before(:each) do
20 | RequestStore.store[:gon] = Gon::Request.new({})
21 | @request = RequestStore.store[:gon]
22 | allow(Gon).to receive(:current_gon).and_return(@request)
23 | end
24 | end
25 |
26 | def request
27 | @request ||= double 'request', :env => {}
28 | end
29 |
30 | def wrap_script(content, cdata=true)
31 | script = "'
36 | end
37 |
--------------------------------------------------------------------------------
/spec/test_data/_sample_partial.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.objects objects
2 |
--------------------------------------------------------------------------------
/spec/test_data/sample.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.objects @objects
2 |
--------------------------------------------------------------------------------
/spec/test_data/sample.rabl:
--------------------------------------------------------------------------------
1 | collection @objects => 'objects'
2 | attributes :inspect
3 |
--------------------------------------------------------------------------------
/spec/test_data/sample_rabl_rails.rabl:
--------------------------------------------------------------------------------
1 | collection :@objects => 'objects'
2 | attributes :inspect
3 |
--------------------------------------------------------------------------------
/spec/test_data/sample_url_helpers.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.url user_path(@user_id)
2 |
--------------------------------------------------------------------------------
/spec/test_data/sample_with_controller_method.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.objects @objects
2 | json.data_from_method private_controller_method
3 |
--------------------------------------------------------------------------------
/spec/test_data/sample_with_helpers.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.date distance_of_time_in_words(20000)
2 |
--------------------------------------------------------------------------------
/spec/test_data/sample_with_helpers.rabl:
--------------------------------------------------------------------------------
1 | collection @objects => 'objects'
2 | attributes :id
3 | node(:time_ago) { |_| distance_of_time_in_words(20000) }
4 |
--------------------------------------------------------------------------------
/spec/test_data/sample_with_helpers_rabl_rails.rabl:
--------------------------------------------------------------------------------
1 | collection :@objects => 'objects'
2 | attributes :inspect
3 | node(:time_ago) { distance_of_time_in_words(20000) }
4 |
--------------------------------------------------------------------------------
/spec/test_data/sample_with_locals.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.some_local some_local
2 | json.some_complex_local_id some_complex_local.id
3 |
--------------------------------------------------------------------------------
/spec/test_data/sample_with_partial.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.partial! 'spec/test_data/_sample_partial.json.jbuilder', :objects => @objects
2 |
--------------------------------------------------------------------------------