has_vcards 0.20.3 → 1.0.0.rc0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +4 -1
- data/README.md +18 -0
- data/Rakefile +28 -0
- data/app/assets/javascripts/has_vcards/application.js +13 -0
- data/app/assets/stylesheets/has_vcards/application.css +15 -0
- data/app/controllers/has_vcards/directory_lookup_controller.rb +21 -0
- data/app/controllers/has_vcards/phone_numbers_controller.rb +16 -0
- data/app/controllers/has_vcards/vcards_controller.rb +22 -0
- data/app/helpers/has_vcards/application_helper.rb +18 -0
- data/app/input/has_vcards/zip_locality_input.rb +16 -0
- data/app/models/has_vcards/address.rb +42 -0
- data/app/models/has_vcards/concerns/has_vcards.rb +42 -0
- data/app/models/has_vcards/phone_number.rb +61 -0
- data/app/models/has_vcards/vcard.rb +123 -0
- data/app/models/has_vcards/vcard/directory_address.rb +25 -0
- data/app/models/has_vcards/vcard/directory_lookup.rb +123 -0
- data/app/views/has_vcards/layouts/has_vcards/application.html.erb +14 -0
- data/app/views/{people → has_vcards/people}/_address_form.html.haml +1 -1
- data/app/views/{phone_numbers → has_vcards/phone_numbers}/_form.html.haml +0 -0
- data/app/views/{phone_numbers → has_vcards/phone_numbers}/_list.html.haml +0 -0
- data/app/views/{phone_numbers → has_vcards/phone_numbers}/_nested_form.html.haml +0 -0
- data/app/views/{phone_numbers → has_vcards/phone_numbers}/_phone_number.html.haml +0 -0
- data/app/views/{vcards → has_vcards/vcards}/_address.html.haml +0 -0
- data/app/views/{vcards → has_vcards/vcards}/_directory_lookup.html.haml +0 -0
- data/app/views/{vcards → has_vcards/vcards}/_directory_matches.html.haml +0 -0
- data/app/views/{vcards → has_vcards/vcards}/_form.html.haml +1 -1
- data/app/views/{vcards → has_vcards/vcards}/_show.html.haml +1 -1
- data/app/views/{vcards → has_vcards/vcards}/directory_lookup.js.erb +0 -0
- data/config/locales/de.yml +11 -18
- data/config/locales/en.yml +21 -6
- data/config/locales/fr.yml +6 -18
- data/config/routes.rb +1 -1
- data/db/migrate/20121113120000_create_honorific_prefixes_table.rb +1 -1
- data/db/migrate/20140617113710_rename_tables_for_namespacing.rb +8 -0
- data/db/migrate/20140619101337_remove_object_from_phone_number.rb +6 -0
- data/db/migrate/20140619102041_rename_vcard_object_to_reference.rb +6 -0
- data/db/migrate/20140619121756_refactor_honorific_prefix.rb +5 -0
- data/lib/has_vcards.rb +5 -18
- data/lib/has_vcards/engine.rb +29 -0
- data/lib/has_vcards/version.rb +4 -1
- data/spec/controllers/has_vcards/phone_numbers_controller_spec.rb +42 -0
- data/spec/controllers/has_vcards/vcards_controller_spec.rb +42 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +29 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +37 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +83 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140620151641_create_somethings.rb +7 -0
- data/spec/dummy/db/schema.rb +66 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +2513 -0
- data/spec/dummy/log/test.log +11671 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/addresses.rb +7 -0
- data/spec/factories/phone_numbers.rb +7 -0
- data/spec/factories/vcards.rb +5 -0
- data/spec/models/has_vcards/address_spec.rb +58 -0
- data/spec/models/has_vcards/concerns/has_vcards_spec.rb +50 -0
- data/spec/models/has_vcards/phone_number_spec.rb +82 -0
- data/spec/models/has_vcards/vcard_spec.rb +172 -0
- data/spec/rails_helper.rb +18 -0
- data/spec/spec_helper.rb +74 -0
- metadata +262 -47
- data/README.markdown +0 -14
- data/app/controllers/directory_lookup_controller.rb +0 -17
- data/app/controllers/phone_numbers_controller.rb +0 -14
- data/app/controllers/vcards_controller.rb +0 -20
- data/app/helpers/has_vcards_helper.rb +0 -13
- data/app/input/zip_locality_input.rb +0 -14
- data/app/models/address.rb +0 -40
- data/app/models/honorific_prefix.rb +0 -6
- data/app/models/phone_number.rb +0 -43
- data/app/models/vcard.rb +0 -112
- data/app/models/vcard/directory_address.rb +0 -21
- data/app/models/vcard/directory_lookup.rb +0 -122
- data/config/application.rb +0 -19
- data/config/boot.rb +0 -6
- data/lib/has_vcards/railtie.rb +0 -19
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
Copyright
|
1
|
+
Copyright 2007-2014 Simon Hürlimann <simon.huerlimann@cyt.ch>
|
2
|
+
Copyright 2009-2014 CyT GmbH <http://www.cyt.ch>
|
3
|
+
Copyright 2008 Agrabah GmbH <http://www.agrabah.ch>
|
4
|
+
Copyright 2007-2010 ZytoLabor <http://www.zyto-labor.com>
|
2
5
|
|
3
6
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
7
|
a copy of this software and associated documentation files (the
|
data/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
has_vcards
|
2
|
+
==========
|
3
|
+
|
4
|
+
[![Build Status](https://secure.travis-ci.org/huerlisi/has_vcards.png)](http://travis-ci.org/huerlisi/has_vcards)
|
5
|
+
|
6
|
+
Rails plugin providing VCard like contact and address models and helpers.
|
7
|
+
|
8
|
+
Install
|
9
|
+
=======
|
10
|
+
|
11
|
+
In Rails simply add
|
12
|
+
|
13
|
+
gem 'has_vcards'
|
14
|
+
|
15
|
+
License
|
16
|
+
=======
|
17
|
+
|
18
|
+
Released under the MIT license.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'HasVcards'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('{app,config,db,lib}/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path('../spec/dummy/Rakefile', __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
21
|
+
|
22
|
+
require 'rspec/core'
|
23
|
+
require 'rspec/core/rake_task'
|
24
|
+
|
25
|
+
desc 'Run all specs in spec directory (excluding plugin specs)'
|
26
|
+
RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
|
27
|
+
|
28
|
+
task default: :spec
|
@@ -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 vendor/assets/javascripts of plugins, if any, 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/sstephenson/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 vendor/assets/stylesheets of plugins, if any, 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,21 @@
|
|
1
|
+
module HasVcards
|
2
|
+
class DirectoryLookupController < ApplicationController
|
3
|
+
def search
|
4
|
+
@selector = params[:selector]
|
5
|
+
vcard_params = extract_vcard_params(params, @selector)
|
6
|
+
|
7
|
+
@vcard = Vcard.new(vcard_params)
|
8
|
+
render 'vcards/directory_lookup'
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def extract_vcard_params(params, selector)
|
14
|
+
keys = selector.delete(']').split('[')
|
15
|
+
vcard_params = params
|
16
|
+
keys.each { |key| vcard_params = vcard_params[key] }
|
17
|
+
|
18
|
+
vcard_params
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HasVcards
|
2
|
+
class PhoneNumbersController < ApplicationController
|
3
|
+
# Use inherited resources logic
|
4
|
+
inherit_resources
|
5
|
+
respond_to :html, :js
|
6
|
+
|
7
|
+
# Relations
|
8
|
+
optional_belongs_to :vcard
|
9
|
+
|
10
|
+
# Optionally support in place editing
|
11
|
+
if respond_to? :in_place_edit_for
|
12
|
+
in_place_edit_for :phone_number, :phone_number_type
|
13
|
+
in_place_edit_for :phone_number, :number
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module HasVcards
|
2
|
+
class VcardsController < ApplicationController
|
3
|
+
# Use inherited resources logic
|
4
|
+
inherit_resources
|
5
|
+
respond_to :html, :js
|
6
|
+
|
7
|
+
def directory_lookup
|
8
|
+
@vcard = Vcard.find(params[:id])
|
9
|
+
end
|
10
|
+
|
11
|
+
def directory_update
|
12
|
+
@vcard = Vcard.find(params[:id])
|
13
|
+
new_params = params[:vcard].select { |key, _| ['family_name', 'given_name', 'street_address', 'postal_code', 'locality'].include?(key) }
|
14
|
+
|
15
|
+
@vcard.update_attributes(new_params)
|
16
|
+
|
17
|
+
@vcard.save
|
18
|
+
|
19
|
+
redirect_to @vcard.reference
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module HasVcards
|
2
|
+
# View helpers
|
3
|
+
#
|
4
|
+
# These helpers are available by default.
|
5
|
+
module ApplicationHelper
|
6
|
+
def address(vcard, line_separator = '<br/>')
|
7
|
+
vcard.address_lines.map { |line| h(line) }.join(line_separator).html_safe
|
8
|
+
end
|
9
|
+
|
10
|
+
def full_address(vcard, line_separator = '<br/>')
|
11
|
+
vcard.full_address_lines.map { |line| h(line) }.join(line_separator).html_safe
|
12
|
+
end
|
13
|
+
|
14
|
+
def contact(vcard, line_separator = '<br/>', label_separator = ' ')
|
15
|
+
vcard.contact_lines(label_separator).map { |line| h(line) }.join(line_separator).html_safe
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HasVcards
|
2
|
+
class ZipLocalityInput < SimpleForm::Inputs::StringInput
|
3
|
+
def zip_codes
|
4
|
+
SwissMatch.zip_codes.map { |zip| zip.to_s }.inspect
|
5
|
+
end
|
6
|
+
|
7
|
+
def input
|
8
|
+
input_html_options[:type] = 'text'
|
9
|
+
input_html_options[:autocomplete] = 'off'
|
10
|
+
input_html_options['data-provide'] = 'typeahead'
|
11
|
+
input_html_options['data-source'] = zip_codes
|
12
|
+
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module HasVcards
|
4
|
+
class Address < ActiveRecord::Base
|
5
|
+
# Access restrictions
|
6
|
+
attr_accessible :extended_address, :street_address, :post_office_box, :postal_code, :locality, :zip_locality if defined?(ActiveModel::MassAssignmentSecurity)
|
7
|
+
|
8
|
+
belongs_to :vcard
|
9
|
+
|
10
|
+
# Validations
|
11
|
+
include I18nHelpers
|
12
|
+
|
13
|
+
def validate_address
|
14
|
+
errors.add_on_blank(:postal_code)
|
15
|
+
errors.add_on_blank(:locality)
|
16
|
+
|
17
|
+
return unless street_address.blank? && extended_address.blank? && post_office_box.blank?
|
18
|
+
|
19
|
+
errors.add(:street_address, "#{t_attr(:street_address, Vcard)} #{I18n.translate('errors.messages.empty')}")
|
20
|
+
errors.add(:extended_address, "#{t_attr(:extended_address, Vcard)} #{I18n.translate('errors.messages.empty')}")
|
21
|
+
errors.add(:post_office_box, "#{t_attr(:post_office_box, Vcard)} #{I18n.translate('errors.messages.empty')}")
|
22
|
+
end
|
23
|
+
|
24
|
+
# Helpers
|
25
|
+
def to_s
|
26
|
+
I18n.translate('has_vcards.address.to_s',
|
27
|
+
street_address: street_address,
|
28
|
+
postal_code: postal_code,
|
29
|
+
locality: locality
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Composed attributes
|
34
|
+
def zip_locality
|
35
|
+
"#{postal_code} #{locality}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def zip_locality=(value)
|
39
|
+
self.postal_code, self.locality = value.split(' ', 2)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module HasVcards
|
2
|
+
module Concerns
|
3
|
+
# ActiveRecord extensions
|
4
|
+
#
|
5
|
+
# Including this module in your ActiveRecord classes sets up associations and helpers to integrate Vcards with that model.
|
6
|
+
#
|
7
|
+
# Use something like this to use it:
|
8
|
+
#
|
9
|
+
# class Something < ActiveRecord::Base
|
10
|
+
# include HasVcards::Concerns::HasVcards
|
11
|
+
# end
|
12
|
+
module HasVcards
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
included do
|
16
|
+
scope :by_name, ->(name) { { include: :vcard, order: 'vcards.full_name', conditions: Vcard.by_name_conditions(name) } }
|
17
|
+
|
18
|
+
# Vcards
|
19
|
+
has_many :vcards, class_name: 'HasVcards::Vcard', as: 'reference', autosave: true, validate: true
|
20
|
+
accepts_nested_attributes_for :vcards
|
21
|
+
|
22
|
+
# Single/Main vcard
|
23
|
+
has_one :vcard, class_name: 'HasVcards::Vcard', as: 'reference', autosave: true, validate: true
|
24
|
+
accepts_nested_attributes_for :vcard
|
25
|
+
|
26
|
+
delegate :full_name, :nickname, :family_name, :given_name, :additional_name, :honorific_prefix, :honorific_suffix, to: :vcard
|
27
|
+
delegate :full_name=, :nickname=, :family_name=, :given_name=, :additional_name=, :honorific_prefix=, :honorific_suffix=, to: :vcard
|
28
|
+
|
29
|
+
def vcard_with_autobuild
|
30
|
+
vcard_without_autobuild || build_vcard
|
31
|
+
end
|
32
|
+
alias_method_chain :vcard, :autobuild
|
33
|
+
|
34
|
+
# Access restrictions
|
35
|
+
if defined?(ActiveModel::MassAssignmentSecurity)
|
36
|
+
attr_accessible :full_name, :nickname, :family_name, :given_name, :honorific_prefix, :honorific_suffix,
|
37
|
+
:vcard_attributes, :vcards_attributes
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Phone Number / Contact model
|
2
|
+
#
|
3
|
+
# This model holds a contact for a Vcard. It is called PhoneNumber in
|
4
|
+
# compliance with the vCard spec, but can hold any kind of contact like mobile
|
5
|
+
# phone, email, or homepage.
|
6
|
+
#
|
7
|
+
# There can be multiple phone numbers be assigned to a vcard, where they are
|
8
|
+
# available as the #contacts association.
|
9
|
+
#
|
10
|
+
# The #label method uses the I18n locales to translate the phone_number_type.
|
11
|
+
# You may add additional ones in under the
|
12
|
+
# 'activerecord.attributes.has_vcards/phone_number.phone_number_type_enum'
|
13
|
+
# scope.
|
14
|
+
module HasVcards
|
15
|
+
class PhoneNumber < ActiveRecord::Base
|
16
|
+
# Access restrictions
|
17
|
+
attr_accessible :phone_number_type, :number if defined?(ActiveModel::MassAssignmentSecurity)
|
18
|
+
|
19
|
+
# Vcard association
|
20
|
+
belongs_to :vcard, inverse_of: :contacts
|
21
|
+
|
22
|
+
# Validation
|
23
|
+
validates_presence_of :number
|
24
|
+
|
25
|
+
# phone number types
|
26
|
+
scope :by_type, ->(value) { where(phone_number_type: value) }
|
27
|
+
scope :phone, by_type('phone')
|
28
|
+
scope :fax, by_type('fax')
|
29
|
+
scope :mobile, by_type('mobile')
|
30
|
+
scope :email, by_type('email')
|
31
|
+
|
32
|
+
def label
|
33
|
+
I18n.translate(phone_number_type, scope: 'activerecord.attributes.has_vcards/phone_number.phone_number_type_enum', default: phone_number_type.titleize)
|
34
|
+
end
|
35
|
+
|
36
|
+
# String
|
37
|
+
def to_s(format = :default, separator = ': ')
|
38
|
+
case format
|
39
|
+
when :label
|
40
|
+
return [label, number].compact.join(separator)
|
41
|
+
else
|
42
|
+
return number
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate a contact URL
|
47
|
+
def to_url
|
48
|
+
scheme =
|
49
|
+
case phone_number_type
|
50
|
+
when 'phone' then 'tel'
|
51
|
+
when 'mobile' then 'tel'
|
52
|
+
when 'fax' then 'fax'
|
53
|
+
when 'email' then 'mailto'
|
54
|
+
end
|
55
|
+
|
56
|
+
return unless scheme
|
57
|
+
|
58
|
+
"#{scheme}:#{number}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Vcard class
|
4
|
+
#
|
5
|
+
# This is the main model containing vcards information. It can be assigned to
|
6
|
+
# any kind of model by assigning the 'reference'.
|
7
|
+
#
|
8
|
+
# To include in a model, you may use the 'has_vcards' helper:
|
9
|
+
#
|
10
|
+
# class Something < ActiveRecord::Base
|
11
|
+
# has_vcards
|
12
|
+
# end
|
13
|
+
module HasVcards
|
14
|
+
class Vcard < ActiveRecord::Base
|
15
|
+
# Reference
|
16
|
+
belongs_to :reference, polymorphic: true
|
17
|
+
|
18
|
+
# Addresses
|
19
|
+
has_one :address, autosave: true, validate: true
|
20
|
+
has_many :addresses, autosave: true, validate: true
|
21
|
+
|
22
|
+
accepts_nested_attributes_for :addresses
|
23
|
+
accepts_nested_attributes_for :address
|
24
|
+
|
25
|
+
delegate :post_office_box, :extended_address, :street_address, :locality, :region, :postal_code, :country_name, :zip_locality, to: :address
|
26
|
+
delegate :post_office_box=, :extended_address=, :street_address=, :locality=, :region=, :postal_code=, :country_name=, :zip_locality=, to: :address
|
27
|
+
|
28
|
+
def address_with_autobuild
|
29
|
+
address_without_autobuild || build_address
|
30
|
+
end
|
31
|
+
alias_method_chain :address, :autobuild
|
32
|
+
|
33
|
+
# Contacts
|
34
|
+
has_many :contacts, class_name: 'PhoneNumber', inverse_of: :vcard do
|
35
|
+
def build_defaults
|
36
|
+
# TODO: i18nify
|
37
|
+
['Tel. geschäft', 'Tel. privat', 'Handy', 'E-Mail'].each do |phone_number_type|
|
38
|
+
next if select { |contact| contact.phone_number_type == phone_number_type }.present?
|
39
|
+
build(phone_number_type: phone_number_type)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
accepts_nested_attributes_for :contacts,
|
45
|
+
reject_if: proc { |attributes| attributes['number'].blank? },
|
46
|
+
allow_destroy: true
|
47
|
+
|
48
|
+
# Access restrictions
|
49
|
+
if defined?(ActiveModel::MassAssignmentSecurity)
|
50
|
+
attr_accessible :full_name, :nickname, :address_attributes, :family_name, :given_name,
|
51
|
+
:post_office_box, :extended_address, :street_address, :locality, :region,
|
52
|
+
:postal_code, :country_name, :zip_locality, :contacts_attributes,
|
53
|
+
:honorific_prefix, :honorific_suffix
|
54
|
+
end
|
55
|
+
|
56
|
+
# SwissMatch
|
57
|
+
include Vcard::DirectoryLookup
|
58
|
+
|
59
|
+
scope :active, conditions: { active: true }
|
60
|
+
scope :by_name, ->(name) { { conditions: by_name_conditions(name) } }
|
61
|
+
scope :with_address, joins(:address).includes(:address)
|
62
|
+
|
63
|
+
# Validations
|
64
|
+
include I18nHelpers
|
65
|
+
|
66
|
+
def validate_name
|
67
|
+
return if full_name.present?
|
68
|
+
|
69
|
+
errors.add(:full_name, "#{t_attr(:full_name, Vcard)} #{I18n.translate('errors.messages.empty')}")
|
70
|
+
errors.add(:family_name, "#{t_attr(:family_name, Vcard)} #{I18n.translate('errors.messages.empty')}")
|
71
|
+
errors.add(:given_name, "#{t_attr(:given_name, Vcard)} #{I18n.translate('errors.messages.empty')}")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Convenience accessors
|
75
|
+
def full_name
|
76
|
+
result = read_attribute(:full_name)
|
77
|
+
result ||= [ family_name, given_name ].compact.join(' ')
|
78
|
+
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
def abbreviated_name
|
83
|
+
return read_attribute(:full_name) if read_attribute(:full_name)
|
84
|
+
|
85
|
+
[given_name.try(:first).try(:upcase), family_name].compact.join('. ')
|
86
|
+
end
|
87
|
+
|
88
|
+
# Advanced finders
|
89
|
+
def self.by_name_conditions(name)
|
90
|
+
['vcards.full_name LIKE :name OR vcards.family_name LIKE :name OR vcards.given_name LIKE :name OR vcards.nickname LIKE :name', { name: name }]
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.find_by_name(name)
|
94
|
+
find :first, conditions: by_name_conditions(name)
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.find_all_by_name(name)
|
98
|
+
find :all, conditions: by_name_conditions(name)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Helper methods
|
102
|
+
def address_lines
|
103
|
+
lines = [extended_address, street_address, post_office_box, "#{postal_code} #{locality}"]
|
104
|
+
|
105
|
+
# Only return non-empty lines
|
106
|
+
lines.reject(&:blank?).compact.map(&:strip)
|
107
|
+
end
|
108
|
+
|
109
|
+
def full_address_lines
|
110
|
+
lines = [honorific_prefix, full_name] + address_lines
|
111
|
+
|
112
|
+
# Only return non-empty lines
|
113
|
+
lines.reject(&:blank?).compact.map(&:strip)
|
114
|
+
end
|
115
|
+
|
116
|
+
def contact_lines
|
117
|
+
lines = contacts
|
118
|
+
|
119
|
+
# Only return non-empty lines
|
120
|
+
lines.map(&:to_s).reject(&:blank?).compact.map(&:strip)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|