rollout_ui2 0.2.0 → 0.3.0

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 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: