flipper-ui 0.2.0.beta2 → 0.2.0.beta3

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.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -11
  3. data/README.md +5 -7
  4. data/examples/basic.ru +27 -26
  5. data/flipper-ui.gemspec +5 -3
  6. data/lib/flipper-ui.rb +1 -0
  7. data/lib/flipper/ui.rb +11 -2
  8. data/lib/flipper/ui/action.rb +45 -4
  9. data/lib/flipper/ui/actions/actors_gate.rb +48 -0
  10. data/lib/flipper/ui/actions/add_feature.rb +20 -0
  11. data/lib/flipper/ui/actions/boolean_gate.rb +26 -0
  12. data/lib/flipper/ui/actions/feature.rb +32 -0
  13. data/lib/flipper/ui/actions/features.rb +23 -3
  14. data/lib/flipper/ui/actions/file.rb +1 -1
  15. data/lib/flipper/ui/actions/gate.rb +11 -117
  16. data/lib/flipper/ui/actions/groups_gate.rb +43 -0
  17. data/lib/flipper/ui/actions/{index.rb → home.rb} +3 -3
  18. data/lib/flipper/ui/actions/percentage_of_actors_gate.rb +27 -0
  19. data/lib/flipper/ui/actions/percentage_of_time_gate.rb +27 -0
  20. data/lib/flipper/ui/actor.rb +13 -0
  21. data/lib/flipper/ui/assets/javascripts/application.coffee +0 -305
  22. data/lib/flipper/ui/assets/stylesheets/_bootstrap-compass.scss +9 -0
  23. data/lib/flipper/ui/assets/stylesheets/_bootstrap-mincer.scss +19 -0
  24. data/lib/flipper/ui/assets/stylesheets/_bootstrap-sprockets.scss +9 -0
  25. data/lib/flipper/ui/assets/stylesheets/_bootstrap.scss +50 -0
  26. data/lib/flipper/ui/assets/stylesheets/application.scss +33 -216
  27. data/lib/flipper/ui/assets/stylesheets/bootstrap/_alerts.scss +73 -0
  28. data/lib/flipper/ui/assets/stylesheets/bootstrap/_badges.scss +68 -0
  29. data/lib/flipper/ui/assets/stylesheets/bootstrap/_breadcrumbs.scss +26 -0
  30. data/lib/flipper/ui/assets/stylesheets/bootstrap/_button-groups.scss +243 -0
  31. data/lib/flipper/ui/assets/stylesheets/bootstrap/_buttons.scss +160 -0
  32. data/lib/flipper/ui/assets/stylesheets/bootstrap/_carousel.scss +269 -0
  33. data/lib/flipper/ui/assets/stylesheets/bootstrap/_close.scss +36 -0
  34. data/lib/flipper/ui/assets/stylesheets/bootstrap/_code.scss +69 -0
  35. data/lib/flipper/ui/assets/stylesheets/bootstrap/_component-animations.scss +37 -0
  36. data/lib/flipper/ui/assets/stylesheets/bootstrap/_dropdowns.scss +214 -0
  37. data/lib/flipper/ui/assets/stylesheets/bootstrap/_forms.scss +578 -0
  38. data/lib/flipper/ui/assets/stylesheets/bootstrap/_glyphicons.scss +305 -0
  39. data/lib/flipper/ui/assets/stylesheets/bootstrap/_grid.scss +84 -0
  40. data/lib/flipper/ui/assets/stylesheets/bootstrap/_input-groups.scss +166 -0
  41. data/lib/flipper/ui/assets/stylesheets/bootstrap/_jumbotron.scss +50 -0
  42. data/lib/flipper/ui/assets/stylesheets/bootstrap/_labels.scss +66 -0
  43. data/lib/flipper/ui/assets/stylesheets/bootstrap/_list-group.scss +124 -0
  44. data/lib/flipper/ui/assets/stylesheets/bootstrap/_media.scss +61 -0
  45. data/lib/flipper/ui/assets/stylesheets/bootstrap/_mixins.scss +39 -0
  46. data/lib/flipper/ui/assets/stylesheets/bootstrap/_modals.scss +150 -0
  47. data/lib/flipper/ui/assets/stylesheets/bootstrap/_navbar.scss +662 -0
  48. data/lib/flipper/ui/assets/stylesheets/bootstrap/_navs.scss +242 -0
  49. data/lib/flipper/ui/assets/stylesheets/bootstrap/_normalize.scss +427 -0
  50. data/lib/flipper/ui/assets/stylesheets/bootstrap/_pager.scss +54 -0
  51. data/lib/flipper/ui/assets/stylesheets/bootstrap/_pagination.scss +88 -0
  52. data/lib/flipper/ui/assets/stylesheets/bootstrap/_panels.scss +265 -0
  53. data/lib/flipper/ui/assets/stylesheets/bootstrap/_popovers.scss +135 -0
  54. data/lib/flipper/ui/assets/stylesheets/bootstrap/_print.scss +107 -0
  55. data/lib/flipper/ui/assets/stylesheets/bootstrap/_progress-bars.scss +87 -0
  56. data/lib/flipper/ui/assets/stylesheets/bootstrap/_responsive-embed.scss +35 -0
  57. data/lib/flipper/ui/assets/stylesheets/bootstrap/_responsive-utilities.scss +177 -0
  58. data/lib/flipper/ui/assets/stylesheets/bootstrap/_scaffolding.scss +162 -0
  59. data/lib/flipper/ui/assets/stylesheets/bootstrap/_tables.scss +234 -0
  60. data/lib/flipper/ui/assets/stylesheets/bootstrap/_theme.scss +273 -0
  61. data/lib/flipper/ui/assets/stylesheets/bootstrap/_thumbnails.scss +38 -0
  62. data/lib/flipper/ui/assets/stylesheets/bootstrap/_tooltip.scss +102 -0
  63. data/lib/flipper/ui/assets/stylesheets/bootstrap/_type.scss +298 -0
  64. data/lib/flipper/ui/assets/stylesheets/bootstrap/_utilities.scss +55 -0
  65. data/lib/flipper/ui/assets/stylesheets/bootstrap/_variables.scss +866 -0
  66. data/lib/flipper/ui/assets/stylesheets/bootstrap/_wells.scss +29 -0
  67. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_alerts.scss +14 -0
  68. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_background-variant.scss +11 -0
  69. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_border-radius.scss +18 -0
  70. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_buttons.scss +52 -0
  71. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_center-block.scss +7 -0
  72. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_clearfix.scss +22 -0
  73. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_forms.scss +88 -0
  74. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_gradients.scss +58 -0
  75. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_grid-framework.scss +81 -0
  76. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_grid.scss +122 -0
  77. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_hide-text.scss +21 -0
  78. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_image.scss +33 -0
  79. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_labels.scss +12 -0
  80. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_list-group.scss +31 -0
  81. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_nav-divider.scss +10 -0
  82. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_nav-vertical-align.scss +9 -0
  83. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_opacity.scss +8 -0
  84. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_pagination.scss +23 -0
  85. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_panels.scss +24 -0
  86. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_progress-bar.scss +10 -0
  87. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_reset-filter.scss +8 -0
  88. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_resize.scss +6 -0
  89. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_responsive-visibility.scss +21 -0
  90. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_size.scss +10 -0
  91. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_tab-focus.scss +9 -0
  92. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_table-row.scss +28 -0
  93. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_text-emphasis.scss +11 -0
  94. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_text-overflow.scss +8 -0
  95. data/lib/flipper/ui/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +222 -0
  96. data/lib/flipper/ui/assets/stylesheets/primer/.scss-lint.yml +446 -0
  97. data/lib/flipper/ui/assets/stylesheets/primer/_alerts.scss +106 -0
  98. data/lib/flipper/ui/assets/stylesheets/primer/_avatars.scss +36 -0
  99. data/lib/flipper/ui/assets/stylesheets/primer/_base.scss +40 -0
  100. data/lib/flipper/ui/assets/stylesheets/primer/_blankslate.scss +96 -0
  101. data/lib/flipper/ui/assets/stylesheets/primer/_buttons.scss +404 -0
  102. data/lib/flipper/ui/assets/stylesheets/primer/_counter.scss +10 -0
  103. data/lib/flipper/ui/assets/stylesheets/primer/_filter-list.scss +68 -0
  104. data/lib/flipper/ui/assets/stylesheets/primer/_flex-table.scss +20 -0
  105. data/lib/flipper/ui/assets/stylesheets/primer/_forms.scss +756 -0
  106. data/lib/flipper/ui/assets/stylesheets/primer/_layout.scss +69 -0
  107. data/lib/flipper/ui/assets/stylesheets/primer/_menu.scss +113 -0
  108. data/lib/flipper/ui/assets/stylesheets/primer/_mixins.scss +53 -0
  109. data/lib/flipper/ui/assets/stylesheets/primer/_normalize.scss +425 -0
  110. data/lib/flipper/ui/assets/stylesheets/primer/_states.scss +32 -0
  111. data/lib/flipper/ui/assets/stylesheets/primer/_tabnav.scss +65 -0
  112. data/lib/flipper/ui/assets/stylesheets/primer/_tooltips.scss +255 -0
  113. data/lib/flipper/ui/assets/stylesheets/primer/_truncate.scss +27 -0
  114. data/lib/flipper/ui/assets/stylesheets/primer/_type.scss +92 -0
  115. data/lib/flipper/ui/assets/stylesheets/primer/_utility.scss +73 -0
  116. data/lib/flipper/ui/assets/stylesheets/primer/_variables.scss +34 -0
  117. data/lib/flipper/ui/assets/stylesheets/primer/primer.scss +39 -0
  118. data/lib/flipper/ui/decorators/feature.rb +37 -4
  119. data/lib/flipper/ui/middleware.rb +12 -3
  120. data/lib/flipper/ui/public/css/application.css +2563 -144
  121. data/lib/flipper/ui/public/css/primer.css +1 -0
  122. data/lib/flipper/ui/public/css/primer/primer.css +1933 -0
  123. data/lib/flipper/ui/public/css/scss/primer.css +1933 -0
  124. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
  125. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.svg +288 -0
  126. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
  127. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
  128. data/lib/flipper/ui/public/fonts/bootstrap/glyphicons-halflings-regular.woff2 +0 -0
  129. data/lib/flipper/ui/public/js/application.js +0 -539
  130. data/lib/flipper/ui/public/js/bootstrap-sprockets.js +12 -0
  131. data/lib/flipper/ui/public/js/bootstrap.js +2317 -0
  132. data/lib/flipper/ui/public/js/bootstrap.min.js +7 -0
  133. data/lib/flipper/ui/public/js/bootstrap/affix.js +162 -0
  134. data/lib/flipper/ui/public/js/bootstrap/alert.js +94 -0
  135. data/lib/flipper/ui/public/js/bootstrap/button.js +116 -0
  136. data/lib/flipper/ui/public/js/bootstrap/carousel.js +237 -0
  137. data/lib/flipper/ui/public/js/bootstrap/collapse.js +211 -0
  138. data/lib/flipper/ui/public/js/bootstrap/dropdown.js +161 -0
  139. data/lib/flipper/ui/public/js/bootstrap/modal.js +339 -0
  140. data/lib/flipper/ui/public/js/bootstrap/popover.js +108 -0
  141. data/lib/flipper/ui/public/js/bootstrap/scrollspy.js +172 -0
  142. data/lib/flipper/ui/public/js/bootstrap/tab.js +153 -0
  143. data/lib/flipper/ui/public/js/bootstrap/tooltip.js +476 -0
  144. data/lib/flipper/ui/public/js/bootstrap/transition.js +59 -0
  145. data/lib/flipper/ui/public/octicons/LICENSE.txt +9 -0
  146. data/lib/flipper/ui/public/octicons/README.md +1 -0
  147. data/lib/flipper/ui/public/octicons/octicons-local.ttf +0 -0
  148. data/lib/flipper/ui/public/octicons/octicons.css +236 -0
  149. data/lib/flipper/ui/public/octicons/octicons.eot +0 -0
  150. data/lib/flipper/ui/public/octicons/octicons.less +235 -0
  151. data/lib/flipper/ui/public/octicons/octicons.svg +200 -0
  152. data/lib/flipper/ui/public/octicons/octicons.ttf +0 -0
  153. data/lib/flipper/ui/public/octicons/octicons.woff +0 -0
  154. data/lib/flipper/ui/public/octicons/sprockets-octicons.scss +232 -0
  155. data/lib/flipper/ui/util.rb +4 -0
  156. data/lib/flipper/ui/version.rb +1 -1
  157. data/lib/flipper/ui/views/add_actor.erb +22 -0
  158. data/lib/flipper/ui/views/add_feature.erb +18 -0
  159. data/lib/flipper/ui/views/add_group.erb +31 -0
  160. data/lib/flipper/ui/views/feature.erb +209 -0
  161. data/lib/flipper/ui/views/features.erb +46 -0
  162. data/lib/flipper/ui/views/layout.erb +31 -149
  163. data/script/release +15 -0
  164. data/spec/flipper/ui/actions/actors_gate_spec.rb +68 -0
  165. data/spec/flipper/ui/actions/add_feature_spec.rb +17 -0
  166. data/spec/flipper/ui/actions/boolean_gate_spec.rb +41 -0
  167. data/spec/flipper/ui/actions/feature_spec.rb +59 -0
  168. data/spec/flipper/ui/actions/features_spec.rb +37 -0
  169. data/spec/flipper/ui/actions/file_spec.rb +43 -0
  170. data/spec/flipper/ui/actions/gate_spec.rb +24 -0
  171. data/spec/flipper/ui/actions/groups_gate_spec.rb +80 -0
  172. data/spec/flipper/ui/actions/home_spec.rb +16 -0
  173. data/spec/flipper/ui/actions/percentage_of_actors_gate_spec.rb +40 -0
  174. data/spec/flipper/ui/actions/percentage_of_time_gate_spec.rb +39 -0
  175. data/spec/flipper/ui/decorators/feature_spec.rb +38 -1
  176. data/spec/flipper/ui/decorators/gate_spec.rb +1 -1
  177. data/spec/flipper/ui_spec.rb +18 -441
  178. data/spec/helper.rb +22 -7
  179. metadata +199 -52
  180. data/examples/flipper.html +0 -14
  181. data/examples/flipper.png +0 -0
  182. data/lib/flipper/ui/assets/javascripts/spine/ajax.coffee +0 -223
  183. data/lib/flipper/ui/assets/javascripts/spine/list.coffee +0 -43
  184. data/lib/flipper/ui/assets/javascripts/spine/local.coffee +0 -16
  185. data/lib/flipper/ui/assets/javascripts/spine/manager.coffee +0 -83
  186. data/lib/flipper/ui/assets/javascripts/spine/relation.coffee +0 -148
  187. data/lib/flipper/ui/assets/javascripts/spine/route.coffee +0 -146
  188. data/lib/flipper/ui/assets/javascripts/spine/spine.coffee +0 -542
  189. data/lib/flipper/ui/assets/javascripts/spine/version +0 -1
  190. data/lib/flipper/ui/public/css/images/animated-overlay.gif +0 -0
  191. data/lib/flipper/ui/public/css/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  192. data/lib/flipper/ui/public/css/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  193. data/lib/flipper/ui/public/css/images/ui-bg_flat_10_000000_40x100.png +0 -0
  194. data/lib/flipper/ui/public/css/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  195. data/lib/flipper/ui/public/css/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  196. data/lib/flipper/ui/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  197. data/lib/flipper/ui/public/css/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  198. data/lib/flipper/ui/public/css/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  199. data/lib/flipper/ui/public/css/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  200. data/lib/flipper/ui/public/css/images/ui-icons_222222_256x240.png +0 -0
  201. data/lib/flipper/ui/public/css/images/ui-icons_228ef1_256x240.png +0 -0
  202. data/lib/flipper/ui/public/css/images/ui-icons_ef8c08_256x240.png +0 -0
  203. data/lib/flipper/ui/public/css/images/ui-icons_ffd27a_256x240.png +0 -0
  204. data/lib/flipper/ui/public/css/images/ui-icons_ffffff_256x240.png +0 -0
  205. data/lib/flipper/ui/public/css/jquery-ui-1.10.3.slider.min.css +0 -5
  206. data/lib/flipper/ui/public/js/handlebars.js +0 -1992
  207. data/lib/flipper/ui/public/js/jquery-ui-1.10.3.slider.min.js +0 -6
  208. data/lib/flipper/ui/public/js/jquery.js +0 -9555
  209. data/lib/flipper/ui/public/js/jquery.min.js +0 -4
  210. data/lib/flipper/ui/public/js/jquery.min.map +0 -1
  211. data/lib/flipper/ui/public/js/spine/ajax.js +0 -320
  212. data/lib/flipper/ui/public/js/spine/list.js +0 -72
  213. data/lib/flipper/ui/public/js/spine/local.js +0 -29
  214. data/lib/flipper/ui/public/js/spine/manager.js +0 -157
  215. data/lib/flipper/ui/public/js/spine/relation.js +0 -260
  216. data/lib/flipper/ui/public/js/spine/route.js +0 -223
  217. data/lib/flipper/ui/public/js/spine/spine.js +0 -927
  218. data/lib/flipper/ui/views/index.erb +0 -9
