Brook  Hudson

Brook Hudson

1659340560

Tabulo: Plain Text Table Generator for Ruby, with A DRY

Tabulo

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.

Overview

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

 

Features

  • Presents a DRY API that is column-based, not row-based, meaning header and body rows are automatically in sync
  • Lets you set fixed column widths, then either wrap or truncate the overflow
  • Alternatively, “pack” the table so that columns are auto-sized to their contents, but without overflowing the terminal
  • Cell alignment is configurable, but has helpful content-based defaults (numbers right, strings left)
  • Tabulate any Enumerable: the underlying collection need not be an array
  • Step through your table a row at a time, printing as you go, without waiting for the underlying collection to load. In other words, have a streaming interface for free.
  • Add an optional title to your table
  • The header row can be repeated at arbitrary intervals
  • Newlines within cell content are correctly handled
  • Multibyte Unicode characters are correctly handled
  • Option to preserve whole words when wrapping content
  • Apply colours and other styling to table content and borders, without breaking the table
  • Easily transpose the table, so that rows are swapped with columns
  • Choose from multiple border configurations, including Markdown, “ASCII”, and smoothly joined Unicode border characters

Tabulo has also been ported to Crystal (with some modifications): see Tablo.

Installation

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'

 

Detailed usage

 

Creating a table

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…

 

Adding columns

 

Quick API

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

 

Full API

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

 

Column labels vs headers

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. (Strings and Symbols 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

 

Positioning columns

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

 

Extracting column content from a hash or array

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.

 

Removing columns

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?)

 

Adding a title

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.

 

Cell alignment

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)

 

Column width, wrapping and truncation

 

Configuring fixed widths

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.

 

Automating column widths

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 packing 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.

 

Configuring padding

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.

 

Overflow handling

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.

 

Wrapping at word boundaries

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).

 

Manual cell wrapping

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.

 

Formatting cell values

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.

 

Colours and other styling

 

Styling cell content

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.

 

Styling column headers

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.

 

Styling the table title

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 borders

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" })

 

Setting default styles

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.

 

Repeating headers

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.

 

Using a Table Enumerator

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.)

 

Accessing cell values

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

 

Accessing the underlying enumerable

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

 

Transposing rows and columns

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.

 

Configuring borders

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.

 

Row dividers

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.

 

Using a table as a snapshot rather than as a dynamic view

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

 

Comparison with other libraries

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.

 

Contributing

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.

 

License

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

#ruby  #ruby-on-rails 

Tabulo: Plain Text Table Generator for Ruby, with A DRY
Hermann  Frami

Hermann Frami

1656599580

Serverless S3 Remover

serverless-s3-remover

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

terminal.png

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

#serverless #s3 #remove #plugin 

Serverless S3 Remover

Query Selector Shadow Dom

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.

ko-fi


// available as an ES6 module for importing in Browser environments

import { querySelectorAllDeep, querySelectorDeep } from 'query-selector-shadow-dom';

What is a nested shadow root?

Image of Shadow DOM elements in dev tools 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])");

API

  • querySelectorAllDeep - mirrors querySelectorAll from the browser, will return an Array of elements matching the query
  • querySelectorDeep - mirrors querySelector from the browser, will return the first matching element of the query.
  • collectAllElementsDeep - collects all elements on the page, including shadow dom

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.

Known limitations

  • Source ordering of results may not be preserved. Due to the nature of how this library works, by breaking down selectors into parts, when using multiple selectors (e.g. split by commas) the results will be based on the order of the query, not the order the result appear in the dom. This is different from the native querySelectorAll functionality. You can read more about this here: https://github.com/Georgegriff/query-selector-shadow-dom/issues/54

Plugins

WebdriverIO

This 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))

How is this different to shadow$

shadow$ only goes one level deep in a shadow root.

Take this example. Image of Shadow DOM elements in dev tools 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))

Known issues

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

Puppeteer

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.

Puppeteer examples

Playwright

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

Protractor

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.

Examples

Provide alternative node

    // 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.

Chrome downloads page

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.

Importing

  • Shipped as an ES6 module to be included using a bundler of your choice (or not).
  • ES5 version bundled ontop the window as window.querySelectorShadowDom available for easy include into a test framework

Running the code locally

npm install

Running the tests

npm test

Running the tests in watch mode

npm run watch

Running the build

npm run build

Author: Georgegriff
Source Code: https://github.com/Georgegriff/query-selector-shadow-dom 
License: MIT license

#node #javascript #webdriver #webcomponents 

Query Selector Shadow Dom
Desmond  Gerber

Desmond Gerber

1653737280

KambojaJS: New Meteoris, A MVC and Modular Framework for Meteor

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

