skozlov-netzke-core 0.1.0.2 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +108 -0
- data/LICENSE +2 -19
- data/Manifest +50 -0
- data/README.rdoc +12 -0
- data/Rakefile +2 -3
- data/TODO +2 -0
- data/autotest/discover.rb +3 -0
- data/generators/netzke_core/netzke_core_generator.rb +6 -6
- data/generators/netzke_core/templates/create_netzke_preferences.rb +2 -2
- data/javascripts/core.js +474 -111
- data/lib/app/controllers/netzke_controller.rb +76 -0
- data/lib/app/models/netzke_preference.rb +128 -39
- data/lib/netzke-core.rb +23 -9
- data/lib/netzke/action_view_ext.rb +26 -0
- data/lib/netzke/base.rb +440 -102
- data/lib/netzke/base_js.rb +258 -0
- data/lib/netzke/controller_extensions.rb +80 -29
- data/lib/netzke/core_ext.rb +72 -21
- data/lib/netzke/feedback_ghost.rb +43 -0
- data/lib/netzke/routing.rb +9 -0
- data/netzke-core.gemspec +10 -11
- data/stylesheets/core.css +12 -0
- data/test/app_root/app/controllers/{application.rb → application_controller.rb} +0 -0
- data/test/app_root/app/models/role.rb +3 -0
- data/test/app_root/app/models/user.rb +3 -0
- data/test/app_root/config/boot.rb +2 -1
- data/test/app_root/config/database.yml +10 -0
- data/test/app_root/config/environment.rb +1 -0
- data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +18 -0
- data/test/app_root/db/migrate/20090423214303_create_roles.rb +11 -0
- data/test/app_root/db/migrate/20090423222114_create_users.rb +12 -0
- data/test/app_root/lib/console_with_fixtures.rb +4 -0
- data/test/app_root/script/console +1 -0
- data/test/fixtures/roles.yml +7 -0
- data/test/fixtures/users.yml +9 -0
- data/test/test_helper.rb +3 -2
- data/test/unit/core_ext_test.rb +66 -0
- data/test/unit/netzke_core_test.rb +167 -0
- data/test/unit/netzke_preference_test.rb +103 -0
- metadata +45 -30
- data/README.mdown +0 -11
- data/generators/netzke_core/templates/create_netzke_layouts.rb +0 -14
- data/generators/netzke_core/templates/netzke.html.erb +0 -10
- data/lib/app/models/netzke_layout.rb +0 -75
- data/lib/netzke/js_class_builder.rb +0 -114
- data/lib/vendor/facets/hash/recursive_merge.rb +0 -28
- data/test/core_ext_test.rb +0 -35
- data/test/netzke_core_test.rb +0 -136
@@ -0,0 +1,258 @@
|
|
1
|
+
module Netzke
|
2
|
+
# == BaseJs
|
3
|
+
# *TODO: outdated*
|
4
|
+
#
|
5
|
+
# Module which provides JS-class generation functionality for the widgets ("client-side"). The generated code
|
6
|
+
# is evaluated once per widget class, and the results are cached in the browser. Included into Netzke::Base class.
|
7
|
+
#
|
8
|
+
# == Widget javascript code
|
9
|
+
# Here's a brief explanation on how a javascript class for a widget gets built.
|
10
|
+
# Widget gets defined as a constructor (a function) by +js_class+ class method (see "Inside widget's contstructor").
|
11
|
+
# +Ext.extend+ provides inheritance from an Ext class specified in +js_base_class+ class method.
|
12
|
+
#
|
13
|
+
# == Inside widget's constructor
|
14
|
+
# * Widget's constructor gets called with a parameter that is a configuration object provided by +js_config+ instance method. This configuration is specific for the instance of the widget, and, for example, contains this widget's unique id. As another example, by means of this configuration object, a grid receives the configuration array for its columns, a form - for its fields, etc. With other words, everything that may change from instance to instance of the same widget's class, goes in here.
|
15
|
+
# * Widget executes its specific initialization code which is provided by +js_before_consttructor+ class method.
|
16
|
+
# For example, a grid may define its column model, a form - its fields, a tab panel - its tabs ("items").
|
17
|
+
# * Widget calls the constructor of the inherited class (see +js_class+ class method) with a parameter that is a merge of
|
18
|
+
# 1) configuration parameter passed to the widget's constructor.
|
19
|
+
module BaseJs
|
20
|
+
def self.included(base)
|
21
|
+
base.extend ClassMethods
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Instance methods
|
26
|
+
#
|
27
|
+
|
28
|
+
# Config that is used for instantiating the widget in javascript
|
29
|
+
def js_config
|
30
|
+
res = {}
|
31
|
+
|
32
|
+
# Unique id of the widget
|
33
|
+
res.merge!(:id => id_name)
|
34
|
+
|
35
|
+
# Recursively include configs of all non-late aggregatees, so that the widget can instantiate them
|
36
|
+
# in javascript immediately.
|
37
|
+
non_late_aggregatees.each_pair do |aggr_name, aggr_config|
|
38
|
+
aggr_instance = aggregatee_instance(aggr_name.to_sym)
|
39
|
+
aggr_instance.before_load
|
40
|
+
res["#{aggr_name}_config".to_sym] = aggr_instance.js_config
|
41
|
+
end
|
42
|
+
|
43
|
+
# Api (besides the default "load_aggregatee_with_cache" - JavaScript side already knows about it)
|
44
|
+
res.merge!(:api => self.class.api_points.reject{ |p| p == :load_aggregatee_with_cache })
|
45
|
+
|
46
|
+
# Widget class name. Needed for dynamic instantiation in javascript.
|
47
|
+
res.merge!(:widget_class_name => short_widget_class_name)
|
48
|
+
|
49
|
+
# Actions, toolbars and menus
|
50
|
+
# tools && res.merge!(:tools => tools)
|
51
|
+
actions && res.merge!(:actions => actions)
|
52
|
+
menu && res.merge!(:menu => menu)
|
53
|
+
|
54
|
+
# Merge with all config options passed as hash to config[:ext_config]
|
55
|
+
res.merge!(ext_config)
|
56
|
+
|
57
|
+
res
|
58
|
+
end
|
59
|
+
|
60
|
+
# All the JS-code required by this instance of the widget to be instantiated in the browser.
|
61
|
+
# It includes the JS-class for the widget itself, as well as JS-classes for all widgets' (non-late) aggregatees.
|
62
|
+
def js_missing_code(cached_dependencies = [])
|
63
|
+
code = dependency_classes.inject("") do |r,k|
|
64
|
+
cached_dependencies.include?(k) ? r : r + "Netzke::#{k}".constantize.js_code(cached_dependencies).strip_js_comments
|
65
|
+
end
|
66
|
+
code.blank? ? nil : code
|
67
|
+
end
|
68
|
+
|
69
|
+
def css_missing_code(cached_dependencies = [])
|
70
|
+
code = dependency_classes.inject("") do |r,k|
|
71
|
+
cached_dependencies.include?(k) ? r : r + "Netzke::#{k}".constantize.css_code(cached_dependencies)
|
72
|
+
end
|
73
|
+
code.blank? ? nil : code
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# The following methods are used when a widget is generated stand-alone (as a part of a HTML page)
|
78
|
+
#
|
79
|
+
|
80
|
+
# instantiating
|
81
|
+
def js_widget_instance
|
82
|
+
%Q{var #{name.jsonify} = new Ext.netzke.cache.#{short_widget_class_name}(#{js_config.to_nifty_json});}
|
83
|
+
end
|
84
|
+
|
85
|
+
# rendering
|
86
|
+
def js_widget_render
|
87
|
+
%Q{#{name.jsonify}.render("#{name.to_s.split('_').join('-')}");}
|
88
|
+
end
|
89
|
+
|
90
|
+
# container for rendering
|
91
|
+
def js_widget_html
|
92
|
+
%Q{<div id="#{name.to_s.split('_').join('-')}"></div>}
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
#
|
97
|
+
#
|
98
|
+
|
99
|
+
# Widget's actions, tools and menus that are loaded at the moment of instantiation
|
100
|
+
def actions; nil; end
|
101
|
+
def menu; nil; end
|
102
|
+
# def tools; nil; end
|
103
|
+
|
104
|
+
# Little helpers
|
105
|
+
def this; "this".l; end
|
106
|
+
def null; "null".l; end
|
107
|
+
|
108
|
+
# Methods used to create the javascript class (only once per widget class).
|
109
|
+
# The generated code gets cached at the browser, and the widget intstances (at the browser side)
|
110
|
+
# get instantiated from it.
|
111
|
+
# All these methods can be overwritten in case you want to extend the functionality of some pre-built widget
|
112
|
+
# instead of using it as is (using both would cause JS-code duplication)
|
113
|
+
module ClassMethods
|
114
|
+
# the JS (Ext) class that we inherit from on JS-level
|
115
|
+
def js_base_class
|
116
|
+
"Ext.Panel"
|
117
|
+
end
|
118
|
+
|
119
|
+
# functions and properties that will be used to extend the functionality of (Ext) JS-class specified in js_base_class
|
120
|
+
def js_extend_properties
|
121
|
+
{}
|
122
|
+
end
|
123
|
+
|
124
|
+
# widget's menus
|
125
|
+
def js_menus; []; end
|
126
|
+
|
127
|
+
# items
|
128
|
+
# def js_items; null; end
|
129
|
+
|
130
|
+
# are we using JS inheritance? for now, if js_base_class is a Netzke class - yes
|
131
|
+
def js_inheritance?
|
132
|
+
superclass != Netzke::Base
|
133
|
+
end
|
134
|
+
|
135
|
+
# Declaration of widget's class (stored in the cache storage (Ext.netzke.cache) at the client side
|
136
|
+
# to be reused at the moment of widget instantiation)
|
137
|
+
def js_class
|
138
|
+
if js_inheritance?
|
139
|
+
# In case of using javascript inheritance, little needs to be done
|
140
|
+
<<-END_OF_JAVASCRIPT
|
141
|
+
// Create the class
|
142
|
+
Ext.netzke.cache.#{short_widget_class_name} = function(config){
|
143
|
+
Ext.netzke.cache.#{short_widget_class_name}.superclass.constructor.call(this, config);
|
144
|
+
};
|
145
|
+
// Extend it with the class that we inherit from, and mix in js_extend_properties
|
146
|
+
Ext.extend(Ext.netzke.cache.#{short_widget_class_name}, Ext.netzke.cache.#{superclass.short_widget_class_name}, Ext.applyIf(#{js_extend_properties.to_nifty_json}, Ext.widgetMixIn));
|
147
|
+
END_OF_JAVASCRIPT
|
148
|
+
else
|
149
|
+
js_add_menus = "this.addMenus(#{js_menus.to_nifty_json});" unless js_menus.empty?
|
150
|
+
<<-END_OF_JAVASCRIPT
|
151
|
+
// Constructor
|
152
|
+
Ext.netzke.cache.#{short_widget_class_name} = function(config){
|
153
|
+
// Do all the initializations that every Netzke widget should do: create methods for API-points,
|
154
|
+
// process actions, tools, toolbars
|
155
|
+
this.commonBeforeConstructor(config);
|
156
|
+
|
157
|
+
// Call the constructor of the inherited class
|
158
|
+
Ext.netzke.cache.#{short_widget_class_name}.superclass.constructor.call(this, config);
|
159
|
+
|
160
|
+
// What every widget should do after calling the constructor of the inherited class, like
|
161
|
+
// setting extra events
|
162
|
+
this.commonAfterConstructor(config);
|
163
|
+
};
|
164
|
+
Ext.extend(Ext.netzke.cache.#{short_widget_class_name}, #{js_base_class}, Ext.applyIf(#{js_extend_properties.to_nifty_json}, Ext.widgetMixIn));
|
165
|
+
END_OF_JAVASCRIPT
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def css_include(*args)
|
170
|
+
included_css = read_inheritable_attribute(:included_css) || []
|
171
|
+
args.each do |inclusion|
|
172
|
+
if inclusion.is_a?(Hash)
|
173
|
+
# we are signalized a non-default file location (e.g. Ext examples)
|
174
|
+
case inclusion.keys.first
|
175
|
+
when :ext_examples
|
176
|
+
location = Netzke::Base.config[:ext_location] + "/examples/"
|
177
|
+
end
|
178
|
+
files = inclusion.values.first
|
179
|
+
else
|
180
|
+
location = ""
|
181
|
+
files = inclusion
|
182
|
+
end
|
183
|
+
|
184
|
+
files = [files] if files.is_a?(String)
|
185
|
+
|
186
|
+
for f in files
|
187
|
+
included_css << location + f
|
188
|
+
end
|
189
|
+
end
|
190
|
+
write_inheritable_attribute(:included_css, included_css)
|
191
|
+
end
|
192
|
+
|
193
|
+
# all JS code needed for this class, including one from the ancestor widget
|
194
|
+
def js_code(cached_dependencies = [])
|
195
|
+
res = ""
|
196
|
+
|
197
|
+
# include the base-class javascript if doing JS inheritance
|
198
|
+
res << superclass.js_code << "\n" if js_inheritance? && !cached_dependencies.include?(superclass.short_widget_class_name)
|
199
|
+
|
200
|
+
# include static javascripts
|
201
|
+
res << js_included << "\n"
|
202
|
+
|
203
|
+
# our own JS class definition
|
204
|
+
res << js_class
|
205
|
+
res
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
# Override this method. Must return an array of paths to javascript files that we depend on.
|
210
|
+
# This javascript code will be loaded along with the widget's class, and before it.
|
211
|
+
def include_js
|
212
|
+
[]
|
213
|
+
end
|
214
|
+
|
215
|
+
# returns all extra js-code (as string) required by this widget's class
|
216
|
+
def js_included
|
217
|
+
res = ""
|
218
|
+
|
219
|
+
include_js.each do |path|
|
220
|
+
f = File.new(path)
|
221
|
+
res << f.read << "\n"
|
222
|
+
end
|
223
|
+
|
224
|
+
res
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# returns all extra js-code (as string) required by this widget's class
|
229
|
+
def css_included
|
230
|
+
res = ""
|
231
|
+
|
232
|
+
included_css = read_inheritable_attribute(:included_css) || []
|
233
|
+
res << included_css.inject("") do |r, path|
|
234
|
+
f = File.new(path)
|
235
|
+
r << f.read
|
236
|
+
end
|
237
|
+
|
238
|
+
res
|
239
|
+
end
|
240
|
+
|
241
|
+
# all JS code needed for this class including the one from the ancestor widget
|
242
|
+
def css_code(cached_dependencies = [])
|
243
|
+
res = ""
|
244
|
+
|
245
|
+
# include the base-class javascript if doing JS inheritance
|
246
|
+
res << superclass.css_code << "\n" if js_inheritance? && !cached_dependencies.include?(superclass.short_widget_class_name)
|
247
|
+
|
248
|
+
res << css_included << "\n"
|
249
|
+
|
250
|
+
res
|
251
|
+
end
|
252
|
+
|
253
|
+
def this; "this".l; end
|
254
|
+
def null; "null".l; end
|
255
|
+
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -2,6 +2,29 @@ module Netzke
|
|
2
2
|
module ControllerExtensions
|
3
3
|
def self.included(base)
|
4
4
|
base.extend ControllerClassMethods
|
5
|
+
base.send(:before_filter, :set_session_data)
|
6
|
+
end
|
7
|
+
|
8
|
+
def set_session_data
|
9
|
+
Netzke::Base.session = session
|
10
|
+
session[:netzke_user_id] = defined?(current_user) ? current_user.try(:id) : nil
|
11
|
+
|
12
|
+
Netzke::Base.user = current_user # for backward compatibility (TODO: eliminate the need for this)
|
13
|
+
|
14
|
+
# set netzke_just_logged_in and netzke_just_logged_out states (may be used by Netzke widgets)
|
15
|
+
if session[:_netzke_next_request_is_first_after_login]
|
16
|
+
session[:netzke_just_logged_in] = true
|
17
|
+
session[:_netzke_next_request_is_first_after_login] = false
|
18
|
+
else
|
19
|
+
session[:netzke_just_logged_in] = false
|
20
|
+
end
|
21
|
+
|
22
|
+
if session[:_netzke_next_request_is_first_after_logout]
|
23
|
+
session[:netzke_just_logged_out] = true
|
24
|
+
session[:_netzke_next_request_is_first_after_logout] = false
|
25
|
+
else
|
26
|
+
session[:netzke_just_logged_out] = false
|
27
|
+
end
|
5
28
|
end
|
6
29
|
|
7
30
|
def method_missing(method_name)
|
@@ -12,16 +35,19 @@ module Netzke
|
|
12
35
|
widget = widget.to_sym
|
13
36
|
action = !action.empty? && action.join("__").to_sym
|
14
37
|
|
15
|
-
# only widget's actions starting with "
|
38
|
+
# only widget's actions starting with "api_" are accessible from outside (security)
|
16
39
|
if action
|
17
|
-
|
40
|
+
api_action = action.to_s.index('__') ? action : "api_#{action}"
|
18
41
|
|
19
42
|
# widget module
|
20
43
|
widget_class = "Netzke::#{self.class.widget_config_storage[widget][:widget_class_name]}".constantize
|
21
44
|
|
22
45
|
# instantiate the server part of the widget
|
23
|
-
widget_instance = widget_class.new(self.class.widget_config_storage[widget]
|
24
|
-
|
46
|
+
widget_instance = widget_class.new(self.class.widget_config_storage[widget])
|
47
|
+
# (OLD VERSION)
|
48
|
+
# widget_instance = widget_class.new(self.class.widget_config_storage[widget].merge(:controller => self)) # OPTIMIZE: top-level widgets have access to the controller - can we avoid that?
|
49
|
+
|
50
|
+
render :text => widget_instance.send(api_action, params)
|
25
51
|
end
|
26
52
|
end
|
27
53
|
end
|
@@ -31,12 +57,13 @@ module Netzke
|
|
31
57
|
# widget_config_storage for all widgets
|
32
58
|
def widget_config_storage
|
33
59
|
@@widget_config_storage ||= {}
|
60
|
+
@@widget_config_storage[self.name] ||= {} # specific for controller
|
34
61
|
end
|
35
62
|
|
36
63
|
#
|
37
64
|
# Use this method to declare a widget in the controller
|
38
65
|
#
|
39
|
-
def
|
66
|
+
def netzke(name, config={})
|
40
67
|
# which module is the widget?
|
41
68
|
config[:widget_class_name] ||= name.to_s.classify
|
42
69
|
config[:name] ||= name
|
@@ -46,39 +73,63 @@ module Netzke
|
|
46
73
|
|
47
74
|
# provide widget helpers
|
48
75
|
ActionView::Base.module_eval <<-END_EVAL, __FILE__, __LINE__
|
76
|
+
def #{name}_server_instance(config = {})
|
77
|
+
default_config = controller.class.widget_config_storage[:#{name}]
|
78
|
+
if config.empty?
|
79
|
+
# only cache when the config is empty (which means that config specified in controller is used)
|
80
|
+
@widget_instance_cache ||= {}
|
81
|
+
@widget_instance_cache[:#{name}] ||= Netzke::Base.instance_by_config(default_config)
|
82
|
+
else
|
83
|
+
# if helper is called with parameters - always return a fresh instance of widget, no caching
|
84
|
+
Netzke::Base.instance_by_config(default_config.deep_merge(config))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
49
88
|
def #{name}_widget_instance(config = {})
|
50
|
-
#
|
51
|
-
global_config = controller.class.widget_config_storage[:#{name}]
|
52
|
-
|
53
|
-
# when instantiating a client side instance, the configuration may be overwritten
|
54
|
-
# (but the server side will know nothing about it!)
|
55
|
-
local_config = global_config.merge(config)
|
56
|
-
|
57
|
-
# instantiate it
|
58
|
-
widget_instance = Netzke::#{config[:widget_class_name]}.new(local_config)
|
59
|
-
|
60
|
-
# return javascript code for instantiating on the javascript level
|
61
|
-
widget_instance.js_widget_instance
|
89
|
+
#{name}_server_instance(config).js_widget_instance
|
62
90
|
end
|
63
91
|
|
64
92
|
def #{name}_class_definition
|
65
|
-
|
66
|
-
|
93
|
+
@generated_widget_classes ||= []
|
94
|
+
res = #{name}_server_instance.js_missing_code(@generated_widget_classes)
|
95
|
+
|
96
|
+
# prevent duplication of javascript when multiple homogeneous widgets are on the same page
|
97
|
+
@generated_widget_classes += #{name}_server_instance.dependencies
|
98
|
+
@generated_widget_classes.uniq!
|
99
|
+
res
|
67
100
|
end
|
101
|
+
|
102
|
+
def #{name}_widget_html
|
103
|
+
#{name}_server_instance.js_widget_html
|
104
|
+
end
|
105
|
+
|
106
|
+
def #{name}_widget_render
|
107
|
+
#{name}_server_instance.js_widget_render
|
108
|
+
end
|
109
|
+
|
68
110
|
END_EVAL
|
69
111
|
|
70
112
|
# add controller action which will render a simple HTML page containing the widget
|
71
113
|
define_method("#{name}_test") do
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
114
|
+
@widget_name = name
|
115
|
+
render :inline => <<-HTML
|
116
|
+
<head>
|
117
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
118
|
+
<title><%= @widget_name %></title>
|
119
|
+
<%= netzke_js_include %>
|
120
|
+
<%= netzke_css_include %>
|
121
|
+
<script type="text/javascript" charset="utf-8">
|
122
|
+
<%= #{name}_class_definition %>
|
123
|
+
Ext.onReady(function(){
|
124
|
+
<%= #{name}_widget_instance %>
|
125
|
+
<%= #{name}_widget_render %>
|
126
|
+
});
|
127
|
+
</script>
|
128
|
+
</head>
|
129
|
+
<body>
|
130
|
+
<%= #{name}_widget_html %>
|
131
|
+
</body>
|
132
|
+
HTML
|
82
133
|
end
|
83
134
|
end
|
84
135
|
end
|
data/lib/netzke/core_ext.rb
CHANGED
@@ -9,18 +9,29 @@ class Hash
|
|
9
9
|
h
|
10
10
|
end : self
|
11
11
|
end
|
12
|
+
|
13
|
+
def jsonify
|
14
|
+
self.inject({}) do |h,(k,v)|
|
15
|
+
new_key = k.instance_of?(String) || k.instance_of?(Symbol) ? k.jsonify : k
|
16
|
+
new_value = v.instance_of?(Array) || v.instance_of?(Hash) ? v.jsonify : v
|
17
|
+
h.merge(new_key => new_value)
|
18
|
+
end
|
19
|
+
end
|
12
20
|
|
13
21
|
# First camelizes the keys, then convert the whole hash to JSON
|
14
|
-
def
|
15
|
-
|
16
|
-
self.recursive_delete_if_nil.convert_keys{|k| k.camelize(:lower)}.to_json
|
17
|
-
end
|
18
|
-
|
19
|
-
# Converts values to strings
|
20
|
-
def stringify_values!
|
21
|
-
self.each_pair{|k,v| self[k] = v.to_s if v.is_a?(Symbol)}
|
22
|
+
def to_nifty_json
|
23
|
+
self.recursive_delete_if_nil.jsonify.to_json
|
22
24
|
end
|
23
25
|
|
26
|
+
# Converts values of a Hash in such a way that they can be easily stored in the database: hashes and arrays are jsonified, symbols - stringified
|
27
|
+
def deebeefy_values
|
28
|
+
inject({}) do |options, (k, v)|
|
29
|
+
options[k] = v.is_a?(Symbol) ? v.to_s : (v.is_a?(Hash) || v.is_a?(Array)) ? v.to_json : v
|
30
|
+
options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# We don't need to pass null values in JSON, they are null by simply being absent
|
24
35
|
def recursive_delete_if_nil
|
25
36
|
self.inject({}) do |h,(k,v)|
|
26
37
|
if !v.nil?
|
@@ -29,13 +40,29 @@ class Hash
|
|
29
40
|
h
|
30
41
|
end
|
31
42
|
end
|
43
|
+
|
44
|
+
# Javascrit-like access to Hash values
|
45
|
+
def method_missing(method, *args)
|
46
|
+
if method.to_s =~ /=$/
|
47
|
+
method_base = method.to_s.sub(/=$/,'').to_sym
|
48
|
+
key = self[method_base.to_s].nil? ? method_base : method_base.to_s
|
49
|
+
self[key] = args.first
|
50
|
+
else
|
51
|
+
key = self[method.to_s].nil? ? method : method.to_s
|
52
|
+
self[key]
|
53
|
+
end
|
54
|
+
end
|
32
55
|
|
33
56
|
end
|
34
57
|
|
35
58
|
class Array
|
59
|
+
def jsonify
|
60
|
+
self.map{ |el| el.instance_of?(Array) || el.instance_of?(Hash) ? el.jsonify : el }
|
61
|
+
end
|
62
|
+
|
36
63
|
# Camelizes the keys of hashes and converts them to JSON
|
37
|
-
def
|
38
|
-
self.recursive_delete_if_nil.
|
64
|
+
def to_nifty_json
|
65
|
+
self.recursive_delete_if_nil.jsonify.to_json
|
39
66
|
end
|
40
67
|
|
41
68
|
# Applies convert_keys to each element which responds to convert_keys
|
@@ -50,28 +77,52 @@ class Array
|
|
50
77
|
end
|
51
78
|
end
|
52
79
|
|
53
|
-
class String
|
54
|
-
|
55
|
-
def l
|
56
|
-
def self.to_json
|
57
|
-
self
|
58
|
-
end
|
80
|
+
class LiteralString < String
|
81
|
+
def to_json(*args)
|
59
82
|
self
|
60
83
|
end
|
61
|
-
|
62
|
-
|
84
|
+
end
|
85
|
+
|
86
|
+
class String
|
87
|
+
def jsonify
|
63
88
|
self.camelize(:lower)
|
64
89
|
end
|
65
90
|
|
66
|
-
#
|
91
|
+
# Converts self to "literal JSON"-string - one that doesn't get quotes appended when being sent "to_json" method
|
92
|
+
def l
|
93
|
+
LiteralString.new(self)
|
94
|
+
end
|
95
|
+
|
96
|
+
# removes JS-comments (both single- and multi-line) from the string
|
67
97
|
def strip_js_comments
|
68
98
|
regexp = /\/\/.*$|(?m:\/\*.*?\*\/)/
|
69
|
-
self.gsub(regexp, '')
|
99
|
+
self.gsub!(regexp, '')
|
100
|
+
|
101
|
+
# also remove empty lines
|
102
|
+
regexp = /^\s*\n/
|
103
|
+
self.gsub!(regexp, '')
|
104
|
+
end
|
105
|
+
|
106
|
+
# "false" => false, "whatever_else" => true
|
107
|
+
def to_b
|
108
|
+
self != "false"
|
70
109
|
end
|
71
110
|
end
|
72
111
|
|
73
112
|
class Symbol
|
74
|
-
def
|
113
|
+
def jsonify
|
75
114
|
self.to_s.camelize(:lower).to_sym
|
76
115
|
end
|
116
|
+
|
117
|
+
def l
|
118
|
+
LiteralString.new(self.to_s)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
module ActiveSupport
|
123
|
+
class TimeWithZone
|
124
|
+
def to_json(options = {})
|
125
|
+
self.to_s(:db).to_json
|
126
|
+
end
|
127
|
+
end
|
77
128
|
end
|