contact_sync 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +37 -0
  5. data/app/assets/javascripts/con_sync/api/users_sync.js +2 -0
  6. data/app/assets/javascripts/con_sync/application.js +13 -0
  7. data/app/assets/stylesheets/con_sync/api/users_sync.css +4 -0
  8. data/app/assets/stylesheets/con_sync/application.css +15 -0
  9. data/app/controllers/con_sync/api/users_sync_controller.rb +27 -0
  10. data/app/controllers/con_sync/application_controller.rb +4 -0
  11. data/app/helpers/con_sync/api/users_sync_helper.rb +4 -0
  12. data/app/helpers/con_sync/application_helper.rb +4 -0
  13. data/app/models/address.rb +5 -0
  14. data/app/models/contact.rb +72 -0
  15. data/app/models/email.rb +5 -0
  16. data/app/models/phone.rb +38 -0
  17. data/app/models/user.rb +3 -0
  18. data/app/views/layouts/con_sync/application.html.erb +14 -0
  19. data/config/routes.rb +0 -0
  20. data/db/global_phone.json +6648 -0
  21. data/lib/contact_sync.rb +12 -0
  22. data/lib/contact_sync/acts_as_syncable.rb +7 -0
  23. data/lib/contact_sync/engine.rb +5 -0
  24. data/lib/contact_sync/syncable.rb +124 -0
  25. data/lib/contact_sync/util/api_constraints.rb +10 -0
  26. data/lib/contact_sync/util/global_phone.rb +3 -0
  27. data/lib/contact_sync/util/string_phone.rb +28 -0
  28. data/lib/contact_sync/version.rb +3 -0
  29. data/lib/generators/install_contact_sync/USAGE +8 -0
  30. data/lib/generators/install_contact_sync/install_contact_sync_generator.rb +33 -0
  31. data/lib/generators/install_contact_sync/templates/create_contact_sync_schema.rb +70 -0
  32. data/lib/generators/install_contact_sync/templates/routes.rb +9 -0
  33. data/lib/tasks/con_sync_tasks.rake +4 -0
  34. data/test/con_sync_test.rb +7 -0
  35. data/test/controllers/con_sync/api/users_sync_controller_test.rb +9 -0
  36. data/test/dummy/README.rdoc +28 -0
  37. data/test/dummy/Rakefile +6 -0
  38. data/test/dummy/app/assets/javascripts/application.js +13 -0
  39. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  40. data/test/dummy/app/controllers/application_controller.rb +5 -0
  41. data/test/dummy/app/helpers/application_helper.rb +2 -0
  42. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  43. data/test/dummy/bin/bundle +3 -0
  44. data/test/dummy/bin/rails +4 -0
  45. data/test/dummy/bin/rake +4 -0
  46. data/test/dummy/bin/setup +29 -0
  47. data/test/dummy/config.ru +4 -0
  48. data/test/dummy/config/application.rb +26 -0
  49. data/test/dummy/config/boot.rb +5 -0
  50. data/test/dummy/config/database.yml +25 -0
  51. data/test/dummy/config/environment.rb +5 -0
  52. data/test/dummy/config/environments/development.rb +41 -0
  53. data/test/dummy/config/environments/production.rb +79 -0
  54. data/test/dummy/config/environments/test.rb +42 -0
  55. data/test/dummy/config/initializers/assets.rb +11 -0
  56. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  57. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  58. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  59. data/test/dummy/config/initializers/inflections.rb +16 -0
  60. data/test/dummy/config/initializers/mime_types.rb +4 -0
  61. data/test/dummy/config/initializers/session_store.rb +3 -0
  62. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  63. data/test/dummy/config/locales/en.yml +23 -0
  64. data/test/dummy/config/routes.rb +4 -0
  65. data/test/dummy/config/secrets.yml +22 -0
  66. data/test/dummy/public/404.html +67 -0
  67. data/test/dummy/public/422.html +67 -0
  68. data/test/dummy/public/500.html +66 -0
  69. data/test/dummy/public/favicon.ico +0 -0
  70. data/test/integration/navigation_test.rb +10 -0
  71. data/test/lib/generators/con_sync/install_con_sync_generator_test.rb +16 -0
  72. data/test/test_helper.rb +20 -0
  73. metadata +210 -0
