rollout_ui2 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a202103c0ae6445adcba865e7fdeb391e4b7cbe
4
- data.tar.gz: 7c5de71b01e84d0dbbe004401959509962a24633
3
+ metadata.gz: 64002c1fb49d0e15c4fec912f5960dd86b9af94d
4
+ data.tar.gz: 797c8dfcea7849943abd07c1ea72532ebfe7c94c
5
5
  SHA512:
6
- metadata.gz: d130d972401708102c4e3678ae5a2d27166d6d577a8970c3f2b0e762fe7c01db991597aed9743e883734a9a7569ce24868c15798b98366afa965b9c0c66c172c
7
- data.tar.gz: 152d123daabe0f7af2a594de57254acf9a1aea5c56d8ad2d6b1dd5a1464669e53772f7872c2a252d2479678920aa842f28db03062453093b7745023dfa76cbfb
6
+ metadata.gz: 5c7249d7ad063ad68fc1ebda86a74eec6a5ea2c1ebf69f3043332ec131c82c31377f92d154450852da9acd22612f8d458dd7b2e8b02047eacefc0c92ec7693f0
7
+ data.tar.gz: da1213298d6d5d02ec7fd69e00092a44d4700053f15e025fd8d1fcec7bbe060ba1fe2a51bbb8e88296d4d73b685843dc16cb3bef71f06a1b4f9a84cc0c148a27
data/Rakefile CHANGED
@@ -1,6 +1 @@
1
1
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
@@ -0,0 +1 @@
1
+ 2.3.0
data/example_rack/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'rerun'
4
+ gem 'pry'
3
5
  gem 'redis'
4
6
  gem 'rollout', '>= 2.0.0'
5
7
 
@@ -1,28 +1,48 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- rollout_ui2 (0.1.1)
4
+ rollout_ui2 (0.2.0)
5
5
  sinatra (~> 1.4, >= 1.4.7)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ coderay (1.1.1)
11
+ ffi (1.9.14)
12
+ listen (3.1.5)
13
+ rb-fsevent (~> 0.9, >= 0.9.4)
14
+ rb-inotify (~> 0.9, >= 0.9.7)
15
+ ruby_dep (~> 1.2)
16
+ method_source (0.8.2)
17
+ pry (0.10.4)
18
+ coderay (~> 1.1.0)
19
+ method_source (~> 0.8.1)
20
+ slop (~> 3.4)
10
21
  rack (1.6.5)
11
22
  rack-protection (1.5.3)
12
23
  rack
24
+ rb-fsevent (0.9.8)
25
+ rb-inotify (0.9.7)
26
+ ffi (>= 0.5.0)
13
27
  redis (3.3.2)
28
+ rerun (0.11.0)
29
+ listen (~> 3.0)
14
30
  rollout (2.4.0)
31
+ ruby_dep (1.5.0)
15
32
  sinatra (1.4.7)
16
33
  rack (~> 1.5)
17
34
  rack-protection (~> 1.4)
18
35
  tilt (>= 1.3, < 3)
36
+ slop (3.6.0)
19
37
  tilt (2.0.5)
20
38
 
21
39
  PLATFORMS
22
40
  ruby
23
41
 
24
42
  DEPENDENCIES
43
+ pry
25
44
  redis
45
+ rerun
26
46
  rollout (>= 2.0.0)
27
47
  rollout_ui2!
28
48
 
@@ -1,7 +1,55 @@
1
+ require 'rollout'
1
2
  require 'rollout_ui2'
2
-
3
3
  require 'redis'