1. Install Meteor, Create New Project & Download Required Packages

#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

2. Create hooked Navbar and Sidebar inside your "root/client/hook" folder

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"

3. Creating Home/Site Page

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 router file on "root/radiegtya:site/lib/router.js", and type this code
/**
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"});
    },   
});
  • We are not yet able to use this router, but once you have created the views, you can access this page by calling this route:
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 controller file on "root/radiegtya:site/lib/controller/SiteController.js"
/*
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

  • create views index file on "root/radiegtya:site/client/views/index.html". Look at the template naming convention, it use "meteorusername_moduleAction" namespacing, so for this views we are gonna use "radiegtya_siteIndex". Which is "radiegtya" for meteor username, "site" for the module name, and "Index" in camel case after site as action name.
<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.

  • Don't forget to add the js file for the index. Create this file on "root/radiegtya:site/client/views/index.js"
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

enter image description here

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:

1.1. meteoris:flash

Meteoris Package to show Flash/Toast Error/Success in your bottom right screen.

enter image description here

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

1.2. meteoris:form-validation

Meteoris package to validate form easily. This Meteoris.FormValidation extension depends on collection2 and simpleschema:

enter image description here

AccountType = new Mongo.Collection("accountType");

var schemas = new SimpleSchema({
    accountClassId: {
        type: String,
        label: "Kelas Akun",
    },    
});

AccountType.attachSchema(schemas);
  • how to use in your js helper:
    /* show error message on view */
    error: function(field) {
        return Meteoris.FormValidation.error(YourCollectionName, field);
    },
  • how to use in your html view:
       <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>

1.3. meteoris:formatter

  • this Meteoris.Formatter extension used for formatting anything like date, datetime, currency etc
  • this require lepozepo:accounting and momentjs:moment to works
  • how to use in your html view:

enter image description here

{{meteoris_formatter 'the function name' firstParam secondParam}}
  • available function name:
    - date (date)
    - dateTime (date)
    - elapsedTime (date)
    - currency (value)
    - combodate (date)
    - combodateEmpty (date)
  • example date formatting
{{meteoris_formatter 'date' getCurrDate}}
Template.someTheme.helpers({
	getCurrDate: function(){
		return new Date();
	}
});

1.4. meteoris:grid-view

  • this Meteoris.GridView extension used for sorting your table header ascending or descending.

enter image description here

  • how to use in your html view:
<th id="btnSortName" class="{{meteoris_gridViewSortClass 'name'}}">Name</th>
  • how to use in your js events:
    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

How to use

  1. You can do general setting of this theme by targetting this url /meteoris/theme-admin/setting

enter image description here

  1. You can easily hook sidebar in html file:

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>
  1. You can hook navbar too in html file: a. In your app client folder, create hook folder and inside it, add this template code. b. You can change everything such as notification, current logged in user, user profile menu etc inside the template as you wish
<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)

  • NB: This package depends on Meteoris Theme Admin
  • The First Registered user will be assigned to admin group by default

You can use this main router:

'/meteoris/user', #to manage user as admin enter image description here

'/meteoris/user/login', #to logged in the user and '/meteoris/user/register', #to registering user enter image description here

'/meteoris/user/profile' #to updating current logged in user profile enter image description here

'/meteoris/user/settings', #to setting user oauth account enter image description here

#4. Install Meteoris Role (Not Required but it will be better to be installed)

enter image description here

How to use

1. Check whether current logged in user is in role or not in Collection/Server:

//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("/");
    }
}

2. Check whether current logged in user is in group or not in Collection/Server:

//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("/");
    }
}

3. Check whether current logged in user is in role or not in Client template:

//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}}

4. Check whether current logged in user is in group or not in Client template:

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

#node #javascript #mvc #framework 

KambojaJS: New Meteoris, A MVC and Modular Framework for Meteor

12素晴らしいLinuxコマンドとユーティリティ

Linux用のあまり知られていないユーティリティとコマンドの別のリストが戻ってきました。これはあなたが日常の仕事をより速く進めるのに役立ちます。

1.グーグル

私たちは、ブラウザのグラフィカルインターフェイスを介して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

グーグル実行中

2. sl(蒸気機関車)

コマンド「ls」を入力する代わりに、「sl」と入力した頻度はどれくらいですか。

面倒な「コマンドが見つかりません」というメッセージを表示する代わりに、この小さなユーティリティは素敵な画像を表示します。少なくともこのように、あなたは少し笑うか、次回はもっと注意するでしょう。

インストール方法:

$sudo apt install sl

使用例:

$ sl

slランニング