@@ -6,7 +6,7 @@ module Flipper
6
6
  module Actions
7
7
  class File < UI::Action
8
8
 
9
- route %r{(images|css|js)/.*\Z}
9
+ route %r{(images|css|js|octicons|fonts)/.*\Z}
10
10
 
11
11
  def get
12
12
  Rack::File.new(public_path).call(request.env)
@@ -1,141 +1,35 @@
1
- require 'flipper/ui/util'
2
1
  require 'flipper/ui/action'
3
- require 'flipper/ui/actions/index'
2
+ require 'flipper/ui/actor'
4
3
  require 'flipper/ui/decorators/feature'
5
4
 
6
5
  module Flipper
7
6
  module UI
8
7
  module Actions
9
8
  class Gate < UI::Action
9
+ route %r{features/[^/]*/[^/]*/?\Z}
10
10
 
11
- # Private: Struct to wrap actors so they can respond to flipper_id.
12
- FakeActor = Struct.new(:flipper_id)
13
-
14
- route %r{features/.*/.*/?\Z}
15
-
16
- # Get should run the index route. All the url does is control what is
17
- # opened and closed when the page is loaded.
18
- def get
19
- run_other_action Index
20
- end
21
-
22
- # FIXME: Return more than just the gate as json response?
23
11
  def post
