flok 0.0.36 → 0.0.38
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/app/drivers/chrome/build_context.rb +1 -1
- data/app/drivers/chrome/config.yml +10 -0
- data/app/drivers/chrome/pipe.rb +24 -1
- data/app/drivers/chrome/src/persist.js +29 -0
- data/app/drivers/chrome/src/sockio.js +3 -1
- data/app/drivers/chrome/src/vendor/store2.js +3 -0
- data/app/kern/callout.js +55 -0
- data/app/kern/controller.js +3 -0
- data/app/kern/crc32.js +22 -0
- data/app/kern/dispatch.js +16 -0
- data/app/kern/mod/event.js +3 -0
- data/app/kern/mod/persist.js +1 -0
- data/app/kern/mod/timer.js +5 -0
- data/app/kern/pagers/mem_pager.js +2 -0
- data/app/kern/pagers/sockio_pager.js +66 -0
- data/app/kern/pagers/spec0.js +35 -0
- data/app/kern/pagers/spec1.js +24 -0
- data/app/kern/pagers/spec2.js +31 -0
- data/app/kern/services/test.rb +7 -0
- data/app/kern/services/vm.rb +141 -0
- data/app/kern/spec_helper.js +5 -0
- data/bin/flok +27 -5
- data/docs/callout.md +8 -0
- data/docs/client_api.md +1 -1
- data/docs/compilation.md +14 -8
- data/docs/controllers.md +2 -1
- data/docs/datatypes.md +3 -1
- data/docs/environmentals.md +6 -0
- data/docs/interactive.md +6 -1
- data/docs/kernel_api.md +3 -0
- data/docs/mod/event.md +5 -1
- data/docs/mod/persist.md +14 -31
- data/docs/mod/timer.md +4 -2
- data/docs/project.md +16 -5
- data/docs/scheduling.md +1 -0
- data/docs/services.md +141 -56
- data/docs/services/vm.md +128 -0
- data/docs/services/vm/pagers.md +46 -0
- data/lib/flok/build.rb +11 -16
- data/lib/flok/platform.rb +29 -6
- data/lib/flok/project_template/app/scripts/script.js +3 -0
- data/lib/flok/project_template/app/services/service.rb +1 -0
- data/lib/flok/project_template/config/config.yml +1 -0
- data/lib/flok/project_template/config/platforms/chrome/config.yml +1 -0
- data/lib/flok/project_template/config/services.rb +1 -0
- data/lib/flok/service_compiler_templates/services.js.erb +78 -9
- data/lib/flok/services_compiler.rb +117 -20
- data/lib/flok/user_compiler.rb +14 -6
- data/lib/flok/user_compiler_templates/ctable.js.erb +10 -0
- data/lib/flok/version.rb +1 -1
- data/spec/env/etc.rb +1 -1
- data/spec/env/global.rb +2 -0
- data/spec/env/iface.rb +20 -4
- data/spec/env/kern.rb +8 -3
- data/spec/etc/cli_spec.rb +319 -165
- data/spec/etc/lib/assets/config.yml +3 -0
- data/spec/etc/lib/platform_spec.rb +14 -10
- data/spec/etc/lib/project_spec.rb +22 -0
- data/spec/etc/service_compiler/config0.rb +1 -0
- data/spec/etc/service_compiler/config1.rb +1 -0
- data/spec/etc/service_compiler/config2.rb +3 -0
- data/spec/etc/service_compiler/service0.rb +22 -6
- data/spec/etc/service_compiler/service1.rb +26 -0
- data/spec/etc/service_compiler/service_bad_type.rb +20 -0
- data/spec/etc/services_compiler_spec.rb +35 -16
- data/spec/etc/user_compiler/data.js +2 -0
- data/spec/etc/user_compiler_spec.rb +7 -1
- data/spec/iface/driver/persist_spec.rb +106 -0
- data/spec/iface/driver/pipe_spec.rb +5 -0
- data/spec/iface/kern/ping_spec.rb +4 -3
- data/spec/kern/assets/blank.rb +0 -0
- data/spec/kern/assets/service0.rb +24 -0
- data/spec/kern/assets/service1.rb +22 -0
- data/spec/kern/assets/service2.rb +27 -0
- data/spec/kern/assets/service_config0.rb +2 -0
- data/spec/kern/assets/service_config1.rb +2 -0
- data/spec/kern/assets/service_controller0.rb +13 -0
- data/spec/kern/assets/service_controller1.rb +32 -0
- data/spec/kern/assets/service_controller2.rb +38 -0
- data/spec/kern/assets/service_controller3.rb +38 -0
- data/spec/kern/assets/vm/config0.rb +2 -0
- data/spec/kern/assets/vm/config1.rb +12 -0
- data/spec/kern/assets/vm/config2.rb +12 -0
- data/spec/kern/assets/vm/config3.rb +12 -0
- data/spec/kern/assets/vm/controller0.rb +8 -0
- data/spec/kern/assets/vm/controller1.rb +18 -0
- data/spec/kern/assets/vm/controller2.rb +18 -0
- data/spec/kern/assets/vm/controller3.rb +20 -0
- data/spec/kern/assets/vm/controller4.rb +22 -0
- data/spec/kern/assets/vm/controller5.rb +22 -0
- data/spec/kern/assets/vm/controller6.rb +21 -0
- data/spec/kern/assets/vm/service0.rb +24 -0
- data/spec/kern/assets/vm/service_controller0.rb +7 -0
- data/spec/kern/callout_spec.rb +153 -0
- data/spec/kern/functions_spec.rb +29 -0
- data/spec/kern/service_controller_spec.rb +213 -0
- data/spec/kern/vm_service_spec.rb +195 -0
- metadata +98 -12
- data/app/kern/services/rest.rb +0 -310
- data/app/kern/services/timer.rb +0 -30
- data/docs/services/timer.md +0 -21
- data/spec/kern/assets/rest_service.rb +0 -20
- data/spec/kern/assets/timer_service.rb +0 -19
- data/spec/kern/timer_service_spec.rb +0 -40
data/lib/flok/platform.rb
CHANGED
@@ -12,17 +12,40 @@ module Flok
|
|
12
12
|
end
|
13
13
|
|
14
14
|
#Get a list of modules for a particular environment for a platform
|
15
|
-
def self.mods
|
15
|
+
def self.mods environment
|
16
16
|
#Create array that looks like a javascript array with single quotes
|
17
|
-
mods = self.config_yml(
|
17
|
+
mods = self.config_yml(environment)['mods']
|
18
18
|
end
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
def self.defines environment
|
21
|
+
#Just converting an array into a hash of true values for easier lookup
|
22
|
+
hash = {}
|
23
|
+
defines_arr = self.config_yml(environment)['defines']
|
24
|
+
if defines_arr
|
25
|
+
defines_arr.each do |e|
|
26
|
+
hash[e] = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
return hash
|
31
|
+
end
|
32
|
+
|
33
|
+
#Get all config.yml information for a config_yml file based on FLOK_CONFIG
|
34
|
+
def self.config_yml environment
|
35
|
+
#Get the config.yml path
|
36
|
+
config_yml_path = ENV['FLOK_CONFIG']
|
37
|
+
if config_yml_path
|
38
|
+
raise "You didn't pass a FLOK_CONFIG variable for the config.yml" unless config_yml_path
|
39
|
+
raise "The FLOK_CONFIG given: #{config_yml_path.inspect} does not contain a file (config.yml)" unless File.exists?(config_yml_path)
|
40
|
+
else
|
41
|
+
#$stderr.puts "Warning: You didn't specify FLOK_CONFIG, Using default config of ./app/drivers/#{ENV['PLATFORM']}/config.yml"
|
42
|
+
config_yml_path = "./app/drivers/#{ENV['PLATFORM']}/config.yml"
|
43
|
+
end
|
44
|
+
|
45
|
+
driver_config = YAML.load_file(config_yml_path)
|
24
46
|
return driver_config[environment]
|
25
47
|
end
|
48
|
+
|
26
49
|
end
|
27
50
|
|
28
51
|
#Alias
|
@@ -0,0 +1 @@
|
|
1
|
+
#Your services should go into this file, you may name your files anything and delete this file
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= File.read("./app/drivers/ENV['PLATFORM']/")
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= File.read(File.join(File.dirname(__FILE__), "../../../app/drivers/#{ENV['PLATFORM']}/config.yml")) %>
|
@@ -0,0 +1 @@
|
|
1
|
+
#Services
|
@@ -1,13 +1,82 @@
|
|
1
1
|
<% @services.each do |s| %>
|
2
|
-
|
3
|
-
|
4
|
-
<%= s.
|
5
|
-
//////////////////////////////////////////////////
|
2
|
+
//Variables
|
3
|
+
var <%= s.name %>_sessions = {}; //Currently connected clients
|
4
|
+
var <%= s.name %>_n_sessions = 0; //Number of sessions
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
//Static global
|
7
|
+
<%= s._global %>
|
8
|
+
|
9
|
+
//Start of <%= s.name %> on_wakeup
|
10
|
+
function <%= s.name %>_on_wakeup() {
|
11
|
+
//Timer related
|
12
|
+
/////////////////////////////////////////////////////////////////////////////////////
|
13
|
+
//The timer base pointer for incomming callout events
|
14
|
+
<%= s.name %>_timer_bp = tels(1);
|
15
|
+
reg_evt(<%= s.name %>_timer_bp, <%= s.name %>_handle_timer_events);
|
16
|
+
|
17
|
+
//Tell the timer to notify our timer event handlers
|
18
|
+
//The name is randomly generated for each one
|
19
|
+
<% s.every_handlers.each do |h| %>
|
20
|
+
reg_interval(<%= s.name %>_timer_bp, '<%= h[:name] %>', <%= h[:ticks] %>);
|
21
|
+
<% end %>
|
22
|
+
/////////////////////////////////////////////////////////////////////////////////////
|
23
|
+
|
24
|
+
<%= s._on_wakeup %>
|
25
|
+
}
|
26
|
+
|
27
|
+
function <%= s.name %>_on_sleep() {
|
28
|
+
//Do not receive anymore timer events
|
29
|
+
dereg_evt(<%= s.name %>_timer_bp);
|
30
|
+
|
31
|
+
<%= s._on_sleep %>
|
32
|
+
}
|
33
|
+
|
34
|
+
function <%= s.name %>_on_connect(bp) {
|
35
|
+
var sessions = <%= s.name %>_sessions;
|
36
|
+
<%= s.name %>_n_sessions += 1;
|
37
|
+
|
38
|
+
//First connection
|
39
|
+
if (<%= s.name %>_n_sessions === 1) {
|
40
|
+
<%= s.name %>_on_wakeup();
|
41
|
+
}
|
42
|
+
|
43
|
+
sessions[bp] = true;
|
44
|
+
<%= s._on_connect %>
|
45
|
+
}
|
46
|
+
|
47
|
+
function <%= s.name %>_on_disconnect(bp) {
|
48
|
+
var sessions = <%= s.name %>_sessions;
|
49
|
+
|
50
|
+
<%= s.name %>_n_sessions -= 1;
|
51
|
+
delete sessions[bp];
|
52
|
+
|
53
|
+
<%= s._on_disconnect %>
|
54
|
+
|
55
|
+
//No more connections
|
56
|
+
if (<%= s.name %>_n_sessions === 0) {
|
57
|
+
<%= s.name %>_on_sleep();
|
58
|
+
}
|
59
|
+
|
60
|
+
}
|
61
|
+
|
62
|
+
//'Event' handlers
|
63
|
+
<% s.event_handlers.each do |h| %>
|
64
|
+
function <%= s.name %>_on_<%= h[:name] %>(bp, params) {
|
65
|
+
var sessions = <%= s.name %>_sessions;
|
66
|
+
<%= h[:str] %>
|
67
|
+
}
|
68
|
+
<% end %>
|
69
|
+
|
70
|
+
//Timer receivers
|
71
|
+
/////////////////////////////////////////////////////////////////////
|
72
|
+
function <%= s.name %>_handle_timer_events(ep, ename, info) {
|
73
|
+
var sessions = <%= s.name %>_sessions;
|
74
|
+
|
75
|
+
<% s.every_handlers.each do |h| %>
|
76
|
+
if (ename === '<%= h[:name] %>') {
|
77
|
+
<%= h[:str] %>
|
78
|
+
}
|
79
|
+
<% end %>
|
11
80
|
}
|
12
|
-
|
81
|
+
/////////////////////////////////////////////////////////////////////
|
13
82
|
<% end %>
|
@@ -1,22 +1,28 @@
|
|
1
1
|
#Compile a controller ruby file into a javascript string
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext/numeric'
|
2
4
|
|
3
5
|
require 'erb'
|
4
6
|
module Flok
|
5
7
|
module ServicesCompiler
|
6
8
|
#Compile a ruby file containing flok controller definitions (from the services)
|
7
|
-
|
9
|
+
#The config is outlined in the documentation under docs/services.md
|
10
|
+
def self.compile rb_src, rb_config
|
11
|
+
#Execute the configuration file first
|
12
|
+
config_context = ServicesCompilerConfigContext.new
|
13
|
+
config_context.instance_eval(rb_config, __FILE__, __LINE__)
|
14
|
+
|
8
15
|
#Execute code in this context, the context will hold all the information
|
9
16
|
#that is used to then generate code
|
10
|
-
context = ServicesCompilerContext.new
|
17
|
+
context = ServicesCompilerContext.new(config_context)
|
11
18
|
context.instance_eval(rb_src, __FILE__, __LINE__)
|
19
|
+
context.ready
|
12
20
|
|
13
21
|
@src = ""
|
14
22
|
services_erb = File.read File.join(File.dirname(__FILE__), "./service_compiler_templates/services.js.erb")
|
15
23
|
services_renderer = ERB.new(services_erb)
|
16
24
|
@src << services_renderer.result(context.get_binding)
|
17
25
|
|
18
|
-
#puts @src
|
19
|
-
|
20
26
|
return @src
|
21
27
|
end
|
22
28
|
end
|
@@ -24,29 +30,71 @@ end
|
|
24
30
|
|
25
31
|
#Compiler executes all rb code inside this context
|
26
32
|
module Flok
|
27
|
-
class
|
28
|
-
|
33
|
+
class ServicesCompilerConfigContext
|
34
|
+
#Each service instance contains a :instance_name and :class
|
35
|
+
attr_accessor :service_instances
|
29
36
|
|
30
37
|
def initialize
|
38
|
+
@service_instances = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def service_instance instance_name, name, options={}
|
42
|
+
@service_instances.push({
|
43
|
+
:instance_name => instance_name,
|
44
|
+
:class => name,
|
45
|
+
:options => options
|
46
|
+
})
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ServicesCompilerContext
|
51
|
+
attr_accessor :services, :config
|
52
|
+
|
53
|
+
def initialize config_context
|
54
|
+
@config = config_context
|
55
|
+
|
56
|
+
#A hash containing the 'class' name of the service to a block that can be used with Service.new
|
57
|
+
@_services = {}
|
58
|
+
|
31
59
|
@services = []
|
32
60
|
end
|
33
61
|
|
62
|
+
def ready
|
63
|
+
#Create an array from the service_instances where each element in the array is the full code of the service
|
64
|
+
@config.service_instances.each do |i|
|
65
|
+
#Get the instance name and class name of the service, normally defined in a ./config/services.rb file
|
66
|
+
sname = i[:instance_name]
|
67
|
+
sclass = i[:class]
|
68
|
+
soptions = i[:options]
|
69
|
+
|
70
|
+
sblock = @_services[sclass]
|
71
|
+
raise "No service found for service_name: #{sclass.inspect} when trying to create service with instance name #{sname.inspect}. @_services contained: #{@_services.inspect} \n@config.service_instances contained: #{@config.service_instances.inspect}" unless sblock
|
72
|
+
service = Service.new(sname, soptions)
|
73
|
+
service.instance_eval(&sblock)
|
74
|
+
@services << service
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
34
78
|
def get_binding
|
35
79
|
return binding
|
36
80
|
end
|
37
81
|
|
38
82
|
def service name, &block
|
39
|
-
@
|
83
|
+
@_services[name] = block
|
40
84
|
end
|
41
85
|
end
|
42
86
|
|
43
87
|
class Service
|
44
|
-
attr_accessor :name
|
45
|
-
def initialize name,
|
88
|
+
attr_accessor :name, :_global, :_on_wakeup, :_on_sleep, :_on_connect, :_on_disconnect, :event_handlers, :every_handlers, :options
|
89
|
+
def initialize name, options
|
46
90
|
@name = name
|
47
|
-
@
|
91
|
+
@options = options
|
92
|
+
|
93
|
+
#These are the 'on' handlers
|
94
|
+
@event_handlers = []
|
48
95
|
|
49
|
-
|
96
|
+
#These are for every 5.seconds
|
97
|
+
@every_handlers = []
|
50
98
|
end
|
51
99
|
|
52
100
|
def get_on_init
|
@@ -57,18 +105,67 @@ module Flok
|
|
57
105
|
return @on_request
|
58
106
|
end
|
59
107
|
|
60
|
-
def
|
61
|
-
|
108
|
+
def global(str)
|
109
|
+
render = ERB.new(str)
|
110
|
+
str = render.result(binding)
|
111
|
+
@_global = str
|
112
|
+
end
|
113
|
+
|
114
|
+
def on_wakeup(str)
|
115
|
+
render = ERB.new(str)
|
116
|
+
str = render.result(binding)
|
117
|
+
@_on_wakeup = str
|
118
|
+
end
|
119
|
+
|
120
|
+
def on_sleep(str)
|
121
|
+
render = ERB.new(str)
|
122
|
+
str = render.result(binding)
|
123
|
+
|
124
|
+
@_on_sleep = str
|
125
|
+
end
|
126
|
+
|
127
|
+
def on_connect(str)
|
128
|
+
render = ERB.new(str)
|
129
|
+
str = render.result(binding)
|
130
|
+
@_on_connect = str
|
62
131
|
end
|
63
132
|
|
64
|
-
def
|
65
|
-
|
133
|
+
def on_disconnect(str)
|
134
|
+
render = ERB.new(str)
|
135
|
+
str = render.result(binding)
|
136
|
+
@_on_disconnect = str
|
137
|
+
end
|
138
|
+
|
139
|
+
def on(name, str)
|
140
|
+
render = ERB.new(str)
|
141
|
+
str = render.result(binding)
|
142
|
+
|
143
|
+
@event_handlers << {
|
144
|
+
:name => name,
|
145
|
+
:str => str
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
def every(seconds, str)
|
150
|
+
@every_handlers << {
|
151
|
+
:name => "#{seconds}_sec_#{SecureRandom.hex[0..6]}",
|
152
|
+
:ticks => seconds*4,
|
153
|
+
:str => str
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
def type str
|
158
|
+
@_type = str.to_s
|
159
|
+
unless ["daemon", "agent"].include? @_type
|
160
|
+
raise "You gave a type for the service, #{@_type.inspect} but this wasn't a valid type of service. Should be \
|
161
|
+
either 'daemon' or 'agent'"
|
162
|
+
end
|
66
163
|
end
|
67
164
|
|
68
165
|
def macro text
|
69
|
-
out = StringIO.new
|
166
|
+
#out = StringIO.new
|
70
167
|
|
71
|
-
text.split("\n").each do |l|
|
168
|
+
#text.split("\n").each do |l|
|
72
169
|
##Request(vc_name, spot_name, context) macro
|
73
170
|
#if l =~ /Request/
|
74
171
|
#l.strip!
|
@@ -84,11 +181,11 @@ module Flok
|
|
84
181
|
#out << %{
|
85
182
|
#}
|
86
183
|
#else
|
87
|
-
out.puts l
|
184
|
+
#out.puts l
|
88
185
|
#end
|
89
|
-
end
|
186
|
+
#end
|
90
187
|
|
91
|
-
return out.string
|
188
|
+
#return out.string
|
92
189
|
end
|
93
190
|
end
|
94
191
|
end
|
data/lib/flok/user_compiler.rb
CHANGED
@@ -15,8 +15,6 @@ module Flok
|
|
15
15
|
ctable_renderer = ERB.new(ctable_erb)
|
16
16
|
@src << ctable_renderer.result(context.get_binding)
|
17
17
|
|
18
|
-
#puts @src
|
19
|
-
|
20
18
|
return @src
|
21
19
|
end
|
22
20
|
end
|
@@ -194,6 +192,9 @@ module Flok
|
|
194
192
|
//Free +1 because that will be the 'main' view
|
195
193
|
main_q.push([1, "if_free_view", embeds[i][j]+1]);
|
196
194
|
|
195
|
+
//Call dealloc on the controller
|
196
|
+
tel_deref(embeds[i][j]).cte.__dealloc__(embeds[i][j]);
|
197
|
+
|
197
198
|
<% if @debug %>
|
198
199
|
var vp = embeds[i][j]+1;
|
199
200
|
//First locate spot this view belongs to in reverse hash
|
@@ -228,7 +229,7 @@ module Flok
|
|
228
229
|
}]);
|
229
230
|
}
|
230
231
|
out.puts res
|
231
|
-
#Request(
|
232
|
+
#Request(service_instance_name, ename, info)
|
232
233
|
elsif l =~ /Request/
|
233
234
|
l.strip!
|
234
235
|
l.gsub!(/Request\(/, "")
|
@@ -237,11 +238,13 @@ module Flok
|
|
237
238
|
o = l.split(",").map{|e| e.strip}
|
238
239
|
|
239
240
|
name = o.shift.gsub(/"/, "")
|
241
|
+
ename = o.shift.gsub(/"/, "")
|
240
242
|
info = o.shift.gsub(/"/, "")
|
241
|
-
|
243
|
+
|
244
|
+
raise "You tried to Request the service #{name.inspect}, but you haven't added that to your 'services' for this controller (#{@controller.name.inspect})" unless @controller._services.include? name
|
242
245
|
|
243
246
|
out << %{
|
244
|
-
|
247
|
+
#{name}_on_#{ename}(__base__, #{info});
|
245
248
|
}
|
246
249
|
else
|
247
250
|
out.puts l
|
@@ -265,12 +268,13 @@ module Flok
|
|
265
268
|
end
|
266
269
|
|
267
270
|
class UserCompilerController
|
268
|
-
attr_accessor :name, :spots, :macros
|
271
|
+
attr_accessor :name, :spots, :macros, :_services
|
269
272
|
def initialize name, ctx, &block
|
270
273
|
@name = name
|
271
274
|
@ctx = ctx
|
272
275
|
@spots = ['main']
|
273
276
|
@macros = {}
|
277
|
+
@_services = []
|
274
278
|
|
275
279
|
self.instance_eval(&block)
|
276
280
|
end
|
@@ -285,6 +289,10 @@ module Flok
|
|
285
289
|
@spots += spots
|
286
290
|
end
|
287
291
|
|
292
|
+
def services *instance_names
|
293
|
+
@_services = instance_names.map{|e| e.to_s}
|
294
|
+
end
|
295
|
+
|
288
296
|
#Pass through action
|
289
297
|
def action name, &block
|
290
298
|
@ctx.action self, name, &block
|
@@ -1,6 +1,16 @@
|
|
1
1
|
ctable = {
|
2
2
|
<% @controllers.each do |c| %>
|
3
3
|
<%= c.name %>: {
|
4
|
+
__init__: function(__base__) {
|
5
|
+
<% c._services.each do |s| %>
|
6
|
+
<%= s %>_on_connect(__base__);
|
7
|
+
<% end %>
|
8
|
+
},
|
9
|
+
__dealloc__: function(__base__) {
|
10
|
+
<% c._services.each do |s| %>
|
11
|
+
<%= s %>_on_disconnect(__base__);
|
12
|
+
<% end %>
|
13
|
+
},
|
4
14
|
name: '<%= c.name %>',
|
5
15
|
root_view: '<%= c.name %>',
|
6
16
|
spots: <%= c.spots.to_json %>,
|
data/lib/flok/version.rb
CHANGED