Module: PincodeFinder

Defined in:
lib/pincode_finder.rb,
lib/pincode_finder/config.rb,
lib/pincode_finder/version.rb,
lib/pincode_finder/github_client.rb

Defined Under Namespace

Classes: Config, GitHubClient

Constant Summary collapse

DATA_FILE =
File.expand_path("../data/pincode_data_optimized.json.gz", __dir__)
DETAILS_HASH =
"{ district: <district>, state: <state>}".freeze
VERSION =
"0.1.4"

Class Method Summary collapse

Class Method Details

.add_pincode(pincode, input_details) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/pincode_finder.rb', line 36

def self.add_pincode(pincode, input_details)
  pincode = pincode.to_s
  input_details = normalize_and_filter(input_details)

  data = load_data
  record = data[pincode]

  return { status: "failure", error: "Pincode already present in the directory" } unless record.nil?

  verified, error = validate_pincode(pincode, input_details)
  return { status: "failure", error: error } unless error.nil?

  data[pincode] = input_details.merge("verified" => verified)

  save_data(data)

  sync_to_github(data)

  {
    status: "success",
    data: data[pincode],
    error: nil
  }
end

.configObject



12
13
14
# File 'lib/pincode_finder/config.rb', line 12

def self.config
  @config ||= Config.new
end

.configure {|config| ... } ⇒ Object

Yields:



16
17
18
# File 'lib/pincode_finder/config.rb', line 16

def self.configure
  yield(config)
end

.delete_pincode(pincode) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/pincode_finder.rb', line 121

def self.delete_pincode(pincode)
  pincode = pincode.to_s
  data = load_data

  record = data[pincode]
  return { status: "failure", error: "pincode not found" } unless record

  data.delete(pincode)

  save_data(data)
  sync_to_github(data)

  {
    status: "success",
    data: {},
    error: nil
  }
end

.find(pincode) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/pincode_finder.rb', line 15

def self.find(pincode)
  pincode = pincode.to_s
  data = load_data

  record = data[pincode]

  if record
    {
      pincode: pincode,
      state: record["state"],
      district: record["district"],
      verified: record["verified"] || true
    }
  else
    {
      error: "Pincode not found",
      message: "You can add it using: PincodeFinder.add_pincode(#{pincode}, #{DETAILS_HASH})"
    }
  end
end

.input_details_validation(data, input_details) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/pincode_finder.rb', line 140

def self.input_details_validation(data, input_details)
  input_state     = input_details["state"].to_s.titleize
  input_district  = input_details["district"].to_s.titleize

  api_state       = data["State"].to_s.titleize
  api_district    = data["District"].to_s.titleize
  api_division    = data["Division"].to_s.titleize

  return [false, "Incorrect state. Allowed: #{api_state}"] unless input_state == api_state

  unless input_district == api_district || input_district == api_division
    return [
      false,
      "Incorrect district. Allowed: #{api_district} or #{api_division}"
    ]
  end

  [true, nil]
end

.load_dataObject



160
161
162
# File 'lib/pincode_finder.rb', line 160

def self.load_data
  Zlib::GzipReader.open(DATA_FILE) { |gz| JSON.parse(gz.read) }
end

.normalize_and_filter(input_details) ⇒ Object



172
173
174
175
176
# File 'lib/pincode_finder.rb', line 172

def self.normalize_and_filter(input_details)
  input_details
    .transform_keys { |k| k.to_s.downcase }
    .slice("district", "state")
end

.save_data(json) ⇒ Object



164
165
166
167
168
169
170
# File 'lib/pincode_finder.rb', line 164

def self.save_data(json)
  json_string = JSON.pretty_generate(json)

  Zlib::GzipWriter.open(DATA_FILE) do |gz|
    gz.write(json_string)
  end
end

.sync_to_github(data) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/pincode_finder.rb', line 178

def self.sync_to_github(data)
  Thread.new do
    begin
      client = GitHubClient.new

      remote = client.get_file_with_sha
      sha    = remote[:sha]

      client.update_file(data, sha)
    rescue StandardError
      nil
    end
  end
end

.update_pincode(pincode, input_details) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/pincode_finder.rb', line 99

def self.update_pincode(pincode, input_details)
  pincode = pincode.to_s
  input_details = normalize_and_filter(input_details)
  data = load_data

  record = data[pincode]
  return { status: "failure", error: "pincode not found" } unless record

  verified, error = validate_pincode(pincode, input_details)
  return { status: "failure", error: error } unless error.nil?

  data[pincode] = input_details.merge("verified" => verified)
  save_data(data)
  sync_to_github(data)

  {
    status: "success",
    data: data[pincode],
    error: nil
  }
end

.validate_pincode(pincode, input_details) ⇒ Object



61
62
63
64
65
66
67
68
69
# File 'lib/pincode_finder.rb', line 61

def self.validate_pincode(pincode, input_details)
  verified, api_data = verify_pincode(pincode)
  return [false, nil] unless verified

  status, error = input_details_validation(api_data, input_details)
  return [false, error] unless status

  [verified, nil]
end

.verify_pincode(pincode) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/pincode_finder.rb', line 71

def self.verify_pincode(pincode)
  begin
    uri = URI("https://api.postalpincode.in/pincode/#{pincode}")

    response = Net::HTTP.start(uri.host, uri.port, use_ssl: true,
                                                   open_timeout: 3, read_timeout: 3) do |http|
      http.get(uri.request_uri)
    end

    return [false, nil] unless response.is_a?(Net::HTTPSuccess)

    json = JSON.parse(response.body) rescue nil
    return [false, nil] unless json.is_a?(Array) && json[0].is_a?(Hash)

    status = json[0]["Status"]
    return [false, nil] unless status == "Success"

    po = json[0]["PostOffice"]
    return [false, nil] unless po.is_a?(Array) && po[0].is_a?(Hash)

    details = po[0].slice("Division", "District", "State")

    [true, details]
  rescue StandardError
    [false, nil]
  end
end