4
- RolloutUi2.wrap(Rollout.new(Redis.new))
4
+
5
+ USERS = [ { id: 1,
6
+ text: "jimi.lepisto@example.com",
7
+ picture: "https://randomuser.me/api/portraits/thumb/men/21.jpg" },
8
+ { id: 2,
9
+ text: "emilia.koskinen@example.com",
10
+ picture: "https://randomuser.me/api/portraits/thumb/women/61.jpg" },
11
+ { id: 3,
12
+ text: "ron.perez@example.com",
13
+ picture: "https://randomuser.me/api/portraits/thumb/men/96.jpg" },
14
+ { id: 4,
15
+ text: "tim.carpenter@example.com",
16
+ picture: "https://randomuser.me/api/portraits/thumb/men/79.jpg" },
17
+ { id: 5,
18
+ text: "aatu.marttila@example.com",
19
+ picture: "https://randomuser.me/api/portraits/thumb/men/36.jpg" },
20
+ { id: 6,
21
+ text: "طاها.نكونظر@example.com",
22
+ picture: "https://randomuser.me/api/portraits/thumb/men/86.jpg" },
23
+ { id: 7,
24
+ text: "gabrielle.chu@example.com",
25
+ picture: "https://randomuser.me/api/portraits/thumb/women/40.jpg" },
26
+ { id: 8,
27
+ text: "hans.philipp@example.com",
28
+ picture: "https://randomuser.me/api/portraits/thumb/men/49.jpg" },
29
+ { id: 9,
30
+ text: "isabella.harris@example.com",
31
+ picture: "https://randomuser.me/api/portraits/thumb/women/32.jpg" },
32
+ { id: 10,
33
+ text: "stacey.olson@example.com",
34
+ picture: "https://randomuser.me/api/portraits/thumb/women/3.jpg" } ]
35
+
36
+ class User
37
+ def self.find_by_id(ids)
38
+ USERS.select { |it| ids.include?(it[:id].to_s) }
39
+ end
40
+
41
+ def self.search(query, page)
42
+ result = USERS.select { |it| %r{#{query}} =~ it[:text] }
43
+ per_page = 3
44
+ {
45
+ results: result[(page-1) * per_page...page * per_page],
46
+ per_page: per_page,
47
+ total_count: result.count
48
+ }
49
+ end
50
+ end
51
+
52
+ RolloutUi2.wrap(Rollout.new(Redis.new)).with_finder(User)
5
53
 
6
54
  RolloutUi2::Server.use Rack::Auth::Basic do |user, pass|
7
55
  user == pass
@@ -1,3 +1,3 @@
1
1
  module RolloutUi2
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/rollout_ui2.rb CHANGED
@@ -4,8 +4,18 @@ require 'yaml'
4
4
 
5
5
  module RolloutUi2
6
6
  class << self
7
+ def with_finder(finder)
8
+ @finder = finder
9
+ self
10
+ end
11
+
12
+ def finder
13
+ @finder
14
+ end
15
+
7
16
  def wrap(rollout)
8
17
  @rollout = rollout
18
+ self
9
19
  end
10
20
 
11
21
  def store
@@ -39,6 +49,10 @@ module RolloutUi2
39
49
  rollout.deactivate(feature.name)
40
50
  end
41
51
 
52
+ def groups
53
+
54
+ end
55
+
42
56
  private
43
57
 
44
58
  def multi(keys)
@@ -73,8 +87,27 @@ module RolloutUi2
73
87
 
74
88
  class Server < Sinatra::Base
75
89
  helpers do
90
+ def active_for(feature, key, user)
91
+ return unless user && user != ""
92
+ case key
93
+ when :any
94
+ feature.active?(RolloutUi2.rollout, user)
95
+ when :percentage
96
+ feature.feature.send(:user_in_percentage?, user) rescue nil
97
+ when :user
98
+ feature.feature.send(:user_in_active_users?, user) rescue nil
99
+ when :group
100
+ feature.feature.send(:user_in_active_group?, user, RolloutUi2.rollout) rescue nil
101
+ end && yield || nil
102
+ end
103
+
104
+ def user
105
+ params[:user]
106
+ end
107
+
76
108
  def all_groups(features)
77
- features.reduce([]) { |a, e| a | e.groups }
109
+ defined_groups = RolloutUi2.rollout.instance_eval("@groups").keys rescue []
110
+ defined_groups | features.reduce([]) { |a, e| a | e.groups }
78
111
  end
79
112
 
80
113
  def as_array(param)
@@ -103,6 +136,27 @@ module RolloutUi2
103
136
  return "eye-open" if public?(feature)
104
137
  "filter"
105
138
  end
139
+
140
+ def users_2_select2(users)
141
+ return users unless users_provided?
142
+ RolloutUi2
143
+ .finder
144
+ .find_by_id(Array(users))
145
+ .map { |it| it.merge!(selected: true, placeholder: it[:text]) }
146
+ .to_json
147
+ end
148
+
149
+ def users_provided?
150
+ !RolloutUi2.finder.nil?
151
+ end
152
+ end
153
+
154
+ get '/users' do
155
+ return status 404 unless users_provided?
156
+ RolloutUi2
157
+ .finder
158
+ .search(params["q"], (params["page"] || 1).to_i)
159
+ .to_json
106
160
  end
107
161
 
108
162
  get '/' do
@@ -127,7 +181,7 @@ module RolloutUi2
127
181
  RolloutUi2.save(feature)
128
182
  end
129
183
 
130
- redirect to('/')
184
+ redirect to("#{request.path_info}?#{request.query_string}")
131
185
  end
132
186
  end
133
187
  end
data/lib/views/index.erb CHANGED
@@ -1,24 +1,22 @@
1
- <div class="panel panel-default">
2
- <div class="panel-body">
3
- <form class="form-inline" method="post">
4
- <div class="form-group form-group-sm">
5
- <label for="newfeature">New feature</label>
6
- <input type="hidden" name="action" value="new" >
7
- <input type="text" name="name" placeholder="feature name" class="form-control" id="newfeature">
8
- </div>
9
- <button type="submit" class="btn btn-default btn-sm">Create</button>
10
- </form>
11
- </div>
1
+ <div class="jumbotron">
2
+ <label for="newfeature">New feature</label>
3
+ <form class="form-inline" method="post" action="<%= request.fullpath %>">
4
+ <div class="form-group form-group-sm">
5
+ <input type="hidden" name="action" value="new" >
6
+ <input type="text" name="name" placeholder="feature name" class="form-control" id="newfeature">
7
+ </div>
8
+ <button type="submit" class="btn btn-default btn-sm">Create</button>
9
+ </form>
12
10
  </div>
13
11
 
14
- <div>
15
- <% @features.each do |feature| %>
16
- <div id="feature-<%= feature.name.to_s %>"
17
- class="panel panel-default <%= 'has-success panel-success' if public?(feature) %>"
18
- style="<%= 'opacity:0.5' if hidden?(feature) %>">
12
+ <% @features.each_with_index do |feature, index| %>
13
+ <div class="row">
14
+ <div id="feature-<%= feature.name.to_s %>"
15
+ class="panel panel-default <%= active_for(feature, :any, user) { "panel-success" } %> <%= 'panel-success' if public?(feature) %>"
16
+ style="<%= 'opacity:0.5' if hidden?(feature) %>">
19
17
  <div class="panel-heading">
20
18
  <div >
21
- <form class="form-inline pull-right" data-require-confirm="true" method="post">
19
+ <form class="form-inline pull-right" data-require-confirm="true" method="post" action="<%= request.fullpath %>">
22
20
  <input type="hidden" name="action" value="delete" >
23
21
  <input type="hidden" name="name" value="<%= feature.name.to_s %>" >
24
22
  <button type="submit" class="glyphicon glyphicon-trash" style="background:none;border:none"><span class="sr-only">Delete</span></button>
@@ -33,10 +31,10 @@
33
31
  <div class="panel-body" style="position:relative">
34
32
  <div style="background:#F8F8F8;position:absolute;left:0;bottom:0px;height:8px;width:100%"></div>
35
33
  <div style="background:#EEEEEE;position:absolute;left:0;bottom:0px;height:8px;width:<%= feature.percentage %>%"></div>
36
- <form class="form-horizontal" method="post">
34
+ <form class="form-horizontal" method="post" action="<%= request.fullpath %>">
37
35
  <input type="hidden" name="action" value="update" >
38
36
  <input type="hidden" name="name" value="<%= feature.name.to_s %>" >
39
- <div class="form-group form-group-sm">
37
+ <div class="form-group form-group-sm <%= active_for(feature, :percentage, user) { "has-success" } %>">
40
38
  <label class="col-sm-2 control-label" for="percentage">Pecentage</label>
41
39
  <div class="col-sm-2">
42
40
  <div class="input-group">
@@ -46,10 +44,11 @@
46
44
  </div>
47
45
  </div>
48
46
 
49
- <div class="form-group form-group-sm">
47
+ <div class="form-group form-group-sm <%= active_for(feature, :group, user) { "has-success" } %>">
50
48
  <label class="col-sm-2 control-label" for="groups">Groups</label>
51
- <div class="col-sm-5">
52
- <select class="form-control" data-role="select2-tags" multiple="multiple" id="groups" name="groups[]">
49
+
50
+ <div class="col-sm-10">
51
+ <select data-tags="true" style="height:5ex" class="form-control" data-role="select2" multiple="multiple" id="groups" name="groups[]">
53
52
  <% (@groups - feature.groups).each do |f| %><option><%= f %></option><% end %>
54
53
  <% feature.groups.each do |f| %><option selected=true><%= f %></option><% end %>
55
54
  </select>
@@ -57,18 +56,29 @@
57
56
  </div>
58
57
  </div>
59
58
 
60
- <div class="form-group form-group-sm">
59
+ <div class="form-group form-group-sm <%= active_for(feature, :user, user) { "has-success" } %>">
61
60
  <label class="col-sm-2 control-label" for="users">Users</label>
62
- <div class="col-sm-5">
63
- <select class="form-control" data-role="select2-tags" multiple="multiple" id="users" name="users[]">
64
- <% feature.users.each do |f| %><option selected=true><%= f %></option><% end %>
65
- </select>
61
+ <div class="col-sm-10">
62
+ <% if users_provided? %>
63
+ <select
64
+ data-tags="true"
65
+ class="form-control user-api"
66
+ multiple="multiple"
67
+ id="users"
68
+ name="users[]"
69
+ data-data='<%= users_2_select2(feature.users) %>'>
70
+ </select>
71
+ <% else %>
72
+ <select class="form-control" data-role="select2" multiple="multiple" id="users" name="users[]">
73
+ <% feature.users.each do |f| %><option selected=true><%= f %></option><% end %>
74
+ </select>
75
+ <% end %>
66
76
  </div>
67
77
  </div>
68
78
 
69
79
  <% if feature.data? %>
70
80
  <div class="form-group form-group-sm">
71
- <div class="col-sm-offset-2 col-sm-5">
81
+ <div class="col-sm-offset-2 col-sm-10">
72
82
  <pre><%= feature.data %></pre>
73
83
  </div>
74
84
  </div>
@@ -81,6 +91,6 @@
81
91
  </div>
82
92
  </form>
83
93
  </div>
84
- </div>
85
- <% end %>
94
+ </div>
86
95
  </div>
96
+ <% end %>
data/lib/views/layout.erb CHANGED
@@ -7,43 +7,144 @@
7
7
  <link href="<%= u "vendor/bootstrap/css/bootstrap.min.css" %>" rel="stylesheet">
8
8
  <link href="<%= u "vendor/select2/css/select2.min.css" %>" rel="stylesheet">
9
9
  <link href="<%= u "vendor/bootstrap/css/bootstrap-theme.css" %>" rel="stylesheet">
10
+ <link href="https://fk.github.io/select2-bootstrap-css/css/select2-bootstrap.css" rel="stylesheet">
11
+ <style>
12
+ .main {
13
+ margin: auto;
14
+ max-width: 980px;
15
+ max-width: 750px;
16
+ }
17
+ /* pull-right on all except xs devices */
18
+ @media (min-width: 768px) {
19
+ .pull-right-sm {
20
+ float: right;
21
+ }
22
+ }
23
+
24
+ /*Make select2 responsive*/
25
+ [class*="col-"] .select2-container, .select2-container{
26
+ width:100%!important;
27
+ }
28
+ [class*="col-"] .select2-container .select2-search input[type="text"],
29
+ {
30
+ padding:2px 4%!important;
31
+ width:90%!important;
32
+ margin:5px 2%;
33
+ }
34
+ [class*="col-"] .select2-container .select2-drop {
35
+ width: 100%!important;
36
+ }
37
+
38
+ .has-success .select2-selection__choice,
39
+ .has-success .select2-selection__choice__remove {
40
+ color: #3c763d !important;
41
+ background-color: #dff0d8 !important;
42
+ border-color: #3c763d !important;
43
+ }
44
+
45
+ .select2-selection__rendered img {
46
+ width: 2.5ex !important;
47
+ }
48
+
49
+ .select2-selection--single, .select2-selection__arrow {
50
+ height: 34px !important;
51
+ padding: 2px;
52
+ }
53
+
54
+ .select2-selection__clear {
55
+ position: absolute;
56
+ z-index: 200
57
+ }
58
+
59
+ </style>
10
60
  </head>
11
61
  <body>
12
- <div class="container">
13
- <!-- Static navbar -->
14
- <nav class="navbar navbar-default" role="navigation">
15
- <div class="navbar-header">
16
- <a class="navbar-brand" href="/">RolloutUi2</a>
62
+ <nav class="navbar navbar-default">
63
+ <div class="container">
64
+ <div class="row">
65
+ <div class="navbar-header"><a class="navbar-brand" href="/">RolloutUi2</a></div>
66
+ <form method="get">
67
+ <div class="col-sm-6 col-sm-offset-4" style="top:7px">
68
+ <div class="form-group">
69
+ <div class="input-group select2-bootstrap-append">
70
+ <% if users_provided? %>
71
+ <select
72
+ data-tags="true"
73
+ class="form-control user-api"
74
+ id="user"
75
+ name="user"
76
+ data-allow-clear="true"
77
+ data-placeholder="Select a user"
78
+ data-data='<%= users_2_select2(params["user"]) %>'>
79
+ <option></option>
80
+ </select>
81
+ <% else %>
82
+ <input type="text" class="form-control" name="user" value="<%= params[:user] %>" placeholder="Select a user">
83
+ <% end %>
84
+ <span class="input-group-btn">
85
+ <button class="btn btn-default" type="submit" data-select2-open="multi-append">
86
+ <span class="glyphicon glyphicon-user"></span>
87
+ </button>
88
+ </span>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </form>
17
93
  </div>
18
- </nav>
19
-
94
+ </div><!-- /.container-fluid -->
95
+ </nav>
96
+ <main class="container main">
20
97
  <div>
21
- <%if @error then %>
22
- <div class="alert alert-error"><%=@error%></div>
23
- <% end %>
98
+ <%if @error then %><div class="alert alert-error"><%=@error%></div><% end %>
24
99
  <%= yield %>
25
100
  </div>
26
- </div>
101
+ </main>
27
102
  <script src="<%= u "vendor/jquery-1.9.1.min.js" %>"></script>
28
103
  <script src="<%= u "vendor/bootstrap/js/bootstrap.min.js" %>"></script>
29
- <script src="<%= u "vendor/select2/js/select2.min.js" %>"></script>
104
+ <script src="<%= u "vendor/select2/js/select2.js" %>"></script>
30
105
  <script src="<%= u "vendor/jq-debounce.min.js" %>"></script>
31
106
  <script type="text/javascript">
107
+ $('[data-role="select2"]').select2({tokenSeparators: [',', ' ']})
32
108
 
109
+ function template(item) {
110
+ if (!item.text) return null;
111
+ if (!item.picture) return '' + item.text;
112
+ return "<span><img src=\"" + item.picture + "\"> " + item.text + "</span>";
113
+ }
33
114
 
34
-
35
- $('[data-role="select2-tags"]').select2({
36
- tags: true,
37
- tokenSeparators: [',', ' ']
38
- })
115
+ $("select.user-api").select2({
116
+ ajax: {
117
+ url: "<%= u 'users' %>",
118
+ dataType: 'json',
119
+ delay: 250,
120
+ data: function (params) {
121
+ return { q: params.term, page: params.page };
122
+ },
123
+ processResults: function (data, params) {
124
+ params.page = params.page || 1;
125
+ return {
126
+ results: data.results,
127
+ pagination: { more: (params.page * data.per_page) < data.total_count }
128
+ };
129
+ },
130
+ cache: true
131
+ },
132
+ escapeMarkup: function (markup) { return markup; },
133
+ minimumInputLength: 1,
134
+ templateResult: template,
135
+ templateSelection: template
136
+ });
39
137
 
40
138
  $('[data-require-confirm="true"]').submit(function() {
41
139
  return confirm("Are you sure?");
42
140
  });
43
141
 
44
- $("input").focusout(function () {
45
- $(this).parents("form").submit();
142
+
143
+ $(".js-example-placeholder-single").select2({
144
+ placeholder: "Select a state",
145
+ allowClear: true
46
146
  });
147
+
47
148
  </script>
48
149
 
49
150
  </body>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rollout_ui2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manuel
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-12-05 00:00:00.000000000 Z
11
+ date: 2016-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -103,6 +103,7 @@ files:
103
103
  - bin/console
104
104
  - bin/setup
105
105
  - example_rack/.bundle/config
106
+ - example_rack/.ruby-version
106
107
  - example_rack/Gemfile
107
108
  - example_rack/Gemfile.lock
108
109
  - example_rack/config.ru
@@ -160,3 +161,4 @@ signing_key:
160
161
  specification_version: 4
161
162
  summary: WebUI for rollout 2
162
163
  test_files: []
164
+ has_rdoc: