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.
Files changed (105) hide show
  1. data/MIT-LICENSE +4 -1
  2. data/README.md +18 -0
  3. data/Rakefile +28 -0
  4. data/app/assets/javascripts/has_vcards/application.js +13 -0
  5. data/app/assets/stylesheets/has_vcards/application.css +15 -0
  6. data/app/controllers/has_vcards/directory_lookup_controller.rb +21 -0
  7. data/app/controllers/has_vcards/phone_numbers_controller.rb +16 -0
  8. data/app/controllers/has_vcards/vcards_controller.rb +22 -0
  9. data/app/helpers/has_vcards/application_helper.rb +18 -0
  10. data/app/input/has_vcards/zip_locality_input.rb +16 -0
  11. data/app/models/has_vcards/address.rb +42 -0
  12. data/app/models/has_vcards/concerns/has_vcards.rb +42 -0
  13. data/app/models/has_vcards/phone_number.rb +61 -0
  14. data/app/models/has_vcards/vcard.rb +123 -0
  15. data/app/models/has_vcards/vcard/directory_address.rb +25 -0
  16. data/app/models/has_vcards/vcard/directory_lookup.rb +123 -0
  17. data/app/views/has_vcards/layouts/has_vcards/application.html.erb +14 -0
  18. data/app/views/{people → has_vcards/people}/_address_form.html.haml +1 -1
  19. data/app/views/{phone_numbers → has_vcards/phone_numbers}/_form.html.haml +0 -0
  20. data/app/views/{phone_numbers → has_vcards/phone_numbers}/_list.html.haml +0 -0
  21. data/app/views/{phone_numbers → has_vcards/phone_numbers}/_nested_form.html.haml +0 -0
  22. data/app/views/{phone_numbers → has_vcards/phone_numbers}/_phone_number.html.haml +0 -0
  23. data/app/views/{vcards → has_vcards/vcards}/_address.html.haml +0 -0
  24. data/app/views/{vcards → has_vcards/vcards}/_directory_lookup.html.haml +0 -0
  25. data/app/views/{vcards → has_vcards/vcards}/_directory_matches.html.haml +0 -0
  26. data/app/views/{vcards → has_vcards/vcards}/_form.html.haml +1 -1
  27. data/app/views/{vcards → has_vcards/vcards}/_show.html.haml +1 -1
  28. data/app/views/{vcards → has_vcards/vcards}/directory_lookup.js.erb +0 -0
  29. data/config/locales/de.yml +11 -18
  30. data/config/locales/en.yml +21 -6
  31. data/config/locales/fr.yml +6 -18
  32. data/config/routes.rb +1 -1
  33. data/db/migrate/20121113120000_create_honorific_prefixes_table.rb +1 -1
  34. data/db/migrate/20140617113710_rename_tables_for_namespacing.rb +8 -0
  35. data/db/migrate/20140619101337_remove_object_from_phone_number.rb +6 -0
  36. data/db/migrate/20140619102041_rename_vcard_object_to_reference.rb +6 -0
  37. data/db/migrate/20140619121756_refactor_honorific_prefix.rb +5 -0
  38. data/lib/has_vcards.rb +5 -18
  39. data/lib/has_vcards/engine.rb +29 -0
  40. data/lib/has_vcards/version.rb +4 -1
  41. data/spec/controllers/has_vcards/phone_numbers_controller_spec.rb +42 -0
  42. data/spec/controllers/has_vcards/vcards_controller_spec.rb +42 -0
  43. data/spec/dummy/README.rdoc +28 -0
  44. data/spec/dummy/Rakefile +6 -0
  45. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  46. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  47. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  48. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  49. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  50. data/spec/dummy/bin/bundle +3 -0
  51. data/spec/dummy/bin/rails +4 -0
  52. data/spec/dummy/bin/rake +4 -0
  53. data/spec/dummy/config.ru +4 -0
  54. data/spec/dummy/config/application.rb +29 -0
  55. data/spec/dummy/config/boot.rb +5 -0
  56. data/spec/dummy/config/database.yml +37 -0
  57. data/spec/dummy/config/environment.rb +5 -0
  58. data/spec/dummy/config/environments/development.rb +37 -0
  59. data/spec/dummy/config/environments/production.rb +83 -0
  60. data/spec/dummy/config/environments/test.rb +39 -0
  61. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  62. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  63. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  64. data/spec/dummy/config/initializers/inflections.rb +16 -0
  65. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  66. data/spec/dummy/config/initializers/session_store.rb +3 -0
  67. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  68. data/spec/dummy/config/locales/en.yml +23 -0
  69. data/spec/dummy/config/routes.rb +4 -0
  70. data/spec/dummy/config/secrets.yml +22 -0
  71. data/spec/dummy/db/development.sqlite3 +0 -0
  72. data/spec/dummy/db/migrate/20140620151641_create_somethings.rb +7 -0
  73. data/spec/dummy/db/schema.rb +66 -0
  74. data/spec/dummy/db/test.sqlite3 +0 -0
  75. data/spec/dummy/log/development.log +2513 -0
  76. data/spec/dummy/log/test.log +11671 -0
  77. data/spec/dummy/public/404.html +67 -0
  78. data/spec/dummy/public/422.html +67 -0
  79. data/spec/dummy/public/500.html +66 -0
  80. data/spec/dummy/public/favicon.ico +0 -0
  81. data/spec/factories/addresses.rb +7 -0
  82. data/spec/factories/phone_numbers.rb +7 -0
  83. data/spec/factories/vcards.rb +5 -0
  84. data/spec/models/has_vcards/address_spec.rb +58 -0
  85. data/spec/models/has_vcards/concerns/has_vcards_spec.rb +50 -0
  86. data/spec/models/has_vcards/phone_number_spec.rb +82 -0
  87. data/spec/models/has_vcards/vcard_spec.rb +172 -0
  88. data/spec/rails_helper.rb +18 -0
  89. data/spec/spec_helper.rb +74 -0
  90. metadata +262 -47
  91. data/README.markdown +0 -14
  92. data/app/controllers/directory_lookup_controller.rb +0 -17
  93. data/app/controllers/phone_numbers_controller.rb +0 -14
  94. data/app/controllers/vcards_controller.rb +0 -20
  95. data/app/helpers/has_vcards_helper.rb +0 -13
  96. data/app/input/zip_locality_input.rb +0 -14
  97. data/app/models/address.rb +0 -40
  98. data/app/models/honorific_prefix.rb +0 -6
  99. data/app/models/phone_number.rb +0 -43
  100. data/app/models/vcard.rb +0 -112
  101. data/app/models/vcard/directory_address.rb +0 -21
  102. data/app/models/vcard/directory_lookup.rb +0 -122
  103. data/config/application.rb +0 -19
  104. data/config/boot.rb +0 -6
  105. data/lib/has_vcards/railtie.rb +0 -19