@@ -0,0 +1,12 @@
1
+ require "contact_sync/engine"
2
+ require 'contact_sync/util/api_constraints'
3
+ require 'contact_sync/util/string_phone'
4
+ require 'contact_sync/util/global_phone'
5
+ require 'contact_sync/acts_as_syncable'
6
+ require 'contact_sync/syncable'
7
+ require 'encrypted_strings'
8
+
9
+ module ContactSync
10
+ end
11
+
12
+ ActiveRecord::Base.send :extend, ContactSync::ActsAsSyncable
@@ -0,0 +1,7 @@
1
+ module ContactSync
2
+ module ActsAsSyncable
3
+ def acts_as_syncable
4
+ include ContactSync::Syncable
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module ContactSync
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ContactSync
4
+ end
5
+ end
@@ -0,0 +1,124 @@
1
+ module ContactSync
2
+ module Syncable
3
+ def phone_number
4
+ return self.encrypted_number.try(:decrypt,:symmetric) || ""
5
+ end
6
+
7
+ def phone_number=(string)
8
+ self.cc_prefix, num = string.extract_country_code
9
+ self.encrypted_number = num.encrypt(:symmetric)
10
+ self.encrypted_number
11
+ end
12
+
13
+ def complete_phone_number
14
+ if self.cc_prefix.blank?
15
+ "00#{self.phone_number}"
16
+ else
17
+ "+#{self.cc_prefix}#{self.phone_number}"
18
+ end
19
+ end
20
+ def sync_contacts(contact_hash = {})
21
+ result = {:new => {success:[], failed:[]}, modified: {success:[], failed:[]}, deleted: {success:[], failed:[]}}
22
+ raise ArgumentError, "You need to provide contacts hash." if contact_hash.blank?
23
+
24
+ ##########################################################################
25
+ #########################Create new Contacts##############################
26
+ if !contact_hash[:new].blank?
27
+ contacts = contact_hash[:new]
28
+ contacts.each do |aContact|
29
+ phones = aContact[:phones]
30
+ emails = aContact[:emails]
31
+
32
+ aContact.delete :phones
33
+ aContact.delete :emails
34
+
35
+ newContact = Contact.new(contact_params(aContact))
36
+ if phones
37
+ phones.each do |aPhone|
38
+ newContact.phones.build(phone_params(aPhone))
39
+ end
40
+ end
41
+ if emails
42
+ emails.each do |anEmail|
43
+ newContact.emails.build(email_params(anEmail))
44
+ end
45
+ end
46
+ if newContact.save
47
+ # result[:new][:success] << newContact.record_id
48
+ self.contacts << newContact
49
+ else
50
+ result[:new][:failed] << newContact.record_id
51
+ end
52
+ end
53
+ end
54
+ ##########################################################################
55
+ ########################Modify Contacts###################################
56
+ if !contact_hash[:modified].blank?
57
+ modified_contacts = contact_hash[:modified]
58
+ modified_contacts.each do |con|
59
+ theContact = self.contacts.find_by_record_id(con[:record_id])
60
+ if ! theContact.blank?
61
+ if !theContact.update_attributes(contact_params(con))
62
+ result[:modified][:failed] << theContact.record_id
63
+ puts "Error Updating Contact."
64
+ else
65
+ # result[:modified][:success] << theContact.record_id
66
+ end
67
+ end
68
+ end
69
+ end
70
+ ##########################################################################
71
+ ########################Delete Contacts###################################
72
+ if !contact_hash[:deleted].blank?
73
+ delete_contacts = contact_hash[:deleted]
74
+ delete_contacts.each do |con|
75
+ if self.contacts.find_by_record_id(con[:record_id].to_i).destroy
76
+ # result[:deleted][:success] << con[:record_id]
77
+ else
78
+ result[:deleted][:failed] << con[:record_id]
79
+ end
80
+ end
81
+ end
82
+ after_contact_sync
83
+ self.save
84
+ return result
85
+ end
86
+
87
+ def matched_contacts
88
+ matched_contacts = []
89
+ user_phones = []
90
+ user_emails = []
91
+ self.contacts.each do |con|
92
+ user_phones.concat con.phones
93
+ user_emails.concat con.emails
94
+ end
95
+
96
+ user_phones.each do |phone|
97
+ u = User.where(phone_number: phone.number.encrypt(:symmetric)).limit(1).first
98
+ matched_contacts << u unless u.blank?
99
+ end
100
+ user_emails.each do |mail|
101
+ u = User.where(email: mail.email).limit(1).first
102
+ matched_contacts << u unless u.blank?
103
+ end
104
+ return matched_contacts
105
+ end
106
+
107
+ private
108
+ def contact_params(aContact)
109
+ aContact.permit(:composite_name, :first_name, :middle_name, :last_name, :prefix, :suffix, :nickname, :job_title, :department, :organization, :birthdate, :note, :creation_date, :modification_date, :record_id)
110
+ end
111
+
112
+ def phone_params aPhone
113
+ aPhone.permit(:label, :number)
114
+ end
115
+
116
+ def email_params anEmail
117
+ anEmail.permit(:label, :email)
118
+ end
119
+
120
+ def after_contact_sync
121
+ self.last_contact_sync = DateTime.now.utc
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,10 @@
1
+ class ApiConstraints
2
+ def initialize(options)
3
+ @version = options[:version]
4
+ @default = options[:default]
5
+ end
6
+
7
+ def matches?(req)
8
+ @default || req.headers['Accept'].include?("application/vnd.kanari.v#{@version}")
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ require 'global_phone'
2
+ puts "\n\n\n\n\n\n\n\n\n\n\n#{ContactSync::Engine.root}\n\n\n\n\n\n\n\n"
3
+ GlobalPhone.db_path = ContactSync::Engine.root.join('db/global_phone.json')
@@ -0,0 +1,28 @@
1
+ require 'active_support/concern'
2
+
3
+ module ContactSync
4
+ module StringPhone
5
+ # extend ActiveSupport::Concern
6
+ def extract_country_code
7
+ string = self
8
+ if string[0..1] == "00"
9
+ string[0..1] = "+"
10
+ end
11
+ if string[0] == "+"
12
+ breakdown = GlobalPhone.parse(string)
13
+ prefix = breakdown.country_code
14
+ num = breakdown.national_string
15
+ return [prefix,num]
16
+ elsif string[0] == "0"
17
+ num = string[1..-1]
18
+ prefix = "0"
19
+ return [prefix, num]
20
+ else
21
+ return ["", string]
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ puts "Including StringPhoner Module to: '#{String.name}' class"
28
+ String.send(:include, ContactSync::StringPhone)
@@ -0,0 +1,3 @@
1
+ module ContactSync
2
+ VERSION = "0.2.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate install_contact_sync Thing
6
+
7
+ This will create:
8
+ what/will/it/create
@@ -0,0 +1,33 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class InstallContactSyncGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ def self.next_migration_number(path)
7
+ ActiveRecord::Generators::Base.next_migration_number(path)
8
+ end
9
+
10
+ source_root File.expand_path('../templates', __FILE__)
11
+
12
+ def generate_migration
13
+ migration_template "create_contact_sync_schema.rb", "db/migrate/create_contact_sync_schema.rb"
14
+ end
15
+
16
+ def generate_routes
17
+ route(File.read(File.join(File.dirname(__FILE__), 'templates/routes.rb') ))
18
+ end
19
+
20
+ # def generate_global_phone_db
21
+ # copy_file "global_phone.json", "db/global_phone.json"
22
+ # end
23
+
24
+ def generate_encrypted_strings_config
25
+ inject_into_file 'config/environments/development.rb', before: "end\n" do <<-'RUBY'
26
+
27
+ EncryptedStrings::SymmetricCipher.default_algorithm = 'aes-256-cbc'
28
+ EncryptedStrings::SymmetricCipher.default_password = 'Replace This with some long alphanumeric string'
29
+
30
+ RUBY
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,70 @@
1
+ class CreateContactSyncSchema < ActiveRecord::Migration
2
+ def up
3
+ create_table :contacts do |t|
4
+ t.string :composite_name
5
+ t.string :first_name
6
+ t.string :middle_name
7
+ t.string :last_name
8
+ t.string :prefix
9
+ t.string :suffix
10
+ t.string :nickname
11
+ t.string :organization
12
+ t.string :job_title
13
+ t.string :department
14
+ t.date :birthdate
15
+ t.text :note
16
+ t.string :device_id
17
+ t.integer :record_id, default: 0
18
+ t.datetime :creation_date
19
+ t.datetime :modification_date
20
+ t.integer :user_id
21
+ t.timestamps
22
+ end
23
+
24
+ create_table :phones do |t|
25
+ t.string :label
26
+ t.string :encrypted_number
27
+ t.string :cc_prefix
28
+ t.string :extension
29
+ t.integer :contact_id
30
+
31
+ t.timestamps
32
+ end
33
+
34
+ create_table :emails do |t|
35
+ t.string :label
36
+ t.string :encrypted_email
37
+ t.integer :contact_id
38
+
39
+ t.timestamps
40
+ end
41
+
42
+ if table_exists? :users
43
+ add_column :users, :last_contact_sync, :datetime
44
+ add_column :users, :encrypted_number, :string
45
+ add_column :users, :cc_prefix, :string
46
+ add_index :users, :encrypted_number
47
+ else
48
+ create_table :users do |t|
49
+ t.string :first_name
50
+ t.string :middle_name
51
+ t.string :last_name
52
+ t.string :email
53
+ t.string :encrypted_number
54
+ t.string :cc_prefix
55
+ t.datetime :last_contact_sync
56
+ end
57
+ add_index :users, :encrypted_number
58
+ end
59
+
60
+ add_index :contacts, [:record_id, :device_id], name: "unique_device_contact", unique: true
61
+ add_index :contacts, :user_id
62
+ add_index :phones, :encrypted_number
63
+ add_index :emails, :encrypted_email
64
+
65
+ end
66
+
67
+ def down
68
+
69
+ end
70
+ end
@@ -0,0 +1,9 @@
1
+
2
+ namespace :api do
3
+ scope module: :v1, constraints: ApiConstraints.new(version: 1, default: :true) do
4
+ resources :users, only: [] do
5
+ post :sync_contacts
6
+ get :match_contacts
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :contact_sync do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ContactSyncTest < ActiveSupport::TestCase
4
+ test "truth" do
5
+ assert_kind_of Module, ContactSync
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ module ContactSync
4
+ class Api::UsersSyncControllerTest < ActionController::TestCase
5
+ # test "the truth" do
6
+ # assert true
7
+ # end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require File.expand_path('../config/application', __FILE__)
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ class ApplicationController < ActionController::Base
2
+ # Prevent CSRF attacks by raising an exception.
3
+ # For APIs, you may want to use :null_session instead.
4
+ protect_from_forgery with: :exception
5
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Dummy</title>
5
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6
+ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>