24
- feature_name, gate_name = request.path.split('/').pop(2).map{|value| Rack::Utils.unescape value }
12
+ feature_name, gate_name = request.path.split('/').pop(2).map{ |value| Rack::Utils.unescape value }
25
13
  update_gate_method_name = "update_#{gate_name}"
26
14
 
27
- unless respond_to?(update_gate_method_name)
28
- update_gate_method_undefined(gate_name)
29
- end
30
-
31
15
  feature = flipper[feature_name.to_sym]
32
- send(update_gate_method_name, feature)
33
- gate = feature.gate(gate_name)
34
- value = feature.gate_values[gate.key]
35
-
36
- json_response Decorators::Gate.new(gate, value).as_json
37
- end
16
+ @feature = Decorators::Feature.new(feature)
38
17
 
39
- def update_boolean(feature)
40
- if params['value'] == 'true'
41
- feature.enable
18
+ if respond_to?(update_gate_method_name, true)
19
+ send(update_gate_method_name, feature)
42
20
  else
43
- feature.disable
44
- end
45
- end
46
-
47
- def update_actor(feature)
48
- value = params['value']
49
-
50
- if Util.blank?(value)
51
- invalid_actor_value(value)
52
- end
53
-
54
- thing = FakeActor.new(value)
55
- actor = flipper.actor(thing)
56
-
57
- case params['operation']
58
- when 'enable'
59
- feature.enable actor
60
- when 'disable'
61
- feature.disable actor
62
- end
63
- end
64
-
65
- def update_group(feature)
66
- group_name = params['value']
67
- group = flipper.group(group_name)
68
-
69
- case params['operation']
70
- when 'enable'
71
- feature.enable group
72
- when 'disable'
73
- feature.disable group
21
+ update_gate_method_undefined(gate_name)
74
22
  end
