-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support for CSV and multiple columns (#2)
* Remove not found `url` gem A `bundle install` complaints with: ``` Fetching gem metadata from https://rubygems.org/........ Fetching gem metadata from https://rubygems.org/. Resolving dependencies.... Your bundle is locked to url (0.3.2), but that version could not be found in any of the sources listed in your Gemfile. If you haven't changed sources, that means the author of url (0.3.2) has removed it. You'll need to update your bundle to a version other than url (0.3.2) that hasn't been removed in order to install. ``` It looks like that the gem that depends on this one had the same issue and drop it. * Gitignore common files * Extract email parsing into class And move Parser's specs to a new class-specific file. * Replace send calls to public method No reason to use `#send` if the method is public. * Improve a bit specs wording * Accept email data as a hash and store as metadata * Enable toggling the parser from config This adds a new parser and while making it backward compatible. It'll only be enabled if you configure with `processor = :metadata`. It does so by extracting specific entry parsing into two classes. Now we have them both encapsulated and we can extend the metadata one however we want without affecting current behaviour. * Read CSV format Well, a bit rudimentary still, but also normalize a bit the entry format. Less flexible => less complexity => less buggy. * Accept CSV header and unlimited columns This turns the entry parsers into full text parsers each with different features, not only at the entry level. While we keep the simple parser that deals only with names for backward compatibility, the new metadata parser deals with CSV and unlimited columns, while being more strict in its input format. * Refactor parsers naming So they better convey the use of the template method pattern and how things are design around inheritance. * Use stdlib's CSV interface Ruby's CSV parser can't compare to our naïve `split(",")`. * Increase #register_users test coverage * Doc Metadata mode and better config key Processor is already used by UserProcessor which is different from the parsers, which may lead to confusion. * Move email and name normalization to parser This is where it belongs. As a result, now the metadata parser doesn't force you to have the email in the second column. It'll just keep it out of the metadata JSON, that's all. * Remove unnecessary chomp The CSV lib already does so. * Memoize header row parsing We are hitting this computation for every single line from the CSV we process. * Fix flaky spec Hopefully. It's now consistently passing but I don't know why. I also took the chance to tidy up the shared examples hoping that'd make it easier to find to root cause of the flakyness.
- Loading branch information
1 parent
33314fb
commit ad8c6c0
Showing
14 changed files
with
494 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,3 +18,7 @@ spec/decidim_dummy_app | |
|
||
# default development application | ||
development_app | ||
|
||
.byebug_history | ||
.ruby-version | ||
tags |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
app/commands/decidim/direct_verifications/verification/base_parser.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# frozen_string_literal: true | ||
|
||
module Decidim | ||
module DirectVerifications | ||
module Verification | ||
# Abstract class all concrete parsers should inherit from. They are expected to implement | ||
# #header, #lines, and #parse_data methods. | ||
class BaseParser | ||
EMAIL_REGEXP = /([A-Z0-9+._-]+@[A-Z0-9._-]+\.[A-Z0-9_-]+)\b/i.freeze | ||
|
||
def initialize(txt) | ||
@txt = txt | ||
@emails = {} | ||
end | ||
|
||
def to_h | ||
lines.each do |line| | ||
EMAIL_REGEXP.match(line) do |match| | ||
email = normalize(match[0]) | ||
emails[email] = parse_data(email, line, header) | ||
end | ||
end | ||
|
||
emails | ||
end | ||
|
||
private | ||
|
||
attr_reader :txt, :emails | ||
|
||
def normalize(value) | ||
value.to_s.downcase | ||
end | ||
end | ||
end | ||
end | ||
end |
42 changes: 42 additions & 0 deletions
42
app/commands/decidim/direct_verifications/verification/metadata_parser.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# frozen_string_literal: true | ||
|
||
require "csv" | ||
|
||
module Decidim | ||
module DirectVerifications | ||
module Verification | ||
class MetadataParser < BaseParser | ||
def header | ||
@header ||= begin | ||
header_row = lines[0].chomp | ||
column_names = tokenize(header_row) | ||
column_names.map(&:to_sym).map(&:downcase) | ||
end | ||
end | ||
|
||
def lines | ||
@lines ||= StringIO.new(txt).readlines | ||
end | ||
|
||
def parse_data(email, line, header) | ||
tokens = tokenize(line) | ||
|
||
hash = {} | ||
header.each_with_index do |column, index| | ||
value = tokens[index].strip | ||
next if value.include?(email) | ||
|
||
hash[column] = value | ||
end | ||
hash | ||
end | ||
|
||
private | ||
|
||
def tokenize(line) | ||
CSV.parse(line)[0] | ||
end | ||
end | ||
end | ||
end | ||
end |
40 changes: 40 additions & 0 deletions
40
app/commands/decidim/direct_verifications/verification/name_parser.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# frozen_string_literal: true | ||
|
||
module Decidim | ||
module DirectVerifications | ||
module Verification | ||
class NameParser < BaseParser | ||
LINE_DELIMITER = /[\r\n;,]/.freeze | ||
NON_ALPHA_CHARS = /[^[:print:]]|[\"\$\<\>\|\\]/.freeze | ||
|
||
def header | ||
nil | ||
end | ||
|
||
def lines | ||
txt.split(LINE_DELIMITER) | ||
end | ||
|
||
def parse_data(email, line, _header) | ||
name = parse_name(email, line) | ||
name = strip_non_alpha_chars(name) | ||
name.presence || fallback_name(email) | ||
end | ||
|
||
private | ||
|
||
def strip_non_alpha_chars(str) | ||
(str.presence || "").gsub(NON_ALPHA_CHARS, "").strip | ||
end | ||
|
||
def parse_name(email, line) | ||
line.split(email).first | ||
end | ||
|
||
def fallback_name(email) | ||
email.split("@").first | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,19 +2,23 @@ | |
|
||
shared_examples_for "checking users" do |params| | ||
context "when check without mails" do | ||
before { params[:userlist] = "" } | ||
|
||
it "renders the index with info message" do | ||
params[:userlist] = "" | ||
post :create, params: params | ||
|
||
expect(flash[:info]).not_to be_empty | ||
expect(flash[:info]).to include("0 users detected") | ||
expect(subject).to render_template("decidim/direct_verifications/verification/admin/direct_verifications/index") | ||
end | ||
end | ||
|
||
context "when check with mails" do | ||
before { params[:userlist] = "[email protected]" } | ||
|
||
it "renders the index with info message" do | ||
params[:userlist] = "[email protected]" | ||
post :create, params: params | ||
|
||
expect(flash[:info]).not_to be_empty | ||
expect(flash[:info]).to include("1 users detected") | ||
expect(subject).to render_template("decidim/direct_verifications/verification/admin/direct_verifications/index") | ||
|
@@ -26,6 +30,7 @@ | |
context "when send valid emails" do | ||
it "creates warning message" do | ||
post :create, params: params | ||
|
||
expect(flash[:warning]).not_to be_empty | ||
expect(flash[:warning]).to include("1 detected") | ||
expect(flash[:warning]).to include("0 errors") | ||
|
@@ -39,6 +44,7 @@ | |
context "when send valid emails" do | ||
it "creates notice message" do | ||
post :create, params: params | ||
|
||
expect(flash[:notice]).not_to be_empty | ||
expect(flash[:notice]).to include("1 detected") | ||
expect(flash[:notice]).to include("0 errors") | ||
|
@@ -50,14 +56,18 @@ | |
|
||
shared_examples_for "revoking users" do |params| | ||
context "when send valid emails" do | ||
it "creates notice message" do | ||
before do | ||
create( | ||
:authorization, | ||
:granted, | ||
name: verification_type, | ||
user: authorized_user | ||
) | ||
end | ||
|
||
it "creates notice message" do | ||
post :create, params: params | ||
|
||
expect(flash[:notice]).not_to be_empty | ||
expect(flash[:notice]).to include("1 detected") | ||
expect(flash[:notice]).to include("0 errors") | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.