確かに、注意を引くのにも役立ちますが、最終的には面倒になる可能性があります。

3. hstr

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

4.はい

Linuxにデフォルトで付属しているこのコマンドは、事前定義された応答を端末に渡すために使用できるシステム管理者にとって便利です。

使用例:

$ yes hello-world

はい実行中

5. rev(リバース)

与えられたすべての文字列を逆にします。これは時々役に立ちます。

使用例:

$ rev
$ hello world!

revrunning

良いことは、何もインストールする必要がないことです。

6. Wikit

このLinuxユーティリティは、コマンドラインからウィキペディアの記事を検索するために使用されます。

使用する必要があるのは、情報を取得する検索語を使用してコマンドを実行することだけです。

URL:https ://www.tecmint.com/wikipedia-commandline-tool/

インストール方法:

# Debian/Ubuntu$ sudo apt install nodejs	$ sudo npm install wikit -g

使用例:

Wikit matrix

ウィキの実行

7. pydf

このツールは、「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

8.trash-cli

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の実行

9. eSpeak NG

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

今、あなたがしなければならないのはあなたの好きなアプリケーションでそれをプレイすることだけです、そしてそれはそれです。

10.一瞥

このコマンドラインシステム監視ユーティリティを使用すると、CPU、負荷平均、メモリ、ネットワークインターフェイス、ディスクI / O、プロセス、およびファイルシステムスペースの使用率を監視できます。

GlancesユーティリティはPythonで記述されており、psutilライブラリを使用してシステム情報を取得し、わかりやすい形式で表示します。

また、構成ファイルを使用してさまざまな警告しきい値を設定することもできます。

すべてを管理するのに非常に便利です。

URL:https ://nicolargo.github.io/glances/

インストール方法:

$ sudo apt install glances

使用例:

$ glances

走っている一瞥

11.gtop

gtopユーティリティは、システムとそのプロセスに関するさまざまな情報を表示するLinuxシステムモニターです。

このツールは使いやすく、リソースをほとんど消費しないため、リソースを無駄にすることなく実行できます。さらに、オペレーティングシステムのコマンドを使用するため、非常に印象的で正確な方法で情報を表示します。

これは、CanGüneyAksakalliによってJavaScriptで開発されたオープンソースツールであり、インストールして実行するには、コンピューターにnodejsをインストールする必要があります。

URL:git clone https://github.com/aksakalli/gtop.git

インストール方法:

$ sudo npm install gtop -g

使用例:

$ gtop

gtopランニング

12.ファクター

以前のユーティリティと同様に、この小さなプログラムはデフォルトであり、ある時点で役立つ場合があります。

ファクターランニング

Linuxのコマンドラインユーティリティや、特に役立つコマンドやあまり知られていないコマンドをご存知の場合は、それについて教えていただければ幸いです。

ありがとうございました!

ソース:https ://medium.com/codex/12-awesome-linux-commands-utilities-49ab56588a84

#linux 

12素晴らしいLinuxコマンドとユーティリティ

12 Increíbles Comandos Y Utilidades De Linux

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.

1. googleador

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

2. sl (Locomotora de vapor)

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.

3. calle

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

4. si

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

5. rev (Reversa)

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.

6. Wiki

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

7. pdf

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

8. basura-cli

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

9. eSpeak NG

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.

10. miradas

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

11. arriba

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

12. factor

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

#linux 

12 Increíbles Comandos Y Utilidades De Linux

Remove/Hide Columns While Export Data In Datatable

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.

Remove/Hide Columns While Export Data In Datatable

https://websolutionstuff.com/post/remove-hide-columns-while-export-data-in-datatable

Thanks for the reading…!!

#export #data #datatable #yajra datatable #remove #export button

Remove/Hide Columns While Export Data In Datatable

How To Remove Spaces Using JQuery

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.

Read More : How To Remove Spaces Using JQuery

https://websolutionstuff.com/post/how-to-remove-spaces-using-jquery


Read Also : How To Check Email Already Exist Or Not In Laravel

https://websolutionstuff.com/post/how-to-check-email-already-exist-or-not-in-laravel

#javascript #jquery #remove #space #textbox #example

How To Remove Spaces Using JQuery

jistul arti

1614855826

PST Duplicate Remover – Top 4 Free Methods

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

PST Duplicate Remover – Top 4 Free Methods

jistul arti

1613210367

QUICK STEPS TO REMOVE DUPLICATE EMAILS IN OUTLOOK

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

QUICK STEPS TO REMOVE DUPLICATE EMAILS IN OUTLOOK
mehul bagada

mehul bagada

1591254416

Laravel Remove Register Route Example

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

Laravel Remove Register Route Example