75
- rescue Flipper::GroupNotRegistered => e
76
- group_not_registered group_name
77
- end
78
-
79
- def update_percentage_of_actors(feature)
80
- value = params['value']
81
- feature.enable_percentage_of_actors value
82
- rescue ArgumentError => exception
83
- invalid_percentage value, exception
84
- end
85
-
86
- def update_percentage_of_time(feature)
87
- value = params['value']
88
- feature.enable_percentage_of_time value
89
- rescue ArgumentError => exception
90
- invalid_percentage value, exception
91
- end
92
-
93
- # Private: Returns error response for invalid actor value.
94
- def invalid_actor_value(value)
95
- response = {
96
- status: 'error',
97
- message: "#{value.inspect} is not a valid actor value.",
98
- }
99
23
 
100
- status 422
101
- halt json_response(response)
24
+ redirect_to "/features/#{@feature.key}"
102
25
  end
103
26
 
104
- # Private: Returns error response for invalid percentage value.
105
- def invalid_percentage(value, exception)
106
- response = {
107
- status: 'error',
108
- message: exception.message,
109
- }
110
-
111
- status 422
112
- halt json_response(response)
113
- end
114
-
115
- # Private: Returns error response that group was not registered.
116
- def group_not_registered(group_name)
117
- response = {status: 'error'}
118
-
119
- if Util.blank?(group_name)
120
- status 422
121
- response[:message] = "Group name is required."
122
- else
123
- status 404
124
- response[:message] = "The group named #{group_name.inspect} has not been registered."
125
- end
126
-
127
- halt json_response(response)
128
- end
27
+ private
129
28
 