data/README.markdown DELETED
@@ -1,14 +0,0 @@
1
- has_vcards
2
- ==========
3
-
4
- Rails plugin providing VCard like contact and address models and helpers.
5
-
6
-
7
- License
8
- =======
9
-
10
- Copyright (c) 2007-2010 Simon Hürlimann <simon.huerlimann@cyt.ch>
11
- Copyright (c) 2008 Agrabah <http://www.agrabah.ch>
12
- Copyright (c) 2007-2010 ZytoLabor <http://www.zyto-labor.com>
13
-
14
- Released under the MIT license.
@@ -1,17 +0,0 @@
1
- class DirectoryLookupController < ApplicationController
2
- def search
3
- @selector = params[:selector]
4
- vcard_params = extract_vcard_params(params, @selector)
5
-
6
- @vcard = Vcard.new(vcard_params)
7
- render 'vcards/directory_lookup'
8
- end
9
-
10
- private
11
- def extract_vcard_params(params, selector)
12
- keys = selector.delete(']').split('[');
13
- vcard_params = params
14
- keys.each {|key| vcard_params = vcard_params[key]}
15
- return vcard_params
16
- end
17
- end
@@ -1,14 +0,0 @@
1
- class PhoneNumbersController < ApplicationController
2
- # User inherited resources logic
3
- inherit_resources
4
- respond_to :html, :js
5
-
6
- # Relations
7
- optional_belongs_to :vcard
8
-
9
- # Optionally support in place editing
10
- if respond_to? :in_place_edit_for
11
- in_place_edit_for :phone_number, :phone_number_type
12
- in_place_edit_for :phone_number, :number
13
- end
14
- end
@@ -1,20 +0,0 @@
1
- class VcardsController < ApplicationController
2
- # User inherited resources logic
3
- inherit_resources
4
- respond_to :html, :js
5
-
6
- def directory_lookup
7
- @vcard = Vcard.find(params[:id])
8
- end
9
-
10
- def directory_update
11
- @vcard = Vcard.find(params[:id])
12
- new_params = params[:vcard].select{|key, value| ['family_name', 'given_name', 'street_address', 'postal_code', 'locality'].include?(key)}
13
-
14
- @vcard.update_attributes(new_params)
15
-
16
- @vcard.save
17
-
18
- redirect_to @vcard.object
19
- end
20
- end
@@ -1,13 +0,0 @@
1
- module HasVcardsHelper
2
- def address(vcard, line_separator = '<br/>')
3
- vcard.address_lines.map{|line| h(line)}.join(line_separator).html_safe
4
- end
5
-
6
- def full_address(vcard, line_separator = '<br/>')
7
- vcard.full_address_lines.map{|line| h(line)}.join(line_separator).html_safe
8
- end
9
-
10
- def contact(vcard, line_separator = '<br/>', label_separator = ' ')
11
- vcard.contact_lines(label_separator).map{|line| h(line)}.join(line_separator).html_safe
12
- end
13
- end
@@ -1,14 +0,0 @@
1
- class ZipLocalityInput < SimpleForm::Inputs::StringInput
2
- def zip_codes
3
- SwissMatch.zip_codes.map{|zip| zip.to_s}.inspect
4
- end
5
-
6
- def input
7
- input_html_options[:type] = 'text'
8
- input_html_options[:autocomplete] = 'off'
9
- input_html_options['data-provide'] = 'typeahead'
10
- input_html_options['data-source'] = zip_codes
11
-
12
- super
13
- end
14
- end
@@ -1,40 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class Address < ActiveRecord::Base
4
- # Access restrictions
5
- attr_accessible :extended_address, :street_address, :post_office_box, :postal_code, :locality, :zip_locality
6
-
7
- belongs_to :vcard
8
-
9
- # Validations
10
- include I18nHelpers
11
-
12
- def validate_address
13
- errors.add_on_blank(:postal_code)
14
- errors.add_on_blank(:locality)
15
-
16
- if street_address.blank? and extended_address.blank? and post_office_box.blank?
17
- errors.add(:street_address, "#{t_attr(:street_address, Vcard)} #{I18n.translate('errors.messages.empty')}")
18
- errors.add(:extended_address, "#{t_attr(:extended_address, Vcard)} #{I18n.translate('errors.messages.empty')}")
19
- errors.add(:post_office_box, "#{t_attr(:post_office_box, Vcard)} #{I18n.translate('errors.messages.empty')}")
20
- end
21
- end
22
-
23
- # Helpers
24
- def to_s
25
- I18n.translate('has_vcards.address.to_s',
26
- :street_address => street_address,
27
- :postal_code => postal_code,
28
- :locality => locality
29
- )
30
- end
31
-
32
- # Composed attributes
33
- def zip_locality
34
- "#{postal_code} #{locality}"
35
- end
36
-
37
- def zip_locality=(value)
38
- self.postal_code, self.locality = value.split(' ', 2)
39
- end
40
- end
@@ -1,6 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class HonorificPrefix < ActiveRecord::Base
4
- # Access restrictions
5
- attr_accessible :sex, :position, :name
6
- end
@@ -1,43 +0,0 @@
1
- class PhoneNumber < ActiveRecord::Base
2
- # Access restrictions
3
- attr_accessible :phone_number_type, :number
4
-
5
- # Vcard association
6
- belongs_to :vcard, :inverse_of => :contacts
7
- belongs_to :object, :polymorphic => true
8
-
9
- # Validation
10
- validates_presence_of :number
11
-
12
- # phone number types
13
- scope :by_type, lambda {|value| where(:phone_number_type => value)}
14
- scope :phone, by_type('phone')
15
- scope :fax, by_type('fax')
16
- scope :mobile, by_type('mobile')
17
- scope :email, by_type('email')
18
-
19
- def label
20
- case phone_number_type
21
- when 'phone'
22
- "Tel:"
23
- when 'fax'
24
- "Fax:"
25
- when 'mobile'
26
- "Mob:"
27
- when 'email'
28
- "Mail:"
29
- else
30
- phone_number_type
31
- end
32
- end
33
-
34
- # String
35
- def to_s(separator = " ", format = :default)
36
- case format
37
- when :label
38
- return [label, number].compact.join(separator)
39
- else
40
- return number
41
- end
42
- end
43
- end
data/app/models/vcard.rb DELETED
@@ -1,112 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module HasAddress
4
- def self.included(base)
5
- base.alias_method_chain :address, :autobuild
6
- end
7
-
8
- def address_with_autobuild
9
- address_without_autobuild || build_address
10
- end
11
- end
12
-
13
- class Vcard < ActiveRecord::Base
14
- # Access restrictions
15
- attr_accessible :full_name, :honorific_prefix, :family_name, :given_name
16
-
17
- has_one :address, :autosave => true, :validate => true
18
- accepts_nested_attributes_for :address
19
- attr_accessible :address_attributes
20
- delegate :post_office_box, :extended_address, :street_address, :locality, :region, :postal_code, :country_name, :zip_locality, :to => :address
21
- delegate :post_office_box=, :extended_address=, :street_address=, :locality=, :region=, :postal_code=, :country_name=, :zip_locality=, :to => :address
22
- attr_accessible :post_office_box, :extended_address, :street_address, :locality, :region, :postal_code, :country_name, :zip_locality
23
- include HasAddress
24
-
25
- has_many :addresses, :autosave => true, :validate => true
26
- accepts_nested_attributes_for :addresses
27
-
28
- scope :active, :conditions => {:active => true}
29
- scope :by_name, lambda {|name| {:conditions => self.by_name_conditions(name)}}
30
- scope :with_address, joins(:address).includes(:address)
31
-
32
- belongs_to :object, :polymorphic => true
33
-
34
- # Validations
35
- include I18nHelpers
36
-
37
- def validate_name
38
- if full_name.blank?
39
- errors.add(:full_name, "#{t_attr(:full_name, Vcard)} #{I18n.translate('errors.messages.empty')}")
40
- errors.add(:family_name, "#{t_attr(:family_name, Vcard)} #{I18n.translate('errors.messages.empty')}")
41
- errors.add(:given_name, "#{t_attr(:given_name, Vcard)} #{I18n.translate('errors.messages.empty')}")
42
- end
43
- end
44
-
45
- # Convenience accessors
46
- def full_name
47
- result = read_attribute(:full_name)
48
- result ||= [ family_name, given_name ].compact.join(' ')
49
-
50
- return result
51
- end
52
-
53
- def abbreviated_name
54
- return read_attribute(:full_name) if read_attribute(:full_name)
55
-
56
- [given_name.try(:first).try(:upcase), family_name].compact.join(". ")
57
- end
58
-
59
- # Advanced finders
60
- def self.by_name_conditions(name)
61
- ['vcards.full_name LIKE :name OR vcards.family_name LIKE :name OR vcards.given_name LIKE :name OR vcards.nickname LIKE :name', {:name => name}]
62
- end
63
-
64
- def self.find_by_name(name)
65
- self.find(:first, :conditions => self.by_name_conditions(name))
66
- end
67
-
68
- def self.find_all_by_name(name)
69
- self.find(:all, :conditions => self.by_name_conditions(name))
70
- end
71
-
72
- # Helper methods
73
- def address_lines
74
- lines = [extended_address, street_address, post_office_box, "#{postal_code} #{locality}"]
75
-
76
- # Only return non-empty lines
77
- lines.map {|line| line.strip unless (line.nil? or line.strip.empty?)}.compact
78
- end
79
-
80
- def full_address_lines
81
- lines = [honorific_prefix, full_name] + address_lines
82
-
83
- # Only return non-empty lines
84
- lines.map {|line| line.strip unless (line.nil? or line.strip.empty?)}.compact
85
- end
86
-
87
- def contact_lines(separator = " ")
88
- lines = contacts.map{|p| p.to_s unless (p.number.nil? or p.number.strip.empty?)}
89
-
90
- # Only return non-empty lines
91
- lines.map {|line| line.strip unless (line.nil? or line.strip.empty?)}.compact
92
- end
93
-
94
- # Phone numbers
95
- has_many :contacts, :class_name => 'PhoneNumber', :as => :object, :inverse_of => :vcard do
96
- def build_defaults
97
- ['Tel. geschäft', 'Tel. privat', 'Handy', 'E-Mail'].map{ |phone_number_type|
98
- build(:phone_number_type => phone_number_type) unless exists?(:phone_number_type => phone_number_type)
99
- }
100
- end
101
- end
102
- accepts_nested_attributes_for :contacts, :reject_if => proc {|attributes| attributes['number'].blank? }, :allow_destroy => true
103
- attr_accessible :contacts_attributes
104
-
105
- # Salutation
106
- def salutation
107
- I18n.translate(honorific_prefix.presence || :default, :scope => 'salutation', :family_name => family_name, :default => :default)
108
- end
109
-
110
- # SwissMatch
111
- include Vcard::DirectoryLookup
112
- end
@@ -1,21 +0,0 @@
1
- module Vcard::DirectoryAddress
2
- def to_vcard
3
- Vcard.new(
4
- :given_name => first_name,
5
- :family_name => family_name,
6
- :street_address => street,
7
- :postal_code => zip_code,
8
- :locality => city
9
- )
10
- end
11
-
12
- def to_vcard_attributes
13
- {
14
- :given_name => first_name,
15
- :family_name => family_name,
16
- :street_address => street,
17
- :postal_code => zip_code,
18
- :locality => city
19
- }
20
- end
21
- end
@@ -1,122 +0,0 @@
1
- module Vcard::DirectoryLookup
2
- def map_for_directory
3
- search = {}
4
- search[:first_name] = given_name
5
- search[:family_name] = family_name
6
- search[:street] = street_address
7
- search[:zip_code] = postal_code
8
- search[:city] = locality
9
-
10
- search
11
- end
12
-
13
- def directory_lookup(ignore_fields = [])
14
- search = map_for_directory
15
-
16
- search.reject!{|key, value| ignore_fields.include? key}
17
-
18
- # TODO:
19
- # We should fetch additional pages if the result indicates there
20
- # are more pages.
21
- ::SwissMatch.directory_service.addresses(search, :per_page => 10)
22
- end
23
-
24
- def directory_found?(ignore_fields = [])
25
- directory_lookup(ignore_fields).present?
26
- end
27
-
28
- def normalize(value)
29
- normalized_value = UnicodeUtils.downcase(value.to_s)
30
- normalized_value.gsub!(/str\./, 'strasse')
31
- normalized_value.gsub!(/str([ $])/, 'strasse\1')
32
-
33
- normalized_value
34
- end
35
-
36
- def directory_matches(ignore_lookup_fields = [])
37
- matches = directory_lookup(ignore_lookup_fields)
38
- matches.map do |match|
39
- search = map_for_directory
40
-
41
- perfect = []
42
- search.each do |key, value|
43
- perfect << key if normalize(match.send(key)) == normalize(value)
44
- end
45
-
46
- partial = []
47
- search.each do |key, value|
48
- partial << key if value.present? && normalize(match.send(key)).include?(normalize(value))
49
- end
50
- partial -= perfect
51
-
52
- bad = search.keys - perfect - partial - ignore_lookup_fields
53
-
54
- {:address => match, :perfect => perfect, :partial => partial, :bad => bad, :ignore => ignore_lookup_fields}
55
- end
56
- end
57
-
58
- def directory_filter(match, filter = {})
59
- # Exact filters
60
- filter[:perfect].each do |field|
61
- return false unless match[:perfect].include?(field)
62
- end if filter[:perfect]
63
- filter[:partial].each do |field|
64
- return false unless match[:partial].include?(field)
65
- end if filter[:partial]
66
- filter[:bad].each do |field|
67
- return false unless match[:bad].include?(field)
68
- end if filter[:bad]
69
-
70
- # "Better than" filters
71
- filter[:partial_or_perfect].each do |field|
72
- return false unless match[:perfect].include?(field) || match[:partial].include?(field)
73
- end if filter[:partial_or_perfect]
74
-
75
- return true
76
- end
77
-
78
- def filtered_matches(filters)
79
- ignores = filters.delete(:ignore) || []
80
-
81
- directory_matches(ignores).collect do |match|
82
- match[:address] if directory_filter(match, filters)
83
- end.compact
84
- end
85
-
86
- # Everything matches
87
- def perfect_matches
88
- filtered_matches(:perfect => [:family_name, :first_name, :street, :city])
89
- end
90
-
91
- def perfect_match?
92
- perfect_matches.present?
93
- end
94
-
95
- # Everything is found, given or family name does is partial match
96
- def great_matches
97
- filtered_matches(:partial_or_perfect => [:family_name, :first_name], :perfect => [:street, :city])
98
- end
99
-
100
- def great_match?
101
- great_matches.present?
102
- end
103
-
104
- # Everything but given name matches, family name might be partial
105
- def family_name_matches
106
- filtered_matches(:ignore => [:first_name], :partial_or_perfect => [:family_name], :perfect => [:street, :city])
107
- end
108
-
109
- def family_name_match?
110
- family_name_matches.present?
111
- end
112
-
113
- # Same address different names
114
- def address_matches
115
- filtered_matches(:ignore => [:first_name, :family_name], :perfect => [:street, :city])
116
- end
117
-
118
- # Similar name in same locality
119
- def locality_matches
120
- filtered_matches(:ignore => [:first_name, :street], :partial_or_perfect => [:family_name], :perfect => [:city])
121
- end
122
- end