editus 1.0.0 → 1.1.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
  SHA256:
3
- metadata.gz: 3cec41e8e70a12bb3dd860f31729437bad4870ff201eb3a4892be778fdcf8558
4
- data.tar.gz: 76b985c2519cb34198be4ce940128f1dd6265347e3ea48ec1cb1ceb5335b6028
3
+ metadata.gz: 60b7c7adcfdb0b6d489e79e0bb946c2a19df3ec8642d6ad701a11400db7e581f
4
+ data.tar.gz: 10ca7886874f225e7b3ff73d51a3befeee3bc10f09a5f5d6324c535a841a5372
5
5
  SHA512:
6
- metadata.gz: e705129944f1362d82e5dc9d749547ffd4ec0d59a4d315a0b90149f843f073f4dde0522ff3ec997b7d45c29b23780df1e4a56e94ac7a239b16e087c776e1a5c4
7
- data.tar.gz: 4b7d829b9c4df393a913f9b05eaebfa149c6953189ad413204e47e358409a8d1e35dfbbb0398d566686e31461729b64185e0d12a5566fe023d78733ea2fa14a8
6
+ metadata.gz: 38dbd733e0865a021849d89bcdd8c896082956903bee8ac717d38c09fd9cf62b8f38c857a9fa3a53b3ee39d08d94eee2633e7d216617d7caa66807cc4ddaf417
7
+ data.tar.gz: 69e48942bbeb9289aff7e256c105dd794fa7ff5aea852d1ddb759122be18e1af41710e778bc4c174447632b6eb959426d1705779bd597dc1468f02dd5447dfe0
data/README.md CHANGED
@@ -1,11 +1,30 @@
1
1
  # Editus
2
- Simplify code execution and database editing. Intuitive web interface. Run code snippets, modify databases in real-time.
3
2
 