130
29
  # Private: Returns error response that gate update method is not defined.
131
30
  def update_gate_method_undefined(gate_name)
132
- response = {
133
- status: 'error',
134
- message: "I have no clue how to update the gate named #{gate_name.inspect}.",
135
- }
136
-
137
- status 404
138
- halt json_response(response)
31
+ error = Rack::Utils.escape("#{gate_name.inspect} gate does not exist therefore it cannot be updated.")
32
+ redirect_to("/features/#{@feature.key}?error=#{error}")
139
33
  end
140
34
  end
141
35
  end
@@ -0,0 +1,43 @@
1
+ require 'flipper/ui/action'
2
+ require 'flipper/ui/decorators/feature'
3
+
4
+ module Flipper
5
+ module UI
6
+ module Actions
7
+ class GroupsGate < UI::Action
8
+ route %r{features/[^/]*/groups/?\Z}
9
+
10
+ def get
11
+ feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
12
+ feature = flipper[feature_name.to_sym]
13
+ @feature = Decorators::Feature.new(feature)
14
+
15
+ breadcrumb "Home", "/"
16
+ breadcrumb "Features", "/features"
17
+ breadcrumb @feature.key, "/features/#{@feature.key}"
18
+ breadcrumb "Add Group"
19
+
20
+ view_response :add_group
21
+ end
22
+
23
+ def post
24
+ feature_name = Rack::Utils.unescape(request.path.split('/')[-2])
25
+ feature = flipper[feature_name.to_sym]
26
+ value = params["value"]
27
+
28
+ case params["operation"]
29
+ when "enable"
30
+ feature.enable_group value
31
+ when "disable"
32
+ feature.disable_group value
33
+ end
34
+
35
+ redirect_to("/features/#{feature.key}")
36
+ rescue Flipper::GroupNotRegistered => e
37
+ error = Rack::Utils.escape("The group named #{value.inspect} has not been registered.")
38
+ redirect_to("/features/#{feature.key}/groups?error=#{error}")
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -4,12 +4,12 @@ require 'flipper/ui/decorators/feature'
4
4
  module Flipper
