1659340560
Tabulo is a Ruby library for generating plain text tables, also known as “ASCII tables”. It is both highly configurable and very easy to use.
Quick API:
> puts Tabulo::Table.new(User.all, :id, :first_name, :last_name).pack
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | John | Citizen |
| 2 | Jane | Doe |
+----+------------+-----------+
Full API:
table = Tabulo::Table.new(User.all) do |t|
t.add_column("ID", &:id)
t.add_column("First name", &:first_name)
t.add_column("Last name") { |user| user.last_name.upcase }
end
> puts table.pack
+----+------------+-----------+
| ID | First name | Last name |
+----+------------+-----------+
| 1 | John | CITIZEN |
| 2 | Jane | DOE |
+----+------------+-----------+
Enumerable
: the underlying collection need not be an arrayTabulo has also been ported to Crystal (with some modifications): see Tablo.
Add this line to your application’s Gemfile:
gem 'tabulo'
And then execute:
$ bundle
Or install it yourself:
$ gem install tabulo
To use the gem, you need to require it in your source code as follows:
require 'tabulo'
You instantiate a Tabulo::Table
by passing it an underlying Enumerable
, being the collection of things that you want to tabulate. Each member of this collection will end up corresponding to a row of the table. The collection can be any Enumerable
, for example a Ruby Array
, or an ActiveRecord relation:
table = Tabulo::Table.new([1, 2, 5])
other_table = Tabulo::Table.new(User.all)
For the table to be useful, however, it must also contain columns…
When the columns correspond to methods on members of the underlying enumerable, you can use the “quick API”, by passing a symbol directly to Tabulo::Table.new
for each column. This symbol also provides the column header:
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
> puts table
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 5 | false | true |
+--------------+--------------+--------------+
Columns can also be added to the table one-by-one using add_column
. This “full API” is more verbose, but provides greater configurability:
table = Tabulo::Table.new([1, 2, 5])
table.add_column(:itself)
table.add_column(:even?)
table.add_column(:odd?)
Alternatively, you can pass an initialization block to new
:
table = Tabulo::Table.new([1, 2, 5]) do |t|
t.add_column(:itself)
t.add_column(:even?)
t.add_column(:odd?)
end
With the full API, columns can also be initialized using a callable to which each object will be passed to determine the value to be displayed in the table. In this case, the first argument to add_column
provides the header text:
table = Tabulo::Table.new([1, 2, 5]) do |t|
t.add_column("N", &:itself)
t.add_column("Doubled") { |n| n * 2 }
t.add_column(:odd?)
end
> puts table
+--------------+--------------+--------------+
| N | Doubled | odd? |
+--------------+--------------+--------------+
| 1 | 2 | true |
| 2 | 4 | false |
| 5 | 10 | true |
+--------------+--------------+--------------+
The add_column
method can be passed a single parameter callable, as shown in the above example, with the parameter representing the member of the underyling enumerable; or it can be passed 2-parameter callable, with the second parameter representing the (0-based) index of each row. This can be useful if you want to display a row number in one of the columns:
table = Tabulo::Table.new(["a", "b", "c"]) do |t|
t.add_column("Row") { |letter, row_index| row_index }
t.add_column("Value", &:itself)
end
> puts table
+--------------+--------------+
| Row | Value |
+--------------+--------------+
| 0 | a |
| 1 | b |
| 2 | c |
+--------------+--------------+
The first argument to add_column
is the called the label for that column. It serves as the column’s unique identifier: only one column may have a given label per table. (String
s and Symbol
s are interchangeable for this purpose.) The label also forms the header shown at the top of the column, unless a separate header:
argument is explicitly passed:
table.add_column(:itself, header: "N")
table.add_column(:itself2, header: "N", &:itself) # header need not be unique
# table.add_column(:itself) # would raise Tabulo::InvalidColumnLabelError, as label must be unique
By default, each new column is added to the right of all the other columns so far added to the table. However, if you want to insert a new column into some other position, you can use the before
option, passing the label of the column to the left of which you want the new column to be added:
table = Tabulo::Table.new([1, 2, 3], :itself, :odd?)
table.add_column(:even?, before: :odd?)
> puts table
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 5 | false | true |
+--------------+--------------+--------------+
Sometimes the data source for the table may be a collection of hashes or arrays. For example:
data = [
{ english: "hello", portuguese: "bom dia" },
{ english: "goodbye", portuguese: "adeus" },
]
or
data = [
["hello", "bom dia"],
["goodbye", "adeus"],
]
To tabulate such a collection, simply use the same mechanism as described above, passing a block to the add_column
method to tell Tabulo how to extract the data for each column from a row. For example, to tabulate the first example above, you could do something like this:
table = Tabulo::Table.new(data) do |t|
t.add_column("English") { |h| h[:english] }
t.add_column("Portuguese") { |h| h[:portuguese] }
end
puts table
For the second example, you could do the following:
table = Tabulo::Table.new(data) do |t|
t.add_column("English") { |a| a[0] }
t.add_column("Portuguese") { |a| a[1] }
end
puts table
In both cases, the output will be as follows:
+--------------+--------------+
| English | Portuguese |
+--------------+--------------+
| hello | bom dia |
| goodbye | adeus |
+--------------+--------------+
If you have previously used other terminal tabulation libraries, you may be accustomed to being required to place your data into an array of hashes or arrays before you can tabulate them. Tabulo, however, offers an API that is more general and flexible than this; your data source can be any enumerable collection (not just an array), and each item in that collection can be any object (not necessarily an array or a hash). However, as shown above, it is still straightforward to tabulate an array of hashes or arrays, if your data source happens to take that form.
There is also a #remove_column
method, for deleting an existing column from a table. Pass it the label of the column you want to remove:
table.remove_column(:even?)
You can give your table a title, using the title
option:
table = Tabulo::Table.new([1, 2, 3], :itself, :even?, :odd?, title: "Numbers")
> puts table
+--------------------------------------------+
| Numbers |
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
+--------------+--------------+--------------+
There is a caveat: Using the title
option with the :markdown
border type will cause the rendered table to cease being valid Markdown, as unfortunately almost no markdown engines support adding a captions (i.e. titles) to tables.
By default, column header text is center-aligned, while the content of each body cell is aligned according to its data type. Numbers are right-aligned, text is left-aligned, and booleans (false
and true
) are center-aligned.
This default behaviour can be set at the table level, by passing :center
, :left
or :right
to the align_header
or align_body
options when initializing the table:
table = Tabulo::Table.new([1, 2], :itself, :even?, align_header: :left, align_body: :right)
The table-level alignment settings can be overridden for individual columns by passing similarly-named options to add_column
, e.g.:
table.add_column("Doubled", align_header: :right, align_body: :left) { |n| n * 2 }
If a table title is present, it is center-aligned by default. This can be changed using the align_title
option when initializing the table:
table = Tabulo::Table.new([1, 2], :itself, :even?, title: "Numbers", align_title: :left)
By default, column width is fixed at 12 characters, plus 1 character of padding on either side. This can be adjusted on a column-by-column basis using the width
option of add_column
:
table = Tabulo::Table.new([1, 2]) do |t|
t.add_column(:itself, width: 6)
t.add_column(:even?, width: 9)
end
> puts table
+--------+-----------+
| itself | even? |
+--------+-----------+
| 1 | false |
| 2 | true |
+--------+-----------+
If you want to set the default column width for all columns of the table to something other than 12, use the column_width
option when initializing the table:
table = Tabulo::Table.new([1, 2], :itself, :even?, column_width: 6)
> puts table
+--------+--------+
| itself | even? |
+--------+--------+
| 1 | false |
| 2 | true |
+--------+--------+
Widths set for individual columns always override the default column width for the table.
Instead of setting column widths “manually”, you can tell the table to sort out the widths itself, so that each column is just wide enough for its header and contents (plus a character of padding on either side):
table = Tabulo::Table.new(["short", "here is a longer phrase"], :itself, :size)
table.pack
> puts table
+-------------------------+------+
| itself | size |
+-------------------------+------+
| short | 5 |
| here is a longer phrase | 23 |
+-------------------------+------+
If the table title happens to be too long to for the existing width of the table, pack
will also arrange for the table to be widened sufficiently to accommodate it without wrapping:
table = Tabulo::Table.new(["a", "b"], :itself, :size, title: "Here are some letters of the alphabet")
table.pack
> puts table
+---------------------------------------+
| Here are some letters of the alphabet |
+-------------------+-------------------+
| itself | size |
+-------------------+-------------------+
| a | 1 |
| b | 1 |
+-------------------+-------------------+
The pack
method returns the table itself, so you can “pack-and-print” in one go:
puts Tabulo::Table.new(["short", "here is a longer phrase"], :itself, :size).pack
You can manually place an upper limit on the total width of the table when packing:
puts Tabulo::Table.new(["short", "here is a longer phrase"], :itself, :size).pack(max_table_width: 24)
+---------------+------+
| itself | size |
+---------------+------+
| short | 5 |
| here is a lon | 23 |
| ger phrase | |
+---------------+------+
Or if you simply call pack
with no arguments (or if you explicitly call pack(max_table_width: :auto)
), the table width will automatically be capped at the width of your terminal.
If you want the table width not to be capped at all, call pack(max_table_width: nil)
.
If the table cannot be fit within the width of the terminal, or the specified maximum width, then column widths are reduced as required, with wrapping or truncation then occuring as necessary (see Overflow handling). Under the hood, a character of width is deducted column by column—the widest column being targetted each time—until the table will fit.
To resize only specific columns, pack
takes an except:
argument, which can be a single column label or an Array of column labels. E.g. pack(except: :id)
will exclude the id
column from resizing and let it keep its current width. This is useful if you want to prevent the addition of linebreaks in your data. When using this option, other columns might be shrunk more to still make the table fit within the max_table_width
.
For even finer-grained control over column and table resizing, see the for the #autosize_columns
and #shrink_to
methods.
Note that pack
ing the table necessarily involves traversing the entire collection up front as the maximum cell width needs to be calculated for each column. You may not want to do this if the collection is very large.
Note also the effect of pack
is to fix the column widths as appropriate to the formatted cell contents given the state of the underlying collection at the point of packing. If the underlying collection changes between that point, and when the table is printed, then the columns will not be resized yet again on printing. This is a consequence of the table always being essentially a “live view” on the underlying collection: formatted contents are never cached within the table itself. There are ways around this, however, if this is not the desired behaviour—see below.
The single character of padding either side of each column is not counted in the column width. The amount of this extra padding can be configured for the table as a whole, using the column_padding
option passed to Table.new
—the default value of this option being 1
.
Passing a single integer to this option causes the given amount of padding to be applied to each side of each column. For example:
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: 0)
> puts table.pack
+------+-----+-----+
|itself|even?| odd?|
+------+-----+-----+
| 1|false| true|
| 2| true|false|
| 5|false| true|
+------+-----+-----+
Passing an array of two integers to this option configures the left and right padding for each column, according to the first and second element of the array, respectively. For example:
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?, column_padding: [0, 2])
> puts table.pack
+--------+-------+-------+
|itself |even? | odd? |
+--------+-------+-------+
| 1 |false | true |
| 2 | true |false |
| 5 |false | true |
+--------+-------+-------+
Note how the padding amount is completely unaffected by the call pack
.
Padding can also be configured on a column-by-column basis, using the padding
option when calling add_column
:
table = Tabulo::Table.new([1, 2, 5], :itself, :even?)
table.add_column(:odd?, padding: 3)
> puts table.pack
+--------+-------+-----------+
| itself | even? | odd? |
+--------+-------+-----------+
| 1 | false | true |
| 2 | true | false |
| 5 | false | true |
+--------+-------+-----------+
This column-level padding
setting always overrides any table-level column_padding
setting, for the column in question.
By default, if cell contents exceed their column width, they are wrapped for as many rows as required:
table = Tabulo::Table.new(
["hello", "abcdefghijklmnopqrstuvwxyz"],
:itself, :length
)
> puts table
+--------------+--------------+
| itself | length |
+--------------+--------------+
| hello | 5 |
| abcdefghijkl | 26 |
| mnopqrstuvwx | |
| yz | |
+--------------+--------------+
Wrapping behaviour is configured for the table as a whole using the wrap_header_cells_to
option for header cells and wrap_body_cells_to
for body cells, both of which default to nil
, meaning that cells are wrapped to as many rows as required. Passing an Integer
limits wrapping to the given number of rows, with content truncated from that point on. The ~
character is appended to the outputted cell content to show that truncation has occurred:
table = Tabulo::Table.new(
["hello", "abcdefghijklmnopqrstuvwxyz"],
:itself, :length,
wrap_body_cells_to: 1
)
> puts table
+--------------+--------------+
| itself | length |
+--------------+--------------+
| hello | 5 |
| abcdefghijkl~| 26 |
+--------------+--------------+
The character used to indicate truncation, which defaults to ~
, can be configured using the truncation_indicator
option passed to Table.new
.
By passing :word
to the wrap_preserve
option on either table initialization (for all columns), or when calling add_column
(for an individual column), whole words can be preserved when wrapping:
sentences = [
"Words are preserved.",
"Excessively long words may still be split to fit into the configured column width.",
]
table = Tabulo::Table.new(sentences, :itself, :length, column_width: 10, wrap_preserve: :word)
> puts table
+------------+------------+
| itself | length |
+------------+------------+
| Words are | 20 |
| preserved. | |
| Excessivel | 82 |
| y long | |
| words may | |
| still be | |
| split to | |
| fit into | |
| the | |
| configured | |
| column | |
| width. | |
+------------+------------+
When wrapping cell content, Tabulo will never insert hyphens itself, although it will recognize existing hyphens, m-dashes and n-dashes as word boundaries.
The wrap_preserve
option defaults to the value :rune
, meaning by default it will not respect word boundaries when wrapping (although it will always preserve whole multibyte Unicode characters).
You can “manually” wrap the content of a title, header or body cell at a particular point, simply by placing a newline character, at that point:
table = Tabulo::Table.new(1..3) do |t|
t.add_column("The number\nitself", &:itself)
t.add_column("Even?", &:even?)
t.add_column("Odd?", &:odd?)
end
> puts table
+--------------+--------------+--------------+
| The number | Even? | Odd? |
| itself | | |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
+--------------+--------------+--------------+
Tabulo will treat any of the character combinations "\n"
, "\r\n"
or "\r"
equally, as a line break, regardless of the platform it’s currently being run on. This ensures things are formatted as expected if, for example, you are examining content that was produced on another platform from the one you’re running Tabulo on.
While the callable passed to add_column
determines the underyling, calculated value in each cell of the column, there is a separate concept, of a “formatter”, that determines how that value will be visually displayed. By default, .to_s
is called on the underlying cell value to “format” it; however, you can format it differently by passing another callable to the formatter
option of add_column
:
table = Tabulo::Table.new(1..3) do |t|
t.add_column("N", &:itself)
t.add_column("Reciprocal", formatter: -> (n) { "%.2f" % n }) do |n|
1.0 / n
end
end
> puts table
+--------------+--------------+
| N | Reciprocal |
+--------------+--------------+
| 1 | 1.00 |
| 2 | 0.50 |
| 3 | 0.33 |
+--------------+--------------+
Note the numbers in the “Reciprocal” column in this example are still right-aligned, even though the callable passed to formatter
returns a String. Default cell alignment is determined by the type of the underlying cell value, not the way it is formatted. This is usually the desired result.
If you want to set the default formatter for all columns of the table to something other than #to_s
, use the formatter
option when initializing the table:
table = Tabulo::Table.new(1..3, formatter: -> (n) { "%.2f" % n }) do |t|
t.add_column("N", &:itself)
t.add_column("Reciprocal") { |n| 1.0 / n }
t.add_column("Half") { |n| n / 2.0 }
end
> puts table
+--------------+--------------+--------------+
| N | Reciprocal | Half |
+--------------+--------------+--------------+
| 1.00 | 1.00 | 0.50 |
| 2.00 | 0.50 | 1.00 |
| 3.00 | 0.33 | 1.50 |
+--------------+--------------+--------------+
Formatters set for individual columns on calling #add_column
always override the default formatter for the table.
The formatter
callback also has an alternative, 2-parameter version. If formatter
is passed a 2-parameter callable, the second parameter will be given a CellData
instance, containing additional information about the cell that may be useful in determining how to format it—see the documentation for details.
In most terminals, if you want to print text that is coloured, or has certain other styles such as underlining, you need to use ANSI escape sequences, either directly, or by means of a library such as Rainbow that uses them internally. Tabulo needs to properly account for escape sequences when performing the width calculations required to render tables. The styler
option on the add_column
method is intended to facilitate this.
For example, suppose you have a table to which you want to add a column that displays true
in green if a given number is even, or else displays false
in red. You can achieve this as follows using raw ANSI escape codes:
table.add_column(
:even?,
styler: -> (cell_value, s) { cell_value ? "\033[32m#{s}\033[0m" : "\033[31m#{s}\033[0m" }
)
Or, if you are using the rainbow gem for colouring, you could do the following:
require "rainbow"
# ...
table.add_column(
:even?,
styler: -> (cell_value, s) { cell_value ? Rainbow(s).green : Rainbow(s).red }
)
The styler
option should be passed a callable that takes either 2, 3 or 4 parameters. The first parameter represents the underlying value of the cell (in this case a boolean indicating whether the number is even). The second parameter represents the formatted string value of that cell, i.e. the cell content after any processing by the formatter. The third and fourth parameters are optional, and contain further information about the cell and its contents that may be useful in determining how to style it. See the documentation for details.
If the content of a cell is wrapped over multiple lines, then the styler
will be called once per line, so that each line of the cell will have the escape sequence applied to it separately (ensuring the styling doesn’t bleed into neighbouring cells).
If the content of a cell has been truncated, then whatever colours or other styling apply to the cell content will also be applied the truncation indicator character.
If you want to apply colours or other styling to the content of a column header, as opposed to cells in the table body, use the header_styler
option, e.g.:
table.add_column(:even?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
The header_styler
option accepts a 1-, 2- or 3-parameter callable. See the documentation for details.
To apply colours or other styling to the table title, if present, use the title_styler
option when initializing the table. This accepts a single-parameter callable:
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, title: "Numbers", title_styler: -> (s) { "\033[32m#{s}\033[0m" })
The title_styler
option accepts a 1- or 2-parameter callable. See the documentation for details.
Styling can also be applied to borders and dividing lines, using the border_styler
option when initializing the table, e.g.:
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, border_styler: -> (s) { "\033[32m#{s}\033[0m" })
By default, no styling is applied to the headers or body content of a column unless configured to do so via the header_styler
or styler
option when calling add_column
for that particular column. It is possible to apply styling by default to all columns in a table, however, as the table initializer also accepts both a header_styler
and a styler
option. For example, if you want all the header text in the table to be green, you could do:
table = Tabulo::Table.new(1..5, :itself, :even?, :odd?, header_styler: -> (s) { "\033[32m#{s}\033[0m" })
Now, all columns in the table will automatically have green header text, unless overridden by another header styler being passed to #add_column
.
By default, headers are only shown once, at the top of the table (header_frequency: :start
). If header_frequency
is passed nil
, headers are not shown at all; or, if passed an Integer
N, headers are shown at the top and then repeated every N rows. This can be handy when you’re looking at table that’s taller than your terminal.
E.g.:
table = Tabulo::Table.new(1..10, :itself, :even?, header_frequency: 5)
> puts table
+--------------+--------------+
| itself | even? |
+--------------+--------------+
| 1 | false |
| 2 | true |
| 3 | false |
| 4 | true |
| 5 | false |
+--------------+--------------+
| itself | even? |
+--------------+--------------+
| 6 | true |
| 7 | false |
| 8 | true |
| 9 | false |
| 10 | true |
+--------------+--------------+
Note that if the table has a title, it will not be repeated; only column headers are repeated.
One can achieve even finer-grained control over printing of headers within the table body by stepping through the table a row at a time (using .each
or other methods of Enumerable
) and calling the the formatted_header
method in combination with horizontal_rule
to produce headers at arbitrary points in the output.
Because it’s an Enumerable
, a Tabulo::Table
can also give you an Enumerator
, which is useful when you want to step through rows one at a time. In a Rails console, for example, you might do this:
> e = Tabulo::Table.new(User.find_each) do |t|
t.add_column(:id)
t.add_column(:email, width: 24)
end.to_enum # <-- make an Enumerator
...
> puts e.next
+--------------+--------------------------+
| id | email |
+--------------+--------------------------+
| 1 | jane@example.com |
=> nil
> puts e.next
| 2 | betty@example.net |
=> nil
Note the use of .find_each
: we can start printing the table without having to load the entire underlying collection. (This is negated if we pack the table, however, since in that case the entire collection must be traversed up front in order for column widths to be calculated.)
Each Tabulo::Table
is an Enumerable
of which each element is a Tabulo::Row
. Each Tabulo::Row
is itself an Enumerable
, of Tabulo::Cell
. The Tabulo::Cell#value
method will return the underlying value of the cell; while Tabulo::Cell#formatted_content
will return its formatted content as a string.
A Tabulo::Row
can also be converted to a Hash
for keyed access. For example:
table = Tabulo::Table.new(1..3, :itself, :even?, :odd?)
table.each do |row|
row.each { |cell| puts cell.value } # 1, false, true...2, true, false...3, false, true
puts row.to_h[:even?].value # false...true...false
end
The column label (being the first argument that was passed to add_column
, converted if necessary to a Symbol
), always forms the key for the purposes of this Hash
:
table = Tabulo::Table.new(1..3) do |t|
t.add_column("Number") { |n| n }
t.add_column(:doubled, header: "Number X 2") { |n| n * 2 }
end
table.each do |row|
cells = row.to_h
puts cells[:Number].value # 1...2...3...
puts cells[:doubled].value # 2...4...6...
end
The underlying enumerable for a table can be retrieved by calling the sources
getter:
table = Tabulo::Table.new([1, 2, 5], :itself, :even?, :odd?)
> table.sources
=> [1, 2, 5]
There is also a corresponding setter, meaning you can reuse the same table to tabulate a different data set, without having to reconfigure the columns and other options from scratch:
table.sources = [50, 60]
> table.sources
=> [50, 60]
In addition, the element of the underlying enumerable corresponding to a particular row can be accessed by calling the source
method on that row:
table.each do |row|
puts row.source # 50...60...
end
By default, Tabulo generates a table in which each row corresponds to a record, i.e. an element of the underlying enumerable, and each column to a field. However, there are times when one instead wants each row to represent a field, and each column a record. This is generally the case when there are a small number or records but a large number of fields. To produce such a table, we can first initialize an ordinary table, specifying fields as columns, and then call transpose
, which returns a new table in which the rows and columns are swapped:
> puts Tabulo::Table.new(-1..1, :even?, :odd?, :zero?, :pred, :succ, :abs).transpose
+-------+--------------+--------------+--------------+
| | -1 | 0 | 1 |
+-------+--------------+--------------+--------------+
| even? | false | true | false |
| odd? | true | false | true |
| zero? | false | true | false |
| pred | -2 | -1 | 0 |
| succ | 0 | 1 | 2 |
| abs | 1 | 0 | 1 |
+-------+--------------+--------------+--------------+
By default, a header row is added to the new table, showing the string value of the element represented in that column. This can be configured, however, along with other aspects of transpose
’s behaviour. For details, see the documentation.
You can configure the kind of border and divider characters that are used when the table is printed. This is done using the border
option passed to Table.new
. The options are as follows.
:ascii
—this is the default; the table is drawn entirely with characters in the ASCII set:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :ascii)
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
+--------------+--------------+--------------+
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :ascii, title: "Numbers")
+--------------------------------------------+
| Numbers |
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
+--------------+--------------+--------------+
:modern
—uses smoothly joined Unicode characters:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :modern)
┌──────────────┬──────────────┬──────────────┐
│ itself │ even? │ odd? │
├──────────────┼──────────────┼──────────────┤
│ 1 │ false │ true │
│ 2 │ true │ false │
│ 3 │ false │ true │
└──────────────┴──────────────┴──────────────┘
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :modern, title: "Numbers")
┌────────────────────────────────────────────┐
│ Numbers │
├──────────────┬──────────────┬──────────────┤
│ itself │ even? │ odd? │
├──────────────┼──────────────┼──────────────┤
│ 1 │ false │ true │
│ 2 │ true │ false │
│ 3 │ false │ true │
└──────────────┴──────────────┴──────────────┘
Note: The unicode characters used for the :modern
border may not render properly when viewing this documentation on some browsers or devices. This doesn’t reflect any brokenness in tabulo
itself.
:markdown
—renders a GitHub flavoured Markdown table:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :markdown)
| itself | even? | odd? |
|--------------|--------------|--------------|
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :markdown, title: "Numbers")
| Numbers |
| itself | even? | odd? |
|--------------|--------------|--------------|
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
However, note that when a table is rendered using the :markdown
border type in combination with a (non-nil
) title
, the result will be invalid Markdown. This is because Markdown engines do not generally support adding a caption (i.e. title) element to tables.
:blank
—no border or divider characters are printed:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :blank)
itself even? odd?
1 false true
2 true false
3 false true
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :blank, title: "Numbers")
Numbers
itself even? odd?
1 false true
2 true false
3 false true
:reduced_ascii
—similar to :ascii
, but without vertical lines:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_modern)
-------------- -------------- --------------
itself even? odd?
-------------- -------------- --------------
1 false true
2 true false
3 false true
-------------- -------------- --------------
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_modern, title: "Numbers")
--------------------------------------------
Numbers
-------------- -------------- --------------
itself even? odd?
-------------- -------------- --------------
1 false true
2 true false
3 false true
-------------- -------------- --------------
:reduced_modern
—similar to :modern
, but without vertical lines:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_ascii)
────────────── ────────────── ──────────────
itself even? odd?
────────────── ────────────── ──────────────
1 false true
2 true false
3 false true
────────────── ────────────── ──────────────
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :reduced_ascii, title: "Numbers")
────────────────────────────────────────────
Numbers
────────────── ────────────── ──────────────
itself even? odd?
────────────── ────────────── ──────────────
1 false true
2 true false
3 false true
────────────── ────────────── ──────────────
Note: The unicode characters used for the :reduced_modern
border may not render properly when viewing this documentation on some browsers or devices. This doesn’t reflect any brokenness in tabulo
itself.
:classic
—reproduces the default behaviour in Tabulo v1; this is like the :ascii
option, but without a bottom border:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :classic)
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, border: :classic, title: "Numbers")
+--------------------------------------------+
| Numbers |
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
Note that, by default, none of the border options includes lines drawn between rows in the body of the table. These are configured via a separate option: see below.
To add lines between rows in the table body, use the row_divider_frequency
option when initializing the table. The default value for this option is nil
, meaning there are no dividing lines between rows. But if this option passed is a positive integer N, then a dividing line is inserted before every Nth row. For example:
> puts Tabulo::Table.new(1..6, :itself, :even?, :odd?, row_divider_frequency: 2)
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
+--------------+--------------+--------------+
| 3 | false | true |
| 4 | true | false |
+--------------+--------------+--------------+
| 5 | false | true |
| 6 | true | false |
+--------------+--------------+--------------+
If you want a line before every row, pass 1
to row_divider_frequency
. For example:
> puts Tabulo::Table.new(1..3, :itself, :even?, :odd?, row_divider_frequency: 1)
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
+--------------+--------------+--------------+
| 2 | true | false |
+--------------+--------------+--------------+
| 3 | false | true |
+--------------+--------------+--------------+
In addition to these options, it is also possible to print horizontal dividers at any chosen point in the table output, by stepping through the table one row at a time and calling the horizontal_rule
method as required.
The nature of a Tabulo::Table
is that of a dynamic view onto the underlying sources
enumerable from which it was initialized (or which was subsequently assigned to its sources
attribute). That is, if the contents of the sources
enumerable change subsequent to initialization of or assignment to sources
, then the table when printed will show the sources
as they are at the time of printing, not as they were at the time of initialization or assignment. For example:
arr = [1, 2]
table = Tabulo::Table.new(arr, :itself, :even?, :odd?)
> puts table
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
+--------------+--------------+--------------+
arr << 3
> puts table
+--------------+--------------+--------------+
| itself | even? | odd? |
+--------------+--------------+--------------+
| 1 | false | true |
| 2 | true | false |
| 3 | false | true |
+--------------+--------------+--------------+
In this example, even though no direct mutations have been made to table
, the result of calling puts table
has changed, in virtue of a mutation on the underyling enumerable arr
.
A similar behaviour can be seen when sources
is an ActiveRecord query, and there are changes to the relevant database table(s) such that the result of the query changes. This is worth bearing in mind when calling pack
on a table, since if the sources
enumerable changes between pack
ing and printing, then the column widths calculated by the pack
method may no longer be “just right” given the changed sources
.
If this is not the desired behaviour, there are ways around this. For example, if dealing with an ActiveRecord relation, you can convert the query to a plain array before initializing the table:
sources = User.all.to_a
table = Tabulo::Table.new(sources, :id, :first_name, :last_name)
table.pack
puts table
Passing an Array
rather than the ActiveRecord query directly means that if there are changes to the content of the users
database table, these will not be reflected in the rendered content of the Tabulo::Table
(unless some of the Tabulo::Table
columns are based on callables that perform further database queries when called…).
Note that it is also possible simply to store the string value of a table for later use, rather than the table itself:
rendered_table = Tabulo::Table.new(1..10, :itself, :even?, :odd?).pack.to_s
There are other libraries for generating plain text tables in Ruby. Popular among these are:
DISCLAIMER: My comments regarding these other libraries are based only on my own, possibly flawed reading of the documentation for, and experimentation with, these libraries at the time of my writing this. Their APIs, features or documentation may well change between when I write this, and when you read it. Please consult the libraries’ own documentation for yourself, rather than relying on these comments.
While these libraries have their strengths, I have personally found that, for the common use case of printing a table on the basis of some underlying enumerable collection (such as an ActiveRecord query result), using these libraries feels more cumbersome than it could be.
For example, suppose we have called User.all
from the Rails console, and want to print a table showing the email, first name, last name and ID of each user, with column headings. Also, we want the ID column to be right-aligned, because it’s a number.
In terminal-table, we could achieve this as follows:
rows = User.all.map { |u| [u.email, u.first_name, u.last_name, u.id] }
headings = ["email", "first name", "last name", "id"]
table = Terminal::Table.new(headings: headings, rows: rows)
table.align_column(3, :right)
puts table
The problem here is that there is no single source of knowledge about which columns appear, and in which order. If we want to add another column to the left of “email”, we need to amend the rows array, and the headings array, and the index passed to align_column
. We bear the burden of keeping these three in sync. This is not be a big deal for small one-off tables, but for tables that have many columns, or that are constructed dynamically based on user input or other runtime factors determining the columns to be included, this can be a hassle and a source of brittleness.
tty-table has a somewhat different API to terminal-table
. It offers both a “row-based” and a “column-based” method of initializing a table. The row-based method is similar to terminal-table
’s in that it burdens the developer with syncing the column ordering across multiple code locations. The “column-based” API for tty-table
, on the other hand, seems to avoid this problem. One way of using it is like this:
users = User.all
table = TTY::Table.new [
{
"email" => users.map(&:email),
"first name" => users.map(&:first_name),
"last name" => users.map(&:last_name),
"id" => users.map(&:id),
}
]
puts table
While this doesn’t seem too bad, it does mean that the underlying collection (users
) has to be traversed multiple times, once for each column, which is inefficient, particularly if the underlying collection is large. In addition, it’s not clear how to pass separate formatting information for each column when initializing in this way. (Perhaps there is a way to do this, but if there is, it doesn’t seem to be documented.) So it seems we still have to use table.align_column(3, :right)
, which again burdens us with keeping the column index passed to align_column
in sync.
As for table_print, this is a handy gem for quickly tabulating ActiveRecord collections from the Rails console. table_print
is similar to tabulo
in that it has a column-based API, so it doesn’t suffer from the multiple-source-of-knowledge issue in regards to column orderings. However, it lacks certain other useful features, such as the ability to repeat headers every N rows, the automatic alignment of columns based on cell content (numbers right, strings left), and a quick and easy way to automatically resize columns to accommodate cell content without overflowing the terminal. Also, as of the time of writing, table_print
’s last significant commit (ignoring a deprecation warning fix in April 2018) was in March 2016.
Finally, it is worth mentioning the hirb library. This is similar to table_print
, in that it’s well suited to quickly displaying ActiveRecord collections from the Rails console. However, like table_print
, there are certain useful features it’s lacking; and using it outside the console environment seems cumbersome. Moreover, it seems no longer to be maintained. At the time of writing, its last commit was in March 2015.
Issues and pull requests are welcome on GitHub at https://github.com/matt-harvey/tabulo.
To start working on Tabulo, git clone
and cd
into your fork of the repo, then run bin/setup
to install dependencies.
bin/console
will give you an interactive prompt that will allow you to experiment; and bundle exec rake spec
will run the test suite. For a list of other Rake tasks that are available in the development environment, run bundle exec rake -T
.
The gem is available as open source under the terms of the MIT License.
Author: matt-harvey
Source code: https://github.com/matt-harvey/tabulo
License: MIT license
1656599580
plugin for serverless to make buckets empty before remove
Usage
Run next command.
$ npm install serverless-s3-remover
Add to your serverless.yml
plugins:
- serverless-s3-remover
custom:
remover:
buckets:
- my-bucket-1
- my-bucket-2
You can specify any number of bucket
s that you want.
Now you can make all buckets empty by running:
$ sls s3remove
When removing
When removing serverless stack, this plugin automatically make buckets empty before removing stack.
$ sls remove
Using Prompt
You can use prompt before deleting bucket.
custom:
remover:
prompt: true # default value is `false`
buckets:
- remover-bucket-a
- remover-bucket-b
Populating the configuration object before using it
custom:
boolean:
true: true
false: false
remover:
prompt: ${self:custom.boolean.${opt:s3-remover-prompt, 'true'}}
I can use the command line argument --s3-remover-prompt false
to disable the prompt feature.
Author: Sinofseven
Source Code: https://github.com/sinofseven/serverless-s3-remover
License: MIT license
1656127500
query-selector-shadow-dom
querySelector that can pierce Shadow DOM roots without knowing the path through nested shadow roots. Useful for automated testing of Web Components e.g. with Selenium, Puppeteer.
// available as an ES6 module for importing in Browser environments
import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';
You can see that
.dropdown-item:not([hidden])
(Open downloads folder) is several layers deep in shadow roots, most tools will make you do something like
document.querySelector("body > downloads-manager").shadowRoot.querySelector("#toolbar").shadowRoot.querySelector(".dropdown-item:not([hidden])")
EW!
with query-selector-shadow-dom:
import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';
querySelectorDeep(".dropdown-item:not([hidden])");
querySelectorAll
from the browser, will return an Array
of elements matching the queryquerySelector
from the browser, will return the first
matching element of the query.Both of the methods above accept a 2nd parameter, see section Provide alternative node
. This will change the starting element to search from i.e. it will find ancestors of that node that match the query.
querySelectorAll
functionality. You can read more about this here: https://github.com/Georgegriff/query-selector-shadow-dom/issues/54This plugin implements a custom selector strategy: https://webdriver.io/docs/selectors.html#custom-selector-strategies
// make sure you have selenium standalone running
const { remote } = require('webdriverio');
const { locatorStrategy } = require('query-selector-shadow-dom/plugins/webdriverio');
(async () => {
const browser = await remote({
logLevel: 'error',
path: '/wd/hub',
capabilities: {
browserName: 'chrome'
}
})
// The magic - registry custom strategy
browser.addLocatorStrategy('shadow', locatorStrategy);
// now you have a `shadow` custom locator.
// All elements on the page
await browser.waitUntil(() => browser.custom$("shadow", ".btn-in-shadow-dom"));
const elements = await browser.$$("*");
const elementsShadow = await browser.custom$$("shadow", "*");
console.log("All Elements on Page Excluding Shadow Dom", elements.length);
console.log("All Elements on Page Including Shadow Dom", elementsShadow.length);
await browser.url('http://127.0.0.1:5500/test/')
// find input element in shadow dom
const input = await browser.custom$('shadow', '#type-to-input');
// type to input ! Does not work in firefox, see above.
await input.setValue('Typed text to input');
// Firefox workaround
// await browser.execute((input, val) => input.value = val, input, 'Typed text to input')
await browser.deleteSession()
})().catch((e) => console.error(e))
shadow$
shadow$
only goes one level deep in a shadow root.
Take this example. You can see that
.dropdown-item:not([hidden])
(Open downloads folder) is several layers deep in shadow roots, but this library will find it, shadow$
would not. You would have to construct a path via css or javascript all the way through to find the right element.
const { remote } = require('webdriverio')
const { locatorStrategy } = require('query-selector-shadow-dom/plugins/webdriverio');
(async () => {
const browser = await remote({capabilities: {browserName: 'chrome'}})
browser.addLocatorStrategy('shadow', locatorStrategy);
await browser.url('chrome://downloads')
const moreActions = await browser.custom$('shadow', '#moreActions');
await moreActions.click();
const span = await browser.custom$('shadow', '.dropdown-item:not([hidden])');
const text = await span.getText()
// prints `Open downloads folder`
console.log(text);
await browser.deleteSession()
})().catch((e) => console.error(e))
https://webdriver.io/blog/2019/02/22/shadow-dom-support.html#browser-support
From the above, firefox setValue
does NOT currently work. . A workaround for now is to use a custom command (or method on your component object) that sets the input field's value via browser.execute(function).
Safari pretty much doesn't work, not really a surprise.
There are some webdriver examples available in the examples folder of this repository. WebdriverIO examples
Update: As of 5.4.0 Puppeteer now has a built in shadow Dom selector, this module might not be required for Puppeteer anymore. They don't have any documentation: https://github.com/puppeteer/puppeteer/pull/6509
There are some puppeteer examples available in the examples folder of this repository.
Update: as of Playwright v0.14.0 their CSS and text selectors work with shadow Dom out of the box, you don't need this library anymore for Playwright.
Playwright works really nicely with this package.
This module exposes a playwright selectorEngine
: https://github.com/microsoft/playwright/blob/main/docs/api.md#selectorsregisterenginefunction-args
const { selectorEngine } = require("query-selector-shadow-dom/plugins/playwright");
const playwright = require('playwright');
await selectors.register('shadow', createTagNameEngine);
...
await page.goto('chrome://downloads');
// shadow= allows a css query selector that automatically pierces shadow roots.
await page.waitForSelector('shadow=#no-downloads span', {timeout: 3000})
For a full example see: https://github.com/Georgegriff/query-selector-shadow-dom/blob/main/examples/playwright
This project provides a Protractor plugin, which can be enabled in your protractor.conf.js
file:
exports.config = {
plugins: [{
package: 'query-selector-shadow-dom/plugins/protractor'
}],
// ... other Protractor-specific config
};
The plugin registers a new locator - by.shadowDomCss(selector /* string */)
, which can be used in regular Protractor tests:
element(by.shadowDomCss('#item-in-shadow-dom'))
The locator also works with Serenity/JS tests that use Protractor under the hood:
import 'query-selector-shadow-dom/plugins/protractor';
import { Target } from '@serenity-js/protractor'
import { by } from 'protractor';
const ElementOfInterest = Target.the('element of interest')
.located(by.shadowDomCss('#item-in-shadow-dom'))
See the end-to-end tests for more examples.
// query from another node
querySelectorShadowDom.querySelectorAllDeep('child', document.querySelector('#startNode'));
// query an iframe
querySelectorShadowDom.querySelectorAllDeep('child', iframe.contentDocument);
This library does not allow you to query across iframe boundaries, you will need to get a reference to the iframe you want to interact with.
If your iframe is inside of a shadow root you could cuse querySelectorDeep
to find the iframe, then pass the contentDocument
into the 2nd argument of querySelectorDeep
or querySelectorAllDeep
.
In the below examples the components being searched for are nested within web components shadowRoots
.
// Download and Paste the lib code in dist into chrome://downloads console to try it out :)
console.log(querySelectorShadowDom.querySelectorAllDeep('downloads-item:nth-child(4) #remove'));
console.log(querySelectorShadowDom.querySelectorAllDeep('#downloads-list .is-active a[href^="https://"]'));
console.log(querySelectorShadowDom.querySelectorDeep('#downloads-list div#title-area + a'));
Shady DOM
If using the polyfills and shady DOM, this library will still work.
window.querySelectorShadowDom
available for easy include into a test frameworknpm install
npm test
npm run watch
npm run build
Author: Georgegriff
Source Code: https://github.com/Georgegriff/query-selector-shadow-dom
License: MIT license
1653737280
WHAT IS METEORIS?
Meteoris is a Realtime MVC + Modular Javascript Framework based on Meteor 1.2.0. This is the latest version of Meteoris out there. In this version, Meteoris still doesn't have a code generator. If you prefer using the old version of meteoris, please use this version https://github.com/radiegtya/meteoris-v1.
QUICKSTART
#install meteor
curl https://install.meteor.com/ | sh
#create new meteor project
meteor create my-meteoris-app
#the main module
meteor add meteoris:core
#another important module
meteor add meteoris:theme-admin
meteor add meteoris:user
meteor add meteoris:role
#remove meteor unused package
meteor remove insecure
meteor remove autopublish
We can easily hook navbar and sidebar to customize our own sidebar and navbar although we are using meteoris:admin-theme template. The content of each template is using navbar and sidebar Admin LTE style. Make sure you are using correct theme name "meteoris_themeAdmin_hookNavbar" for sidebar, and "meteoris_themeAdmin_hookSidebar" for navbar.
<template name="meteoris_themeAdmin_hookNavbar">
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
{{#if currentUser}}
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="/images/user.png" class="user-image" alt="User Image">
<span class="hidden-xs">{{currentUser.profile.name}}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="/images/user.png" class="img-circle" alt="User Image">
<p>
{{currentUser.profile.name}}
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-left">
<a href="#" class="btn btn-default btn-flat">Profile</a>
</div>
<div class="pull-right">
<a href="#" id="btnLogout" class="btn btn-default btn-flat">Logout</a>
</div>
</li>
</ul>
</li>
{{else}}
<li>
<a href="/meteoris/user/login">Login</a>
</li>
{{/if}}
</ul>
</div>
</template>
"at root/client/hook/navbar.html"
var ctrl = new Meteoris.UserController();
Template.meteoris_themeAdmin_hookNavbar.events = {
'click #btnLogout': function(){
ctrl.logout();
}
};
"at root/client/hook/navbar.js"
<template name="meteoris_themeAdmin_hookSidebar">
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
{{#if currentUser}}
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img src="/images/user.png" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>{{currentUser.profile.name}}</p>
<a href="#"><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
{{/if}}
<!--search form-->
<form action="#" method="get" class="sidebar-form">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i></button>
</span>
</div>
</form>
<!--/.search form-->
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATION</li>
<li><a href="/"><i class="fa fa-home"></i> Home</a></li>
<!--Uncomment this if you want to hide this menu using the power of meteoris:role-->
<!--{{#if meteoris_roleUserIsInGroup "admin"}}-->
<li class="header">ADMIN AREA</li>
<li class="treeview">
<a href="#">
<i class="fa fa-gears"></i>
<i class="fa fa-angle-left pull-right"></i>
<span>Setting</span>
</a>
<ul class="treeview-menu">
<li><a href="/meteoris/theme-admin/setting"><i class="fa fa-laptop"></i> Theme Admin Setting</a></li>
<li><a href="/meteoris/user"><i class="fa fa-users"></i> Users Management</a></li>
<li><a href="/meteoris/user/settings"><i class="fa fa-user"></i> User Settings</a></li>
<li><a href="/meteoris/role"><i class="fa fa-flag-o"></i> Role Management</a></li>
</ul>
</li>
<!--{{/if}}-->
</ul>
</section>
<!-- /.sidebar -->
</aside>
</template>
"at root/client/hook/sidebar.html"
Meteoris use simple MVC or MVVM + Modular paradigm to structuring app. Please use this standard structure to achieve best result when using meteoris.
3.1. Folder Structure
root/
meteorusernameorgroup:module/
client/ # Client folder
views/ # Views for html/js files
lib/ # Both Access folder
collections/ # Collections folder
controllers/ # Controllers Folder
router.js # Router js file
server/ # Server folder
3.2. Router Meteoris using Kadira Flow Router to linking between pages. Because we are using modular technique, assume that we are using my meteor account called "radiegtya" and using module name called "site". So we should make folder under root folder called "radiegtya:site". To use modular router, simply follow this step:
/**
create group routes, so every router has prefix radiegtya
*/
var groupRoutes = FlowRouter.group({
prefix: '/radiegtya',
name: 'radiegtya',
});
/**
create the main router called site, and use BlazeLayout render "meteoris_themeAdminMain" from metoris:theme-admin package, and accessing template called "radiegtya_siteIndex"
*/
groupRoutes.route('/site', {
action: function() {
BlazeLayout.render('meteoris_themeAdminMain', {content: "radiegtya_siteIndex"});
},
});
localhost:3000/radiegtya/site
3.3. Creating Controller for Site Controller actually just a method to be called inside your template js, and this will make your code more modular and readable.
/*
create a namespace called Radiegtya.SiteController
*/
Namespace('Radiegtya.SiteController');
/**
Create controller which extends Meteoris Controller
*/
Radiegtya.SiteController = Meteoris.Controller.extend({
constructor : function() {
// set default counter at constructor
Session.setDefault('counter', 0);
},
/* passing data to index helpers template */
index: function() {
//return some value, to be passed on index.js
return {
counter: this.getCounter(),
myName: "Ega Radiegtya",
myHobby: "Drinking Coffee"
};
},
getCounter: function(){
return Session.get("counter");
},
setCounter: function(counter){
Session.set('counter', this.getCounter() + 1);
}
});
3.4. Creating Views Page for Site
<template name="radiegtya_siteIndex">
<div class="content">
<div class="box">
<div class="box-body">
<button>Click Me</button>
<p>
You've pressed the button {{counter}} times.
</p>
</div>
</div>
</div>
</template>
because we are installing meteoris:theme-admin, you can use adminLTE styling and html inside your views html file. We are using meteor example to make You more familiar with the code.
var ctrl = new Radiegtya.SiteController();
/**
In the template helper section we are using index() method to get object return value from controller. It's because index was the action and also the file name suffix. This structure will make You easier to get jobs done, when your team getting bigger and the code getting bigger.
*/
Template.radiegtya_siteIndex.helpers({
myName: function () {
return ctrl.index().myName;
},
myHobby: function () {
return ctrl.index().myHobby;
},
counter: function () {
return ctrl.index().counter;
}
});
/**
in the template events, we don't need to use index() method to call any action. Because it just simply method calling through controller.
*/
Template.radiegtya_siteIndex.events({
'click button': function () {
//increment the counter when button is clicked
ctrl.setCounter();
}
});
Now finally access this route to check your apps.
localhost:3000/radiegtya/site
Awesome! We are successfully creating our simple app in MVC and Modular way with very easy setup. Ofc with amazing template which is reusable. You can also use this code to create a meteor package easily.
3.5. Creating Collection You can use aldeed:collection2, aldeed:simple-schema, and dburles:collection-helpers in our Meteoris collection. Future explanation will be comming soon. ======== COMING SOON =========
3.6. Creating Server You can use reywood:publish-composite here. Future explanation coming soon. ======== COMING SOON =========
3.7. Using Meteoris Plugin For now You can refer to the below this Quick Start article.
====================================================================================
#1. INSTALLATION
meteor add meteoris:core
it will also installing it's dependencies, which are:
Meteoris Package to show Flash/Toast Error/Success in your bottom right screen.
how to use in your js file:
//set flash message example
Meteoris.Flash.set('success', 'Data Successfully added');
Meteoris.Flash.set('danger', 'Data Failed to be added');
how to use in your html file:
<!-- Simply place this code on your html view anywhere -->
{{> meteoris_flash}}
Meteoris package to validate form easily. This Meteoris.FormValidation extension depends on collection2 and simpleschema:
AccountType = new Mongo.Collection("accountType");
var schemas = new SimpleSchema({
accountClassId: {
type: String,
label: "Kelas Akun",
},
});
AccountType.attachSchema(schemas);
/* show error message on view */
error: function(field) {
return Meteoris.FormValidation.error(YourCollectionName, field);
},
<div class="form-group {{#if error 'name'}}has-error{{/if}}">
<label for="name" class="control-label">Kelas Akun *</label>
<input type="text" id="name" value="{{name}}" placeholder="Kelas Akun" class="form-control" autofocus="true">
<span class="help-block">{{error "name"}}</span>
</div>
{{meteoris_formatter 'the function name' firstParam secondParam}}
- date (date)
- dateTime (date)
- elapsedTime (date)
- currency (value)
- combodate (date)
- combodateEmpty (date)
{{meteoris_formatter 'date' getCurrDate}}
Template.someTheme.helpers({
getCurrDate: function(){
return new Date();
}
});
<th id="btnSortName" class="{{meteoris_gridViewSortClass 'name'}}">Name</th>
Template.accountTypeIndex.events = {
/* sorting by parameter */
'click #btnSortName': function(e) {
Meteoris.GridView.sort('name');
},
}
#2. Install Meteoris Theme Admin (Not Required But It will better to be installed)
This current version of meteoris only have 1 theme which is called meteoris:theme-admin. This theme using popular Admin LTE for it's UI, Click here for more info about Admin LTE.
To install this theme, simply add meteor package:
meteor add meteoris:theme-admin
a. In your app client folder, create hook folder and inside it, add this template code. b. You can change everything such as loggedin user, global search, and menus inside the template as you wish
<!-- In your app client folder, create hook folder and inside it, add this template code. -->
<!-- You can change everything such as loggedin user, global search, and menus inside the template as you wish -->
<template name="meteoris_themeAdmin_hookSidebar">
<!-- Left side column. contains the logo and sidebar -->
<aside class="main-sidebar">
<!-- sidebar: style can be found in sidebar.less -->
<section class="sidebar">
{{#if currentUser}}
<!-- Sidebar user panel -->
<div class="user-panel">
<div class="pull-left image">
<img src="/images/user.png" class="img-circle" alt="User Image">
</div>
<div class="pull-left info">
<p>{{currentUser.profile.name}}</p>
<a href="#"><i class="fa fa-circle text-success"></i> Online</a>
</div>
</div>
{{/if}}
<!--search form-->
<form action="#" method="get" class="sidebar-form">
<div class="input-group">
<input type="text" name="q" class="form-control" placeholder="Search...">
<span class="input-group-btn">
<button type="submit" name="search" id="search-btn" class="btn btn-flat"><i class="fa fa-search"></i></button>
</span>
</div>
</form>
<!--/.search form-->
<!-- sidebar menu: : style can be found in sidebar.less -->
<ul class="sidebar-menu">
<li class="header">MAIN NAVIGATION</li>
<li><a href="/"><i class="fa fa-home"></i> Home</a></li>
<!--Uncomment this if you want to hide this menu using the power of meteoris:role-->
<!--{{#if meteoris_roleUserIsInGroup "admin"}}-->
<li class="header">ADMIN AREA</li>
<li class="treeview">
<a href="#">
<i class="fa fa-gears"></i>
<i class="fa fa-angle-left pull-right"></i>
<span>Setting</span>
</a>
<ul class="treeview-menu">
<li><a href="/meteoris/theme-admin/setting"><i class="fa fa-laptop"></i> Theme Admin Setting</a></li>
<li><a href="/meteoris/user"><i class="fa fa-users"></i> Users Management</a></li>
<li><a href="/meteoris/user/settings"><i class="fa fa-user"></i> User Settings</a></li>
<li><a href="/meteoris/role"><i class="fa fa-flag-o"></i> Role Management</a></li>
</ul>
</li>
<!--{{/if}}-->
</ul>
</section>
<!-- /.sidebar -->
</aside>
</template>
<template name="meteoris_themeAdmin_hookNavbar">
<div class="navbar-custom-menu">
<ul class="nav navbar-nav">
{{#if currentUser}}
<!-- User Account: style can be found in dropdown.less -->
<li class="dropdown user user-menu">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<img src="/images/user.png" class="user-image" alt="User Image">
<span class="hidden-xs">{{currentUser.profile.name}}</span>
</a>
<ul class="dropdown-menu">
<!-- User image -->
<li class="user-header">
<img src="/images/user.png" class="img-circle" alt="User Image">
<p>
{{currentUser.profile.name}}
</p>
</li>
<!-- Menu Footer-->
<li class="user-footer">
<div class="pull-left">
<a href="#" class="btn btn-default btn-flat">Profile</a>
</div>
<div class="pull-right">
<a href="#" id="btnLogout" class="btn btn-default btn-flat">Logout</a>
</div>
</li>
</ul>
</li>
{{else}}
<li>
<a href="/meteoris/user/login">Login</a>
</li>
{{/if}}
</ul>
</div>
</template>
c. add js file according to the template to get some event, and helper
var ctrl = new Meteoris.UserController();
Template.meteoris_themeAdmin_hookNavbar.events = {
'click #btnLogout': function(){
ctrl.logout();
}
};
#3. Meteoris User (Not Required but it will be better to be installed)
'/meteoris/user', #to manage user as admin
'/meteoris/user/login', #to logged in the user and '/meteoris/user/register', #to registering user
'/meteoris/user/profile' #to updating current logged in user profile
'/meteoris/user/settings', #to setting user oauth account
#4. Install Meteoris Role (Not Required but it will be better to be installed)
//you can use this code on collection-allow or on server
Meteoris.Role.userIsInRole(collection, action);
//example on collection:
MyCollection.allow({
insert: function(userId, doc) {
return Meteoris.Role.userIsInRole("my-collection", Meteoris.Role.POST);
},
update: function(userId, doc) {
return Meteoris.Role.userIsInRole("my-collection", Meteoris.Role.PUT);
},
remove: function(userId, doc) {
return Meteoris.Role.userIsInRole("my-collection", Meteoris.Role.DELETE);
},
});
//example on router
var roleRoutes = FlowRouter.group({
prefix: '/meteoris/role',
name: 'meteoris_role',
triggersEnter: [authenticating]
});
/* router level validation, only allow user with group "admin" to access this page */
function authenticating() {
if (!Meteoris.Role.userIsInRole("my-collection", Meteoris.Role.GET_ALL)){
Meteoris.Flash.set("danger", "403 Unauthenticated");
FlowRouter.go("/");
}
}
//you can use this code on collection-allow or on server
Meteoris.Role.userIsInGroup(groupName);
//example on collection:
MyCollection.allow({
insert: function(userId, doc) {
return Meteoris.Role.userIsInGroup("admin");
},
update: function(userId, doc) {
return Meteoris.Role.userIsInGroup("admin");
},
remove: function(userId, doc) {
return Meteoris.Role.userIsInGroup("admin");
},
});
var roleRoutes = FlowRouter.group({
prefix: '/meteoris/role',
name: 'meteoris_role',
triggersEnter: [authenticating]
});
/* router level validation, only allow user with group "admin" to access this page */
function authenticating() {
if (!Meteoris.Role.userIsInGroup("user")){
Meteoris.Flash.set("danger", "403 Unauthenticated");
FlowRouter.go("/");
}
}
//you can use this code on client template html
{{#if meteoris_roleUserIsInRole "collectionName" "actionName"}}
<!-- Your logic here -->
{{/if}}
//example on client template html:
{{#if meteoris_roleUserIsInRole "my-collection" "GET_ALL"}}
<li><a href="/my-collection"><i class="fa fa-flag-o"></i> My Collection Menu</a></li>
{{/if}}
//you can use this code on client template html
{{#if meteoris_roleUserIsInGroup "groupName"}}
<!-- Your logic here -->
{{/if}}
//example on client template html:
{{#if meteoris_roleUserIsInGroup "admin"}}
<li><a href="/my-collection"><i class="fa fa-flag-o"></i> My Collection Menu</a></li>
{{/if}}
Author: Meteoris
Source Code: https://github.com/meteoris/meteoris
License:
1652493960
Linux用のあまり知られていないユーティリティとコマンドの別のリストが戻ってきました。これはあなたが日常の仕事をより速く進めるのに役立ちます。
私たちは、ブラウザのグラフィカルインターフェイスを介してGoogle検索を使用することに慣れています。ただし、これは、グラフィカルインターフェイスが使用できない環境では問題になります。googlerユーティリティを使用すると、コマンドラインからGoogle検索を実行できるようになります。
Googlerは、Linuxターミナル内でGoogle(Web&News&Search)にアクセスするためのフル機能のPythonベースのコマンドラインツールです。
URL:https ://github.com/jarun/googler
インストール方法:
# ubuntu$ sudo add-apt-repository ppa:twodopeshaggy/jarun
$ sudo apt-get update
$ sudo apt install googlerOR$ sudo snap install googler
使用例:
$ googler ford usa
グーグル実行中
コマンド「ls」を入力する代わりに、「sl」と入力した頻度はどれくらいですか。
面倒な「コマンドが見つかりません」というメッセージを表示する代わりに、この小さなユーティリティは素敵な画像を表示します。少なくともこのように、あなたは少し笑うか、次回はもっと注意するでしょう。
インストール方法:
$sudo apt install sl
使用例:
$ sl
slランニング
確かに、注意を引くのにも役立ちますが、最終的には面倒になる可能性があります。
hstrは私が毎日使用している優れたツールで、シェル履歴の提案ボックスに入力されたコマンドの履歴を簡単に表示、参照、検索できます。bashとzshで使用できます。
URL:http ://dvorka.github.io/hstr/
URL:https ://github.com/dvorka/hstr
インストール方法:
#Ubuntu
$ sudo add-apt-repository ppa:ultradvorka/ppa && sudo apt-get update && sudo apt-get install hstr && hstr --show-configuration >> ~/.bashrc && . ~/.bashrc
Linuxにデフォルトで付属しているこのコマンドは、事前定義された応答を端末に渡すために使用できるシステム管理者にとって便利です。
使用例:
$ yes hello-world
はい実行中
与えられたすべての文字列を逆にします。これは時々役に立ちます。
使用例:
$ rev
$ hello world!
revrunning
良いことは、何もインストールする必要がないことです。
このLinuxユーティリティは、コマンドラインからウィキペディアの記事を検索するために使用されます。
使用する必要があるのは、情報を取得する検索語を使用してコマンドを実行することだけです。
URL:https ://www.tecmint.com/wikipedia-commandline-tool/
インストール方法:
# Debian/Ubuntu$ sudo apt install nodejs $ sudo npm install wikit -g
使用例:
Wikit matrix
ウィキの実行
このツールは、「df」コマンドの優れた代替手段です。Linuxファイルシステムで使用されているディスク容量と使用可能なディスク容量がdfコマンドと同じように表示されますが、色が異なり、結果をカスタマイズすることもできます。
Pythonで書かれていることに注意してください。
URL:https ://pypi.org/project/pydf/
#Ubuntu# only if you do not have phyton installed:
$ sudo apt install python3-pip
$ pip install pydf
実行中のpydf
Trash-cliは、ファイルをゴミ箱に移動し、元の絶対パス、削除日、およびアクセス許可を記録するコマンドラインインターフェイスです。
URL:https://github.com/andreafrancia/trash-cli
インストール方法:
# Debian/Ubuntu systems
$ sudo easy_install trash-cli
使用例:
# We create an example file named file1
$ echo "hello world!" > file1
$ trash-put file1
#
$ trash-list
使用可能なコマンド:
$ trash-put #trash files and directories.
$ trash-list #list trashed files.
$ trash-restore #restore a trashed file.
$ trash-rm #remove individual files from the trashcan.
$ trash-empty #empty the trashcan(s).
trash-cliの実行
eSpeak NGは、英語やその他の言語でテキストを音声に変換するために使用できる無料のオープンソースソフトウェアです。これは、JonathanDuddingtonによって作成されたeSpeakエンジンに基づいています。
URL: https://github.com/espeak-ng/espeak-ng
インストール方法:
# Ubuntu
$ apt-get install espeak
それでは、「こんにちはケスク!」というセリフを話しましょう。そしてそれをhello.mp4オーディオファイルに録音します:
$ espeak "Hi Kesk!" -w hello.mp4 -g 60 -p 70 -s 100 -v en-us
録音したいテキストファイルを指定することもできます。
$ espeak -f example.txt -w example.mp4 -g 60 -p 70 -s 100 -v en-us
今、あなたがしなければならないのはあなたの好きなアプリケーションでそれをプレイすることだけです、そしてそれはそれです。
このコマンドラインシステム監視ユーティリティを使用すると、CPU、負荷平均、メモリ、ネットワークインターフェイス、ディスクI / O、プロセス、およびファイルシステムスペースの使用率を監視できます。
GlancesユーティリティはPythonで記述されており、psutilライブラリを使用してシステム情報を取得し、わかりやすい形式で表示します。
また、構成ファイルを使用してさまざまな警告しきい値を設定することもできます。
すべてを管理するのに非常に便利です。
URL:https ://nicolargo.github.io/glances/
インストール方法:
$ sudo apt install glances
使用例:
$ glances
走っている一瞥
gtopユーティリティは、システムとそのプロセスに関するさまざまな情報を表示するLinuxシステムモニターです。
このツールは使いやすく、リソースをほとんど消費しないため、リソースを無駄にすることなく実行できます。さらに、オペレーティングシステムのコマンドを使用するため、非常に印象的で正確な方法で情報を表示します。
これは、CanGüneyAksakalliによってJavaScriptで開発されたオープンソースツールであり、インストールして実行するには、コンピューターにnodejsをインストールする必要があります。
URL:git clone https://github.com/aksakalli/gtop.git
インストール方法:
$ sudo npm install gtop -g
使用例:
$ gtop
gtopランニング
以前のユーティリティと同様に、この小さなプログラムはデフォルトであり、ある時点で役立つ場合があります。
ファクターランニング
Linuxのコマンドラインユーティリティや、特に役立つコマンドやあまり知られていないコマンドをご存知の場合は、それについて教えていただければ幸いです。
ありがとうございました!
ソース:https ://medium.com/codex/12-awesome-linux-commands-utilities-49ab56588a84
1652493840
Vuelvo con otra lista de utilidades y comandos poco conocidos para Linux que te ayudarán a ir más rápido con tu trabajo diario.
Estamos acostumbrados a utilizar la búsqueda de Google a través de una interfaz gráfica en el navegador. Sin embargo, esto es un problema en entornos donde la interfaz gráfica no está disponible. Con la utilidad googler podremos realizar búsquedas en Google desde la línea de comandos.
Googler es una herramienta de línea de comandos basada en Python con todas las funciones para acceder a Google (Web & News & Search) dentro de la terminal de Linux.
URL: https://github.com/jarun/googler
Cómo instalarlo:
# ubuntu$ sudo add-apt-repository ppa:twodopeshaggy/jarun
$ sudo apt-get update
$ sudo apt install googlerOR$ sudo snap install googler
Ejemplo de uso:
$ googler ford usa
google corriendo
En lugar de escribir el comando 'ls', ¿con qué frecuencia ha escrito 'sl'?
En lugar de mostrarte el tedioso mensaje de "comando no encontrado", esta pequeña utilidad te mostrará una bonita imagen; al menos así te reirás un poco o tendrás más cuidado la próxima vez.
Cómo instalarlo:
$sudo apt install sl
Ejemplo de uso:
$ sl
sl corriendo
Es cierto que al final puede resultar cansino, aunque también servirá para que prestes atención.
hstr es una gran herramienta que uso a diario que le permite ver, explorar y buscar fácilmente el historial de comandos ingresados en el cuadro de sugerencias del historial de shell. Está disponible para bash y zsh.
URL: http://dvorka.github.io/hstr/
URL: https://github.com/dvorka/hstr
Cómo instalarlo:
#Ubuntu
$ sudo add-apt-repository ppa:ultradvorka/ppa && sudo apt-get update && sudo apt-get install hstr && hstr --show-configuration >> ~/.bashrc && . ~/.bashrc
Este comando que viene por defecto en Linux es útil para los administradores de sistemas que pueden usarlo para pasar una respuesta predefinida a la terminal.
Ejemplo de uso:
$ yes hello-world
si corriendo
Invierte cada cadena que se le da, lo que a veces es útil.
Ejemplo de uso:
$ rev
$ hello world!
revoluciones corriendo
Lo bueno es que no tienes que instalar nada.
Esta utilidad de Linux se utiliza para buscar artículos de Wikipedia desde la línea de comandos.
Lo único que tienes que hacer para usar es ejecutar el comando con el término de búsqueda que deseas obtener la información.
URL: https://www.tecmint.com/wikipedia-commandline-tool/
Cómo instalarlo:
# Debian/Ubuntu$ sudo apt install nodejs $ sudo npm install wikit -g
Ejemplo de uso:
Wikit matrix
corriendo
Esta herramienta es una excelente alternativa al comando “df”. Muestra la cantidad de espacio en disco utilizado y disponible en un sistema de archivos de Linux, al igual que el comando df, pero con diferentes colores, y también le permite personalizar los resultados.
Tenga en cuenta que está escrito en Python.
URL: https://pypi.org/project/pydf/
#Ubuntu# only if you do not have phyton installed:
$ sudo apt install python3-pip
$ pip install pydf
pdf ejecutando
Trash-cli es una interfaz de línea de comandos que desecha archivos y registra la ruta absoluta original, la fecha de eliminación y los permisos.
URL: https://github.com/andreafrancia/trash-cli
Cómo instalarlo:
# Debian/Ubuntu systems
$ sudo easy_install trash-cli
Ejemplo de uso:
# We create an example file named file1
$ echo "hello world!" > file1
$ trash-put file1
#
$ trash-list
Comandos disponibles:
$ trash-put #trash files and directories.
$ trash-list #list trashed files.
$ trash-restore #restore a trashed file.
$ trash-rm #remove individual files from the trashcan.
$ trash-empty #empty the trashcan(s).
basura-cli corriendo
eSpeak NG es un software gratuito y de código abierto que se puede utilizar para convertir texto a voz en inglés y otros idiomas. Se basa en el motor eSpeak creado por Jonathan Duddington.
URL: https://github.com/espeak-ng/espeak-ng
Cómo instalarlo:
# Ubuntu
$ apt-get install espeak
Ahora, digamos la línea "¡Hola, Kesk!" y grábelo en el archivo de audio hello.mp4:
$ espeak "Hi Kesk!" -w hello.mp4 -g 60 -p 70 -s 100 -v en-us
También puede especificar el archivo de texto que desea grabar.
$ espeak -f example.txt -w example.mp4 -g 60 -p 70 -s 100 -v en-us
Ahora todo lo que tienes que hacer es jugarlo con tu aplicación favorita, y listo.
Esta utilidad de monitoreo del sistema de línea de comandos le permite monitorear la CPU, el promedio de carga, la memoria, las interfaces de red, la E/S del disco, los procesos y la utilización del espacio del sistema de archivos.
La utilidad Glances está escrita en Python y utiliza la biblioteca psutil para obtener información del sistema y mostrarla en un formato amigable.
También nos permite establecer diferentes umbrales de alerta mediante un archivo de configuración.
Muy útil para tenerlo todo bajo control.
URL: https://nicolargo.github.io/glances/
Cómo instalarlo:
$ sudo apt install glances
Ejemplo de uso:
$ glances
miradas corriendo
La utilidad gtop es un monitor del sistema Linux que muestra diversa información sobre el sistema y sus procesos.
La herramienta es fácil de usar y consume muy pocos recursos, por lo que podemos tenerla funcionando sin gastar recursos. Además, muestra la información de una forma bastante llamativa y precisa ya que utiliza comandos del sistema operativo.
Es una herramienta de código abierto desarrollada en JavaScript por Can Güney Aksakalli, y necesitará tener instalado nodejs en su computadora para instalarlo y ejecutarlo.
URL: clon de git https://github.com/aksakalli/gtop.git
Cómo instalarlo:
$ sudo npm install gtop -g
Ejemplo de uso:
$ gtop
corriendo
Al igual que la utilidad anterior, este pequeño programa viene por defecto y puede ser de ayuda en algún momento.
funcionamiento del factor
Si conoces alguna utilidad de línea de comandos de Linux o algún comando que te ayude especialmente y no sea muy conocido, te agradecería que nos lo comentaras.
¡Gracias!
Fuente: https://medium.com/codex/12-awesome-linux-commands-utilities-49ab56588a84
1618147009
Hello Guys,
In this small tutorial I will give example of remove or hide columns while export data from datatable.
And I will give you example of yajra datatable export button.
When we are using jquery datatable for displaying data in datatable and export datatabel information into PDF, Excel or CSV we might be exclude or hide some columns. So, here I will give you demo how to remove or hide columns when exporting data when using jquery.
Thanks for the reading…!!
#export #data #datatable #yajra datatable #remove #export button
1616561940
In this tutorial I will explain you how to remove extra space using jquery, many times we have requirments to remove white space from textbox, or many time users added unnecessary space in field which occupied more space in database.
#javascript #jquery #remove #space #textbox #example
1614855826
Copy messages are those messages that show up consistently in Outlook PST records. A great deal of disarray is made among clients choosing; how to manage the email comprising of copies or where to discover and eliminate copy messages in Outlook. You can see the underneath screen capture of copy email messages in Outlook.
Explanations for Duplicates Emails in Outlook
Numerous things can make Outlook things copy, yet the most widely recognized reasons are talked about beneath:
Outlook’s email-related ‘Record Settings’ can make messages copy. In the event that the ‘Leave Messages on the Server’ settings alternative initiated it might bring about various duplicates of messages being made.
Synchronizing Outlook with different applications/gadgets (Phones, PDAs, and so on)
Other Reasons: You may get different duplicates of similar email from your companions or associates who may advance it to borrow your time.
Sometimes even your enemy of infection programming may likewise make copy messages.
Effect of Duplicate Emails in Outlook
The accompanying results might be experienced in the event that you don’t eliminate copy messages in Outlook:
Outlook will begin to back off in execution. For instance, on the off chance that you need to locate a specific message utilizing the Instant Search highlight of Outlook, yet all that you see is “Looking”. Additionally, you need to stand by quietly while Outlook rehashes the looking through measure through all email messages in a given organizer including copied ones.
In a large portion of the frameworks, Outlook begins to back off when the letter drop crosses its size impediment, and in the event that you don’t diminish the size of your post box containing Outlook information, your Outlook may begin hanging, freezing and not reacting.
4 Free Solutions to Remove Duplicate Emails in Outlook
Account Settings: Sometimes accidentally, when you endeavor to arrange Outlook PST documents more than one time it might add numerous records to MS Outlook synchronizing same PST information for those documents, which make copies in Outlook.
Do the accompanying strides to eliminate the copies for this situation:
Go to File >> Info >> click on Account Settings.
You will see the Account Settings window. There in the E-mail tab, search the name of the record arranged to Outlook. In the event that a similar record shows up more than one time, eliminate an additional record. To do that, drag your cursor, select the E-mail record, and snap on Remove.
After eliminating the copy account, click Close.
Import/Export: Do the accompanying advances (see the pictorial strides) to utilize this strategy:
Start Outlook and snap on File >> select Open and Export >> click on Import/Export
You will see Import and Export Wizard window, select an alternative Import from another program or document and snap Next.
You will see Import a document window, select Outlook Data File (.pst) and snap Next.
You will see Import Outlook Data File, peruse the document to import and check Do not import copies and snap Next.
Finally, select Outlook Data File to trade total information or you can choose the specific envelopes of ‘Standpoint Data File’ in the wake of tapping on the chose organizers >> click on Finish.
Modifying ‘View’ settings in Outlook to eliminate copy messages in Outlook
Use Outlook’s Clean Up Tool
Tidy Up Tool is a component of Outlook that permits you to eliminate copy messages in Outlook. The following are the means you need to follow:
Open your MS Outlook >> click on Clean Up from the lace bar of Outlook window to extend all the choices.
After growing three alternatives will be given: “Tidy Up Conversation”, “Tidy Up Folders”, “Tidy Up Folders and Subfolders” >> click on “Tidy Up Folders and Sub envelopes” choice.
A box specifically “Tidy Up Folder” will spring up >> which will find out if to continue the tidy up or drop? Snap on “Tidy Up Folder”.
Another choice that a “Tidy Up Folder” show is “Settings”, click on “Settings” choice to physically give the area of erased things organizer (If you are utilizing your own custom area to save erased envelope rather than default area).
After tapping on “Settings” it will open “Standpoint Options” window >> click on “Peruse” to give the area of PST record.
A spring up will show up by name Select Folder (showing all the accessible envelopes in Outlook) >> click on Deleted Items >> click OK.
At last, click on “Tidy Up Folder”.
Use Outlook Duplicate Remove Tool whenever you need to remove duplicate items from Outlook email client application. Outlook Duplicates Remover tool allows users to remove all kinds of duplicate items from Outlook without any data loss. The duplicate remover is fully capable of removing all duplicate items in PST mailbox like emails, notes, tasks, attachments, calendars, journals, etc.
#pst #duplicate #remove
1613210367
Standpoint is perhaps the most mainstream email customers across the business clients. Yet, at times, clients frequently go over the issue of copy messages in their MS Outlook account. Be that as it may, the copy messages mean having a similar email more than once in any of the envelopes, for example, inbox. While utilizing any variant of this work area based email customer, this issue can show up whenever and makes a peculiar yet irritating difficulty for the clients.
PURPOSE FOR DUPLICATE EMAILS IN MS OUTLOOK
In the “Record Setting” of Outlook, if the alternative “Leave Messages on the Server” is initiated, this will make different duplicates of a solitary email.
Duplicate messages can show up if there is an issue in synchronizing various gadgets with Outlook.
Sometimes, the counter infection programming introduced in the framework is answerable for the copy messages.
If the Inbox update recurrence is set to an exceptionally short span, it can likewise bring about copy messages.
RESULT OF DUPLICATE EMAILS IN OUTLOOK
Copy email messages can cause various issues. Initially, it superfluously devours a ton of room. Along these lines, it hinders the exhibition of Microsoft Outlook and doesn’t work appropriately. As the PST record size continues expanding superfluously enormous because of copy messages, it can cause information defilement and information misfortune in Outlook. In addition, copy messages in the inbox make bunches of disarray additionally among the clients.
Try Outlook Duplicates Remover remarkably decreases the size of the Outlook PST files and thus helps in preventing PST corruption due to being over-sized.The tool can scan the whole mailbox, such as Sent Items, Inbox, Deleted Items and Outbox. It removes all multiple and Duplicate email copies.
#pst #duplicate #remove
1611585832
https://www.pythonpool.com/remove-punctuation-python/
#remove #punctuation #python
1591254416
Hi Guys.
In this example,I will learn you how to remove register route in laravel, you can simply and easy to remove register route.
As we know laravel provide Auth::routes() for all login, registration, reset password and email verification. You can use default following routes lists instead of Auth::routes()
Link :- https://www.nicesnippets.com/blog/laravel-remove-register-route-example
#laravel #laravel5 #laravel6 #laravel7 #route #remove