4
- ## Installation
3
+ [![Gem Version](https://badge.fury.io/rb/editus.svg)](https://badge.fury.io/rb/editus)
4
+
5
+ Streamline your coding and database editing processes with Editus. Its intuitive web interface allows you to execute code snippets and perform real-time database modifications with ease.
6
+
7
+ # Table of contents
8
+
9
+ - [Editus](#editus)
10
+ - [Table of contents](#table-of-contents)
11
+ - [Installation](#installation)
12
+ - [Usage](#usage)
13
+ * [Authentication](#authentication)
14
+ * [Models](#models)
15
+ * [Add Script](#add-script)
16
+ + [Query](#query)
17
+ - [Contributing](#contributing)
18
+ - [License](#license)
19
+
20
+ <small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small>
21
+
22
+
23
+ # Installation
5
24
  Add this line to your application's Gemfile:
6
25
 
7
26
  ```ruby
8
- gem "editus", git: "https://github.com/muoihai-com/editus.git"
27
+ gem "editus"
9
28
  ```
10
29
 
11
30
  And then execute:
@@ -23,14 +42,14 @@ Add the following to your `config/routes.rb`:
23
42
 
24
43
  ```ruby
25
44
  Rails.application.routes.draw do
26
- mount Editus::Engine => "/updater" unless Rails.env.production?
45
+ mount Editus::Engine => "/editus" unless Rails.env.production?
27
46
  ...
28
47
  end
29
48
  ```
30
49
 
31
- ## Usage
50
+ # Usage
32
51
 
33
- ### Authentication
52
+ ## Authentication
34
53
 
35
54
  Editus supports two forms of authentication:
36
55
 
@@ -52,11 +71,19 @@ config.auth = [%w[user@example.com Pass@123456], %w[manager@example.com Pass@123
52
71
 
53
72
  Use one of the above authentication methods to secure access to Editus. Note that the `editus_account` method will take precedence if both methods are provided.
54
73
 
55
- ### Models
74
+ ## Models
56
75
 
57
76
  Display a simple form interface that helps you update the fields of the selected model. The update will use `update_columns` so will ignore callback and validate
58
77
 
59
- ### Add Script
78
+ You can configure to display only the models and fields that you allow:
79
+
80
+ ```
81
+ config.models = ["User", {name: "Admin", fields: %w[name], exclude_fields: %w[id]}]
82
+ ```
83
+
84
+ For the `Admin` model specified using the fields key. Additionally, the exclude_fields key is used to exclude the `id` field from being displayed.
85
+
86
+ ## Add Script
60
87
 
61
88
  To execute existing code create a directory `config/editus` in your code
62
89
 
@@ -66,12 +93,12 @@ Example:
66
93
  ```rb
67
94
  Editus::Script.define :update_nick_name_user do
68
95
  title "Update nick_name of user"
69
- task :up do
70
- user = User.find(1)
71
- nick_name = user.nick_name
72
- user.update_columns nick_name: "xatara"
96
+ task :up do |id, new_nick_name|
97
+ user = User.find(id)
98
+ old_nick_name = user.nick_name
99
+ user.update_columns nick_name: new_nick_name
73
100
 
74
- [1, nick_name]
101
+ [id, old_nick_name]
75
102
  end
76
103
 
77
104
  task :down do |id, nick_name|
@@ -85,10 +112,23 @@ Make sure the filename and the defined name are the same. In the above code `tit
85
112
  `task :up` is the code that will be executed when you run it.
86
113
  `task :down` is the code that will be executed when you undo, if you don't need to undo you can skip it.
87
114
 
88
- It can use the result returned from the `up` function to use as an input parameter
115
+ It can use the result returned from the `up` method to use as an input parameter. The `up` method can also accept parameters directly.
116
+
117
+ ### Query
118
+
119
+ In addition to running the two tasks `up` and `down`, you can perform other small code snippets by using `query`.
120
+
121
+ ```rb
122
+ Editus::Script.define :users do
123
+ desc "Number of users"
124
+ query :count_user do
125
+ User.count
126
+ end
127
+ end
128
+ ```
89
129
 
90
- ## Contributing
130
+ # Contributing
91
131
  Contribution directions go here.
92
132
 
93
- ## License
133
+ # License
94
134
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -53,12 +53,26 @@ module Editus
53
53
  action.user = request_account
54
54
  action.model_id = script.name
55
55
  action.model_name = script.title
56
- action.changes = script.proxy.up if script.proxy.respond_to?(:up)
56
+ action.changes = script.proxy.up(*parameters) if script.proxy.respond_to?(:up)
57
57
  action.save
58
58
 
59
59
  redirect_to root_path
60
60
  end
61
61
 
62
+ def query
63
+ script = Editus::Script.all[params[:id].to_sym]
64
+ method = params[:method].to_sym
65
+ return render json: {status: false, error: :script_not_found}, status: 200 if script.blank?
66
+
67
+ unless script.proxy.respond_to?(method)
68
+ return render json: {status: false, error: :undefined_method},
69
+ status: 200
70
+ end
71
+
72
+ result = script.proxy.try(method, *parameters)
73
+ render json: {status: true, data: result}, status: 200
74
+ end
75
+
62
76
  def undo
63
77
  action = Editus::Actions.all.find{|act| act.id == params[:id]}
64
78
  return redirect_to(root_path) if action.blank?
@@ -117,5 +131,9 @@ module Editus
117
131
  params[:user]&.each{|key, _| params[:user].delete(key) if params[:user][key].blank?}
118
132
  (params[:user] || {}).as_json
119
133
  end
134
+
135
+ def parameters
136
+ params[:parameters]&.values || []
137
+ end
120
138
  end
121
139
  end
@@ -5,14 +5,31 @@ module Editus
5
5
  end
6
6
 
7
7
  def field_tag record, col, *args
8
- if record.try(col).is_a?(Time)
8
+ case record.type_of_col(col)
9
+ when :datetime
9
10
  text_field_tag(*args, placeholder: "YYYY-MM-DD HH:mm:ss UTC")
10
- elsif [true, false].include? record.try(col)
11
+ when :boolean
11
12
  safe_join [hidden_field_tag(args.first, "false", id: nil),
12
13
  check_box_tag(args.first, "true", record.try(col))]
13
14
  else
14
15
  text_field_tag(*args)
15
16
  end
16
17
  end
18
+
19
+ def parameters proxy, method
20
+ return [] unless proxy.respond_to?(method)
21
+
22
+ proxy.method(method).parameters.filter_map do |(type, value)|
23
+ value if type.in?(%i[req opt])
24
+ end
25
+ end
26
+
27
+ def field_enums? record, col
28
+ record.defined_enums.key?(col.to_s) && record.defined_enums[col].present?
29
+ end
30
+
31
+ def enums record, col
32
+ record.defined_enums[col].keys.join(", ")
33
+ end
17
34
  end
18
35
  end
@@ -2,7 +2,7 @@
2
2
  <h1 class="ui header">Models</h1>
3
3
  </div>
4
4
 
5
- <%= form_tag validate_path, class: 'ui form warning' do %>
5
+ <%= form_tag validate_path, class: 'ui form warning manual' do %>
6
6
  <div class="ui mt-4 text container">
7
7
  <p>
8
8
  <%= select_tag "klass", options_for_select(@model_names.sort, params[:klass]), prompt: "Select a model", id: 'select-klass' %>
@@ -40,6 +40,10 @@
40
40
  <%= label_tag "user[#{col}]", class: "flex items-center" do %>
41
41
  <%= field_tag @record, col, "user[#{col}]", nil %><span class="ml-2">True</span>
42
42
  <% end %>
43
+ <% elsif field_enums?(@record, col) %>
44
+ <div class="ui" data-tooltip="<%= enums(@record, col) %>" data-inverted="" data-variation="mini">
45
+ <%= field_tag @record, col, "user[#{col}]", nil %>
46
+ </div>
43
47
  <% else %>
44
48
  <%= field_tag @record, col, "user[#{col}]", nil %>
45
49
  <% end %>
@@ -18,33 +18,157 @@
18
18
  <% @scripts.each do |script| %>
19
19
  <section id="<%= script.name %>">
20
20
  <h1 class="ui header"><%= script.title %></h1>
21
- <div>
22
- <button class="ui labeled mini icon red button confirmation" data-method="run">
21
+ <% if parameters(script.proxy, :up).present? %>
22
+ <button class="ui labeled mini icon red button parameters" data-name="<%= script.name %>">
23
23
  <i class="plus icon"></i>
24
24
  Run
25
25
  </button>
26
+ <% end %>
26
27
 
27
- <%= form_tag run_path(id: script.name), method: :post, id: "run" do %>
28
- <% end %>
28
+ <div class="ui parameters-<%= script.name %> small modal">
29
+ <div class="header">
30
+ <%= parameters(script.proxy, :up).present? ? "Parameters" : "Confirmation" %>
31
+ </div>
32
+ <div class="scrolling content">
33
+ <%= form_tag run_path(id: script.name), method: :post, id: "run-#{script.name}", class: 'ui form' do %>
34
+ <% parameters(script.proxy, :up).each do |key| %>
35
+ <div class="inline fields">
36
+ <div class="two wide field">
37
+ </div>
38
+ <div class="six wide field">
39
+ <label><%= key.to_s.capitalize %></label>
40
+ </div>
41
+ <div class="six wide field">
42
+ <%= text_field_tag "parameters[#{key}]", nil %>
43
+ </div>
44
+ </div>
45
+ <% end %>
46
+ <% end %>
47
+
48
+ <p class="<%= parameters(script.proxy, :up).present? ? "mt-8" : "" %>">
49
+ These changes may cause data errors, do you still want to continue?
50
+ </p>
51
+ </div>
52
+ <div class="actions">
53
+ <div class="ui cancel button">Cancel</div>
54
+ <div class="ui red ok button">OK</div>
55
+ </div>
29
56
  </div>
30
57
  <div class="ui accordion">
31
58
  <div class="title">
32
59
  <i class="dropdown icon"></i>
33
- Up
60
+ Content
34
61
  </div>
35
62
  <div class="content">
36
63
  <div class="transition hidden">
37
- <div class="editor"><%= script.up %></div>
64
+ <div class="editor"><%= script.content %></div>
38
65
  </div>
39
66
  </div>
40
- <div class="title">
41
- <i class="dropdown icon"></i>
42
- Down
43
- </div>
44
- <div class="content">
45
- <div class="editor"><%= script.down %></div>
46
- </div>
67
+
68
+ <% if script.queries.present? %>
69
+ <div class="title">
70
+ <i class="dropdown icon"></i>
71
+ Queries
72
+ </div>
73
+ <div class="content">
74
+ <div class="transition hidden">
75
+ <div class="ui middle aligned selection list">
76
+ <% script.queries.each_with_index do |query, index| %>
77
+ <% query_key = "#{script.name}-#{index}" %>
78
+
79
+ <div class="item flex items-center justify-space-between query" data-index="<%= query_key %>">
80
+ <div class="content">
81
+ <%= query[:description] %>
82
+ </div>
83
+ <div class="content">
84
+ <div class="ui mini icon primary button">
85
+ <i class="terminal icon"></i>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <div class="ui query_<%= query_key %> small modal">
91
+ <div class="header">
92
+ Query
93
+ </div>
94
+ <div class="scrolling content">
95
+ <%= form_tag query_path(id: script.name, method: query[:query_name]), method: :post, id: "query_form_#{query_key}", class: 'ui form' do %>
96
+ <% parameters(script.proxy, query[:query_name]).each do |key| %>
97
+ <div class="inline fields">
98
+ <div class="two wide field">
99
+ </div>
100
+ <div class="six wide field">
101
+ <label><%= key.to_s.capitalize %></label>
102
+ </div>
103
+ <div class="six wide field">
104
+ <%= text_field_tag "parameters[#{key}]", nil %>
105
+ </div>
106
+ </div>
107
+ <% end %>
108
+ <% end %>
109
+
110
+ <p class="<%= parameters(script.proxy, query[:query_name]).present? ? "mt-8" : "" %>">
111
+ These changes may cause data errors, do you still want to continue?
112
+ </p>
113
+
114
+ <button class="ui button primary" id="run_query_<%= query_key %>">
115
+ Query
116
+ </button>
117
+
118
+ <pre class="bg-neutral-300 rounded p-4" id="pre_query_<%= query_key %>" style="display: none;">
119
+ </pre>
120
+ </div>
121
+ <div class="actions">
122
+ <div class="ui cancel button">Close</div>
123
+ </div>
124
+ </div>
125
+ <% end %>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ <% end %>
130
+ </div>
47
131
  </section>
48
132
  <div class="ui divider"></div>
49
133
  <% end %>
50
134
  </div>
135
+
136
+ <script>
137
+ $("button.parameters").on("click", function (e) {
138
+ const name = $(e.currentTarget).data('name')
139
+
140
+ $(`.ui.parameters-${name}.modal`)
141
+ .modal({
142
+ onApprove: function () {
143
+ $(`#run-${name}`).submit()
144
+ }
145
+ })
146
+ .modal("show");
147
+ });
148
+
149
+ $(".query").on("click", function (e) {
150
+ const index = $(e.currentTarget).data('index')
151
+
152
+ $(`.ui.query_${index}.modal`).modal("show");
153
+ $(`#run_query_${index}`).on("click", function() {
154
+ const formId = `#query_form_${index}`
155
+ const preId = `#pre_query_${index}`
156
+ const serializeArray = $(formId).serializeArray()
157
+ const action = $(formId).attr("action")
158
+
159
+ $.post(action, $(formId).serialize()).done(function(res) {
160
+ if (res.status) {
161
+ $(preId).text(JSON.stringify(res.data))
162
+ } else {
163
+ $(preId).text(`Query Error: ${res.error}`)
164
+ }
165
+
166
+ $(preId).show()
167
+ });
168
+ })
169
+ });
170
+
171
+ $('form input').on('keypress', function(e) {
172
+ return e.which !== 13;
173
+ });
174
+ </script>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta name="viewport" content="width=device-width, initial-scale=1" />
5
5
 
6
- <title>Model updater</title>
6
+ <title>Editus</title>
7
7
  <%= csrf_meta_tags %>
8
8
  <%= csp_meta_tag %>
9
9
 
@@ -48,11 +48,14 @@
48
48
  margin-top: 7em;
49
49
  }
50
50
  .flex{
51
- display: flex;
51
+ display: flex !important;
52
52
  }
53
- items-center{
53
+ .items-center{
54
54
  align-items: center;
55
55
  }
56
+ .justify-space-between {
57
+ justify-content: space-between;
58
+ }
56
59
  .flex-column {
57
60
  flex-direction: column;
58
61
  }
@@ -86,6 +89,9 @@
86
89
  .mt-4 {
87
90
  margin-top: 1rem;
88
91
  }
92
+ .mt-8 {
93
+ margin-top: 2rem;
94
+ }
89
95
  .mx-4 {
90
96
  margin-bottom: 1rem;
91
97
  margin-top: 1rem;
@@ -95,6 +101,7 @@
95
101
  }
96
102
  pre {
97
103
  white-space: pre-line;
104
+ word-break: break-word;
98
105
  }
99
106
  .ui.cards>.orange.card, .ui.orange.card, .ui.orange.cards>.card {
100
107
  box-shadow: 0 0 0 1px #d4d4d5, 0 0px 3px 1px #f2711c, 0 1px 3px 0 #d4d4d5;
@@ -108,12 +115,18 @@
108
115
  .ui.celled.table tr td, .ui.celled.table tr th {
109
116
  word-break: break-all;
110
117
  }
118
+ .ui.list>.item.flex:after {
119
+ display: none;
120
+ }
121
+ [data-tooltip][data-variation='mini']:after {
122
+ font-size: 12px;
123
+ }
111
124
  </style>
112
125
  </head>
113
126
  <body>
114
127
  <div class="ui fixed inverted menu">
115
128
  <div class="ui container">
116
- <%= link_to 'Model Updater', root_path, class: "header item" %>
129
+ <%= link_to 'Editus', root_path, class: "header item" %>
117
130
  <%= link_to 'Home', root_path, class: "item" %>
118
131
  <div class="ui simple dropdown item">
119
132
  Menu <i class="dropdown icon"></i>
@@ -146,7 +159,6 @@
146
159
  $(".ui.confirmation.modal")
147
160
  .modal({
148
161
  onApprove: function () {
149
- console.log($(e.currentTarget).data('method'))
150
162
  switch ($(e.currentTarget).data('method')) {
151
163
  case 'run':
152
164
  $("#" + $(e.currentTarget).data('method')).submit()
data/config/routes.rb CHANGED
@@ -5,7 +5,7 @@ Editus::Engine.routes.draw do
5
5
  get "scripts", to: "home#scripts"
6
6
  post "/update", to: "home#update", as: :update
7
7
  post "validate", to: "home#validate", as: :validate
8
-
9
8
  post "scripts/:id/run", to: "home#run_script", as: :run
10
9
  post "undo/:id", to: "home#undo", as: :undo
10
+ post "query/:id/:method", to: "home#query", as: :query
11
11
  end
data/lib/editus/cop.rb CHANGED
@@ -33,9 +33,9 @@ module Editus
33
33
  return nil if model.blank?
34
34
  return nil unless model.is_a?(String) || model.is_a?(Hash)
35
35
 
36
- name = model.is_a?(String) ? model : model["name"]
37
- fields = model.is_a?(Hash) ? model["fields"] : []
38
- exclude_fields = model.is_a?(Hash) ? model["exclude_fields"] : []
36
+ name = model.is_a?(String) ? model : model[:name]
37
+ fields = model.is_a?(Hash) ? model[:fields] : []
38
+ exclude_fields = model.is_a?(Hash) ? model[:exclude_fields] : []
39
39
 
40
40
  {name: name, fields: fields, exclude_fields: exclude_fields}
41
41
  end
data/lib/editus/proxy.rb CHANGED
@@ -29,7 +29,7 @@ module Editus
29
29
  info = Editus::Cop.instance.info(klass)
30
30
  all = cols
31
31
  exclude_fields = info[:exclude_fields] || []
32
- fields = info[:fields] ? (all & info[:fields]) : all
32
+ fields = info[:fields].present? ? (all & info[:fields]) : all
33
33
 
34
34
  fields - %w[id] - exclude_fields
35
35
  end
@@ -55,6 +55,14 @@ module Editus
55
55
  klass.update_columns attributes
56
56
  end
57
57
 
58
+ def type_of_col column
59
+ klass.class.columns_hash[column.to_s]&.type
60
+ end
61
+
62
+ def defined_enums
63
+ klass.class.defined_enums
64
+ end
65
+
58
66
  private
59
67
 
60
68
  def cols
data/lib/editus/script.rb CHANGED
@@ -21,10 +21,16 @@ module Editus
21
21
  @scripts[name.to_sym] || @scripts[name.to_sym] = new(name.to_sym)
22
22
  end
23
23
 
24
- attr_accessor :name, :title, :path, :proxy, :content
24
+ attr_accessor :name, :path, :proxy, :content, :queries
25
+ attr_writer :title
25
26
 
26
27
  def initialize name = nil
27
28
  @name = name
29
+ @queries = []
30
+ end
31
+
32
+ def add_query desc, name
33
+ @queries.push({description: desc, query_name: name})
28
34
  end
29
35
 
30
36
  def up
@@ -34,6 +40,10 @@ module Editus
34
40
  def down
35
41
  content[/task\s.*down.*do.*\n[\s\S]*?\n\s*end/]
36
42
  end
43
+
44
+ def title
45
+ @title || @name.to_s.humanize
46
+ end
37
47
  end
38
48
 
39
49
  class DSL
@@ -45,6 +55,7 @@ module Editus
45
55
 
46
56
  def initialize name
47
57
  @name = name
58
+ @query_index = 0
48
59
  end
49
60
 
50
61
  def task method, &block
@@ -59,6 +70,31 @@ module Editus
59
70
  internal = Internal.find_or_create @name
60
71
  internal.title = txt
61
72
  end
73
+
74
+ def desc description
75
+ @description = if description.blank? || !description.is_a?(String)
76
+ @query_index += 1
77
+ "Query##{@query_index}"
78
+ else
79
+ description
80
+ end
81
+ end
82
+
83
+ def query method, &block
84
+ internal = Internal.find_or_create @name
85
+ internal.proxy ||= Editus::DefinitionProxy.new(@name)
86
+ query_name = "query_#{method}"
87
+ internal.proxy.define_singleton_method(query_name, &block)
88
+ add_query_to_internal internal, query_name
89
+ end
90
+
91
+ private
92
+
93
+ def add_query_to_internal internal, query_name
94
+ description = desc(@description)
95
+ internal.add_query description, query_name
96
+ @description = nil
97
+ end
62
98
  end
63
99
 
64
100
  class Reader
@@ -1,5 +1,5 @@
1
1
  module Editus
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  SUMMARY = "This application is a simple web interface integrated with an existing Ruby on Rails application. It allows users to directly edit models, run predefined scripts, and easily undo changes."
4
4
  DESCRIPTION = <<~DOC
5
5
  This application is a user-friendly web interface designed to work with a Ruby on Rails application. With a simple and intuitive web interface, users can conveniently access the features of the application.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: editus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hungkieu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-02 00:00:00.000000000 Z
11
+ date: 2023-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -89,7 +89,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
89
  requirements:
90
90
  - - ">="
91
91
  - !ruby/object:Gem::Version
92
- version: '0'
92
+ version: 2.6.0
93
93
  required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - ">="