5
5
  module UI
6
6
  module Actions
7
- class Index < UI::Action
7
+ class Home < UI::Action
8
8
 
9
- route %r{.*}
9
+ route %r{/?\Z}
10
10
 
11
11
  def get
12
- view_response :index
12
+ redirect_to "/features"
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,27 @@
1
+ require 'flipper/ui/action'
2
+ require 'flipper/ui/decorators/feature'
3
+
4
+ module Flipper
5
+ module UI
6
+ module Actions
7
+ class PercentageOfActorsGate < UI::Action
8
+ route %r{features/[^/]*/percentage_of_actors/?\Z}
9
+
10
+ def post
11
+ feature_name = Rack::Utils.unescape(request.path.split("/")[-2])
12
+ feature = flipper[feature_name.to_sym]
13
+ @feature = Decorators::Feature.new(feature)
14
+
15
+ begin
16
+ feature.enable_percentage_of_actors params["value"]
17
+ rescue ArgumentError => exception
18
+ error = Rack::Utils.escape("Invalid percentage of actors value: #{exception.message}")
19
+ redirect_to("/features/#{@feature.key}?error=#{error}")
20
+ end
21
+
22
+ redirect_to "/features/#{@feature.key}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ require 'flipper/ui/action'
2
+ require 'flipper/ui/decorators/feature'
3
+
4
+ module Flipper
5
+ module UI
6
+ module Actions
7
+ class PercentageOfTimeGate < UI::Action
8
+ route %r{features/[^/]*/percentage_of_time/?\Z}
9
+
10
+ def post
11
+ feature_name = Rack::Utils.unescape(request.path.split("/")[-2])
12
+ feature = flipper[feature_name.to_sym]
13
+ @feature = Decorators::Feature.new(feature)
14
+
15
+ begin
16
+ feature.enable_percentage_of_time params["value"]
17
+ rescue ArgumentError => exception
18
+ error = Rack::Utils.escape("Invalid percentage of time value: #{exception.message}")
19
+ redirect_to("/features/#{@feature.key}?error=#{error}")
20
+ end
21
+
22
+ redirect_to "/features/#{@feature.key}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module Flipper
2
+ module UI
3
+ # Internal: Shim for turning a string flipper id into something that responds to
4
+ # flipper_id for Flipper::Types::Actor.
5
+ class Actor
6
+ attr_reader :flipper_id
7
+
8
+ def initialize(flipper_id)
9
+ @flipper_id = flipper_id
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,305 +0,0 @@
1
- class Feature extends Spine.Model
2
- @configure "Feature", "id", "name", "state", "description", "gates"
3
- @extend Spine.Model.Ajax
4
- @extend url: "#{Flipper.Config.url}/features"
5
-
6
- constructor: ->
7
- super
8
- @gates = @gates.map (data) =>
9
- data.feature_id = @id
10
- new Gate(data)
11
-
12
- gate: (name) ->
13
- gates = @gates.filter (gate) ->
14
- gate.name == name
15
- gates[0]
16
-
17
- window.Feature = Feature
18
-
19
- class Gate extends Spine.Model
20
- @configure "Gate", "feature_id", "key", "name", "value"
21
-
22
- constructor: ->
23
- super
24
-
25
- url: ->
26
- "#{Flipper.Config.url}/features/#{encodeURIComponent @feature_id}/#{encodeURIComponent @name}"
27
-
28
- disableSetMember: (member, success_callback, error_callback) ->
29
- @setMember('disable', member, success_callback, error_callback)
30
-
31
- enableSetMember: (member, success_callback, error_callback) ->
32
- @setMember('enable', member, success_callback, error_callback)
33
-
34
- setMember: (operation, member, success_callback, error_callback) ->
35
- options =
36
- type: 'POST'
37
- url: @url()
38
- data:
39
- operation: operation
40
- value: member
41
- success: (data, status, xhr) =>
42
- Feature.trigger('reload')
43
- @value = data.value
44
- success_callback(data, status, xhr) if success_callback
45
- error: (data, status, error) =>
46
- response = if data.responseText then $.parseJSON data.responseText else message: "Something went wrong..."
47
- alert "ERROR: #{response.message}"
48
- error_callback(data, status) if error_callback
49
-
50
- $.ajax options
51
-
52
- save: (opts) ->
53
- result = super
54
- @ajaxSave(opts)
55
- Feature.trigger('reload')
56
- result
57
-
58
- ajaxSave: (opts) ->
59
- options =
60
- type: 'POST'
61
- url: @url()
62
- data:
63
- value: @value
64
- error: (data, status, error) =>
65
- response = if data.responseText then $.parseJSON data.responseText else message: "Something went wrong..."
66
- alert "ERROR: #{response.message}"
67
-
68
- $.ajax options
69
-
70
- class App extends Spine.Controller
71
- constructor: ->
72
- super
73
- @feature_list = new App.FeatureList(el: $('#features'))
74
-
75
- class App.FeatureList extends Spine.Controller
76
- constructor: ->
77
- super
78
- @feature_controllers = {}
79
- Feature.bind "refresh", @addAll
80
- Feature.bind "reload", @reload
81
-
82
- Feature.one 'refresh', ->
83
- Spine.Route.setup
84
- history: true
85
-
86
- Feature.fetch()
87
-
88
- Spine.Route.add /features\/(.*)\/(.*)\/?/, (matches) =>
89
- params =
90
- id: matches.match[1]
91
- gate: matches.match[2]
92
- if controller = @feature_controllers[params.id]
93
- controller.edit()
94
- controller.activateGate(params)
95
-
96
- Spine.Route.add /features\/(.*)\/?/, (matches) =>
97
- params =
98
- id: matches.match[1]
99
- if controller = @feature_controllers[params.id]
100
- controller.edit()
101
- controller.openDefaultGate()
102
-
103
- addOne: (feature) =>
104
- controller = new App.Feature(feature: feature)
105
- @feature_controllers[feature.id] = controller
106
- @append controller.render()
107
-
108
- addAll: =>
109
- @html ''
110
- $('#no_features').hide()
111
- all_features = Feature.all()
112
- if all_features.length > 0
113
- @addOne feature for feature in all_features
114
- else
115
- $('#no_features').show()
116
-
117
- reload: =>
118
- Feature.fetch()
119
- @addAll
120
-
121
- class App.Feature extends Spine.Controller
122
- elements:
123
- '.feature': 'dom_feature'
124
- '.gates': 'dom_gates'
125
-
126
- events:
127
- 'click .show-settings': 'openFeature'
128
- 'click .hide-settings': 'hide'
129
- 'click [data-tab]': 'clickTab'
130
-
131
- constructor: ->
132
- super
133
- throw "@feature required" if !@feature?
134
-
135
- render: ->
136
- @html @template(@feature)
137
- @gate_list = new App.GateList
138
- el: @dom_gates
139
- @el
140
-
141
- openFeature: (event) ->
142
- event.preventDefault() if event
143
- @navigate "#{Flipper.Config.url}/features/#{@feature.id}"
144
-
145
- openDefaultGate: ->
146
- @navigate "#{Flipper.Config.url}/features/#{@feature.id}/boolean"
147
-
148
- template: (feature) ->
149
- source = $("#feature-template").html()
150
- template = Handlebars.compile(source)
151
- template(feature)
152
-
153
- clickTab: (event) ->
154
- event.preventDefault()
155
- tab = $(event.currentTarget)
156
- name = tab.attr('data-tab')
157
- @navigate "#{Flipper.Config.url}/features/#{@feature.id}/#{name}"
158
-
159
- activateGate: (params) ->
160
- name = params.gate
161
- @gate_list[name].active(params)
162
- @el.find('[data-tab]').removeClass('active')
163
- @el.find("[data-tab=#{name}]").addClass('active')
164
-
165
- edit: (event) ->
166
- event.preventDefault() if event
167
- @dom_feature.addClass('settings')
168
-
169
- hide: (event) ->
170
- event.preventDefault() if event
171
- @dom_feature.removeClass('settings')
172
- @navigate "#{Flipper.Config.url}/"
173
-
174
- class App.Gate extends Spine.Controller
175
- constructor: ->
176
- super
177
- @active @renderForParams
178
-
179
- renderForParams: (params) ->
180
- @feature = Feature.find(params.id)
181
- @gate = @feature.gate(params.gate)
182
- @render()
183
-
184
- render: ->
185
- @html @template("#gate-#{@name.replace(/_/g, '-')}-template", @gate)
186
- $slider = $(".slider-range")
187
- $slider_value = $slider.siblings("input[type='text']")
188
-
189
- $slider.slider
190
- range: "min",
191
- value: @gate.value,
192
- min: 0,
193
- max: 100,
194
- slide: ( event, ui ) ->
195
- $slider_value.val( ui.value )
196
- return
197
-
198
- $slider_value.val $slider.slider( "value" )
199
- $slider_value.change ()->
200
- $slider.slider "value", $(@).val()
201
- return
202
-
203
- template: (html_id, context) ->
204
- source = $(html_id).html()
205
- template = Handlebars.compile(source)
206
- template(context)
207
-
208
- class App.Gate.Boolean extends App.Gate
209
- elements:
210
- 'input[value=true]': 'input'
211
-
212
- events:
213
- 'submit form': 'submit'
214
-
215
- constructor: ->
216
- @name = 'boolean'
217
- super
218
-
219
- submit: (event) ->
220
- event.preventDefault()
221
- @gate.value = @input.is(':checked')
222
- @gate.save()
223
- @navigate "#{Flipper.Config.url}/"
224
-
225
- class App.Gate.Set extends App.Gate
226
- elements:
227
- '.disable': 'dom_disable'
228
- '.members': 'dom_members'
229
- '[name=value]': 'dom_input'
230
-
231
- events:
232
- 'click .disable': 'disable'
233
- 'submit form': 'submit'
234
-
235
- disable: (event) ->
236
- event.preventDefault()
237
- member = $(event.currentTarget).closest('.member')
238
- value = member.attr('data-value')
239
- @gate.disableSetMember value, (data, status, xhr) ->
240
- member.remove()
241
-
242
- submit: (event) ->
243
- event.preventDefault()
244
- value = @dom_input.val()
245
- self = @
246
-
247
- @gate.enableSetMember value, (data, status, xhr) ->
248
- html = self.template "#gate-member-template", value
249
- self.dom_members.append html
250
- self.dom_input.val ''
251
-
252
- class App.Gate.Group extends App.Gate.Set
253
- constructor: ->
254
- @name = 'group'
255
- super
256
-
257
- class App.Gate.Actor extends App.Gate.Set
258
- constructor: ->
259
- @name = 'actor'
260
- super
261
-
262
- class App.Gate.Percentage extends App.Gate
263
- elements:
264
- 'input[type=text]': 'input'
265
-
266
- events:
267
- 'submit form': 'submit'
268
-
269
- validate: ()->
270
- float_value = parseFloat(@gate.value)
271
- valid = true
272
-
273
- if isNaN(float_value) || float_value < 0 || float_value > 100
274
- alert "The percentage value provided is not valid"
275
- valid = false
276
-
277
- return valid
278
-
279
- submit: (event) ->
280
- event.preventDefault()
281
- @gate.value = @input.val()
282
- return unless @validate()
283
- @gate.save()
284
- @navigate "#{Flipper.Config.url}/"
285
-
286
- class App.Gate.PercentageOfActors extends App.Gate.Percentage
287
- constructor: ->
288
- @name = 'percentage_of_actors'
289
- super
290
-
291
- class App.Gate.PercentageOfTime extends App.Gate.Percentage
292
- constructor: ->
293
- @name = 'percentage_of_time'
294
- super
295
-
296
- class App.GateList extends Spine.Stack
297
- controllers:
298
- boolean: App.Gate.Boolean
299
- group: App.Gate.Group
300
- actor: App.Gate.Actor
301
- percentage_of_actors: App.Gate.PercentageOfActors
302
- percentage_of_time: App.Gate.PercentageOfTime
303
-
304
- jQuery ->
305
- new App(el: $('#app'))