contact_manager 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: da527969ce6fd98138bdef3127469852a8b33c366f380e1a882cb5fdc89f5624
4
+ data.tar.gz: 2ee57d438aed53cc3c21bf0100e9e2826a1e89e64688ef4d7aa837910978c0ea
5
+ SHA512:
6
+ metadata.gz: 85aa0ae30fd630b7a51b3264781e35c1a05aa1d99a394aae054e172097aa389bbfece26bb54c7723c5bc7f37de56511ca0672854ce3f643c23d881342f3c2904
7
+ data.tar.gz: 11d3cf705f2bf0eee3b1de13f8f810f035467fd5342f3681c2fafa0a1c19c29448c43101bdc517553bc862112397c5bfa226ff1926838eeb2b2cadf3f41a1945
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2022 Andy Maleh
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # <img src="https://raw.githubusercontent.com/AndyObtiva/contact_manager/master/icons/linux/Contact%20Manager.png" height=85 /> Contact Manager 1.0.0
2
+ ## [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-swt/master/images/glimmer-logo-hi-res.png" height=40 /> Glimmer Application](https://github.com/AndyObtiva/glimmer-dsl-swt)
3
+ [![Gem Version](https://badge.fury.io/rb/contact_manager.svg)](http://badge.fury.io/rb/contact_manager)
4
+
5
+ Contact Manager is a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) sample leveraging [SQLite DB](https://www.sqlite.org/index.html) via [ActiveRecord](https://rubygems.org/gems/activerecord).
6
+
7
+ ![Contact Manager Screenshot](/screenshots/contact-manager.gif)
8
+
9
+ ## Setup
10
+
11
+ ### Option 1: Install Native Executable Package
12
+
13
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/contact_manager/master/icons/linux/Contact%20Manager.png" height=40 /> Contact Manager 1.0.0 (Mac Catalina 10.15.7 x86_64 DMG)](https://www.dropbox.com/s/swc0jl7joy29m84/Contact%20Manager-1.0.0-x64-catalina-10.15.7.dmg?dl=1)
14
+
15
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/contact_manager/master/icons/linux/Contact%20Manager.png" height=40 /> Contact Manager 1.0.0 (Windows 10.0.19043 x86_64 MSI)](https://www.dropbox.com/s/tan8hbv3qk959t7/Contact%20Manager-1.0.0-x64-Windows-10.0.19043.msi?dl=1)
16
+
17
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/contact_manager/master/icons/linux/Contact%20Manager.png" height=40 /> Contact Manager 1.0.0 (Linux x86_64 DEB)](https://www.dropbox.com/s/v6pckxcpw4quafx/contact-manager_1.0.0-1_amd64.deb?dl=1)
18
+
19
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/contact_manager/master/icons/linux/Contact%20Manager.png" height=40 /> Contact Manager 1.0.0 (Linux x86_64 RPM)](https://www.dropbox.com/s/4dby42ux7ebyv6n/contact-manager-1.0.0-1.x86_64.rpm?dl=1)
20
+
21
+ ### Option 2: Install Ruby Gem
22
+
23
+ Start by setting up [JDK 18](https://www.oracle.com/java/technologies/downloads) & [JRuby 9.3.3.0](https://www.jruby.org/) (+ [RVM](http://rvm.io/) on Mac/Linux) just as per [Glimmer DSL for SWT prerequisites](https://github.com/AndyObtiva/glimmer-dsl-swt#pre-requisites).
24
+
25
+ Install Ruby gem:
26
+
27
+ ```
28
+ gem install contact_manager -v1.0.0
29
+ ```
30
+
31
+ Run:
32
+
33
+ ```
34
+ contact_manager
35
+ ```
36
+
37
+ ### Option 3: Clone Project Locally
38
+
39
+ Start by setting up [JDK 18](https://www.oracle.com/java/technologies/downloads) & [JRuby 9.3.3.0](https://www.jruby.org/) (+ [RVM](http://rvm.io/) on Mac/Linux) just as per [Glimmer DSL for SWT prerequisites](https://github.com/AndyObtiva/glimmer-dsl-swt#pre-requisites).
40
+
41
+ Clone:
42
+
43
+ ```
44
+ git clone https://github.com/AndyObtiva/contact_manager.git
45
+ ```
46
+
47
+ Change directory:
48
+
49
+ ```
50
+ cd contact_manager
51
+ ```
52
+
53
+ Bundle:
54
+
55
+ ```
56
+ bundle
57
+ ```
58
+
59
+ Run:
60
+
61
+ ```
62
+ glimmer run
63
+ ```
64
+
65
+ Or run with the binary script:
66
+
67
+ ```
68
+ bin/contact_manager
69
+ ```
70
+
71
+ You can [package a native executable](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md) DMG on Mac:
72
+
73
+ ```
74
+ glimmer "package[dmg]"
75
+ ```
76
+
77
+ You can [package a native executable](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md) MSI on Windows (assuming you followed [MSI related setup instructions](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md)):
78
+
79
+ ```
80
+ glimmer "package[msi]"
81
+ ```
82
+
83
+ You can [package a native executable](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md) DEB on debian-based Linux distros:
84
+
85
+ ```
86
+ glimmer "package[deb]"
87
+ ```
88
+
89
+ You can [package a native executable](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md) RPM on redhat-based Linux distros:
90
+
91
+ ```
92
+ glimmer "package[rpm]"
93
+ ```
94
+
95
+ ## Software Architecture & Design
96
+
97
+ Contact Manager follows the [Model-View-Presenter](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter) flavor of [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller), so the View communicates to the Model via a Presenter, which is an enhanced Controller that enables bidirectional attribute data-binding between the Model and the View.
98
+
99
+ The View uses `contact_form`, `contact_table`, and `contact_manager_menu_bar` custom widgets (components)
100
+
101
+ The Model layer includes a `Contact` and `ContactRepository` ([DDD Repository Pattern](https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf)) in addition to `ContactPresenter` (which is both a Controller and a Model at a higher level).
102
+
103
+ `Contact` follows the [Active Record Pattern](https://en.wikipedia.org/wiki/Active_record_pattern) for [Object Relational Mapping](https://en.wikipedia.org/wiki/Object%E2%80%93relational_mapping) to store objects in a [SQLite](https://www.sqlite.org/index.html) relational database.
104
+
105
+ The Contact Manager graphical user interface leverages the [Master-Detail Interface Pattern](https://en.wikipedia.org/wiki/Master%E2%80%93detail_interface).
106
+
107
+ The database is stored at `~/db/
108
+
109
+ ## TODO
110
+
111
+ [TODO.md](TODO.md)
112
+
113
+ ## Change Log
114
+
115
+ [CHANGELOG.md](CHANGELOG.md)
116
+
117
+ ## Contributing
118
+
119
+ - Check out the latest master to make sure the feature hasn't been
120
+ implemented or the bug hasn't been fixed yet.
121
+ - Check out the issue tracker to make sure someone already hasn't
122
+ requested it and/or contributed it.
123
+ - Fork the project.
124
+ - Start a feature/bugfix branch.
125
+ - Commit and push until you are happy with your contribution.
126
+ - Make sure to add tests for it. This is important so I don't break it
127
+ in a future version unintentionally.
128
+ - Please try not to mess with the Rakefile, version, or history. If
129
+ you want to have your own version, or is otherwise necessary, that
130
+ is fine, but please isolate to its own commit so I can cherry-pick
131
+ around it.
132
+
133
+ ## Copyright
134
+
135
+ [MIT](LICENSE.txt)
136
+
137
+ Copyright (c) 2022 Andy Maleh. See [LICENSE.txt](LICENSE.txt) for further details.
138
+
139
+ --
140
+
141
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=40 />](https://github.com/AndyObtiva/glimmer) Built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) (JRuby Desktop Development GUI Library)
142
+
143
+ Contact Manager logo was made by [Freepik](https://www.flaticon.com/authors/freepik) from [www.flaticon.com](http://www.flaticon.com)
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,3 @@
1
+ require_relative '../contact_manager'
2
+
3
+ ContactManager::View::AppView.launch
@@ -0,0 +1,39 @@
1
+ require 'db/db'
2
+
3
+ class Contact < ActiveRecord::Base
4
+ STATES = [ 'AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'GA',
5
+ 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME',
6
+ 'MI', 'MN', 'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM',
7
+ 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX',
8
+ 'UT', 'VA', 'VT', 'WA', 'WI', 'WV', 'WY']
9
+ PROVINCES = ['NL', 'PE', 'NS', 'NB', 'QC', 'ON', 'MB', 'SK', 'AB', 'BC', 'YT', 'NT', 'NU']
10
+
11
+ validates :first_name, presence: true
12
+ validates :last_name, presence: true
13
+ validates :email, format: {with: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i, message: 'must be a valid email address'}, allow_blank: true
14
+ validates :phone, format: {with: /\A[(]?[0-9]{3}[)]?[ -\.\/0-9]{7,9}\z/, message: 'must be a valid phone number'}, allow_blank: true
15
+ validates :zip_or_postal_code, format: {with: /\A[a-z0-9][0-9][a-z0-9][ 0-9][a-z0-9][a-z0-9]?[0-9]?\z/i, message: 'must be a valid phone number'}, allow_blank: true
16
+ validate :email_or_phone_is_present
17
+
18
+ def address
19
+ address_fields = [street, city, state_or_province, zip_or_postal_code, country]
20
+ address_fields.map { |field| field.blank? ? nil : field }.compact.join(', ')
21
+ end
22
+
23
+ def country_options
24
+ ['', 'Canada', 'USA']
25
+ end
26
+
27
+ def state_or_province_options
28
+ (PROVINCES + [''] + STATES)
29
+ end
30
+
31
+ private
32
+
33
+ def email_or_phone_is_present
34
+ if email.blank? && phone.blank?
35
+ errors.add(:email, 'must be present unless phone is present')
36
+ errors.add(:phone, 'must be present unless email is present')
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ require 'contact_manager/model/contact'
2
+ require 'contact_manager/model/contact_repository'
3
+
4
+ # ContactPresenter is an enhanced Controller that also
5
+ # enables bidirectional data-binding of contact attributes
6
+ class ContactPresenter
7
+ attr_accessor :contacts, :query, :current_contact
8
+
9
+ def initialize
10
+ renew_current_contact
11
+ self.contacts = ContactRepository.instance.all
12
+
13
+ # Monitor Contact collection changes
14
+ # the after_commit hook executes the block under a different object
15
+ # binding, so we must use `this` to access self
16
+ this = self
17
+ Contact.after_commit(on: [:create, :update, :destroy]) do
18
+ this.contacts = ContactRepository.instance.all
19
+ end
20
+ end
21
+
22
+ def query=(query_value)
23
+ @query = query_value
24
+ self.contacts = ContactRepository.instance.search(query_value)
25
+ end
26
+
27
+ def renew_current_contact
28
+ self.current_contact = Contact.new
29
+ end
30
+
31
+ def save_current_contact
32
+ current_contact.save.tap do |saved|
33
+ renew_current_contact if saved
34
+ end
35
+ end
36
+
37
+ def destroy_current_contact
38
+ if current_contact&.persisted?
39
+ current_contact.destroy
40
+ renew_current_contact
41
+ end
42
+ end
43
+
44
+ def destroy_all_contacts
45
+ ContactRepository.instance.destroy_all_contacts
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ require 'singleton'
2
+ require 'contact_manager/model/contact'
3
+
4
+ class ContactRepository
5
+ include Singleton
6
+
7
+ def all
8
+ Contact.all
9
+ end
10
+
11
+ def search(query_value)
12
+ if query_value.present?
13
+ attribute_names = Contact.new.attributes.keys
14
+ conditions = attribute_names.reduce('') do |conditions, attribute|
15
+ if conditions.blank?
16
+ conditions += "lower(#{attribute}) like ?"
17
+ else
18
+ conditions += " OR lower(#{attribute}) like ?"
19
+ end
20
+ end
21
+ Contact.where(conditions, *(["%#{query_value.downcase}%"]*attribute_names.count))
22
+ else
23
+ all
24
+ end
25
+ end
26
+
27
+ def destroy_all_contacts
28
+ Contact.destroy_all
29
+ end
30
+ end
@@ -0,0 +1,70 @@
1
+ require 'contact_manager/model/contact_presenter'
2
+
3
+ require 'contact_manager/view/contact_manager_menu_bar'
4
+ require 'contact_manager/view/contact_form'
5
+ require 'contact_manager/view/contact_table'
6
+
7
+ class ContactManager
8
+ module View
9
+ class AppView
10
+ include Glimmer::UI::Application
11
+
12
+ before_body do
13
+ @contact_presenter = ContactPresenter.new
14
+
15
+ @display = display {
16
+ on_about do
17
+ display_about_dialog
18
+ end
19
+
20
+ on_preferences do
21
+ display_about_dialog
22
+ end
23
+ }
24
+ end
25
+
26
+ body {
27
+ shell {
28
+ grid_layout
29
+
30
+ image File.join(APP_ROOT, 'icons', 'windows', "Contact Manager.ico") if OS.windows?
31
+ image File.join(APP_ROOT, 'icons', 'linux', "Contact Manager.png") unless OS.windows?
32
+ text "Contact Manager"
33
+
34
+ @contact_form = contact_form(contact_presenter: @contact_presenter) {
35
+ layout_data {
36
+ horizontal_alignment :fill
37
+ grab_excess_horizontal_space true
38
+ }
39
+ }
40
+
41
+ contact_table(
42
+ contact_presenter: @contact_presenter,
43
+ reset_validations_action: @contact_form.method(:reset_validations),
44
+ ) {
45
+ layout_data {
46
+ horizontal_alignment :fill
47
+ grab_excess_horizontal_space true
48
+ vertical_alignment :fill
49
+ grab_excess_vertical_space true
50
+ }
51
+ }
52
+
53
+ contact_manager_menu_bar(
54
+ contact_presenter: @contact_presenter,
55
+ about_action: method(:display_about_dialog),
56
+ save_contact_action: @contact_form.method(:save_contact),
57
+ reset_validations_action: @contact_form.method(:reset_validations),
58
+ )
59
+ }
60
+ }
61
+
62
+ def display_about_dialog
63
+ message_box(body_root) {
64
+ text 'About'
65
+ message "Contact Manager #{VERSION}\n\n#{LICENSE}"
66
+ }.open
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,134 @@
1
+ class ContactManager
2
+ module View
3
+ class ContactForm
4
+ include Glimmer::UI::CustomWidget
5
+
6
+ options :contact_presenter
7
+
8
+ body {
9
+ composite {
10
+ grid_layout {
11
+ num_columns 2
12
+ make_columns_equal_width true
13
+ margin_width 0
14
+ margin_height 0
15
+ vertical_spacing 0
16
+ }
17
+
18
+ form_column {
19
+ form_field(:first_name)
20
+ form_field(:last_name)
21
+ form_field(:email)
22
+ form_field(:phone)
23
+ }
24
+
25
+ form_column {
26
+ form_field(:street)
27
+ form_field(:city)
28
+ form_field(:state_or_province, editor: :combo, editor_args: :read_only, property: :selection)
29
+ form_field(:zip_or_postal_code)
30
+ form_field(:country, editor: :combo, editor_args: :read_only, property: :selection)
31
+ }
32
+
33
+ composite { # having a composite ensures padding around button
34
+ layout_data {
35
+ horizontal_span 2
36
+ horizontal_alignment :fill
37
+ grab_excess_horizontal_space true
38
+ }
39
+
40
+ grid_layout {
41
+ margin_height 0
42
+ }
43
+
44
+ button {
45
+ layout_data {
46
+ horizontal_alignment :fill
47
+ grab_excess_horizontal_space true
48
+ }
49
+
50
+ text 'Save Contact'
51
+
52
+ on_widget_selected do
53
+ save_contact
54
+ end
55
+ }
56
+ }
57
+ }
58
+ }
59
+
60
+ def form_column(&content)
61
+ composite { |the_composite|
62
+ layout_data {
63
+ horizontal_alignment :fill
64
+ vertical_alignment :fill
65
+ grab_excess_horizontal_space true
66
+ grab_excess_vertical_space true
67
+ }
68
+
69
+ grid_layout {
70
+ num_columns 2
71
+ make_columns_equal_width false
72
+ }
73
+
74
+ content.call(the_composite)
75
+ }
76
+ end
77
+
78
+ def form_field(field, editor: :text, editor_args: [], property: :text)
79
+ @form_field_labels ||= {}
80
+ @form_field_labels[field] = label {
81
+ layout_data {
82
+ width_hint 120
83
+ }
84
+
85
+ text field.to_s.gsub('_', ' ').titlecase
86
+ }
87
+
88
+ @form_field_texts ||= {}
89
+ @form_field_texts[field] = send(editor, *editor_args) {
90
+ layout_data {
91
+ width_hint 150
92
+ horizontal_alignment :fill
93
+ grab_excess_horizontal_space true
94
+ }
95
+
96
+ # use nested data-binding to monitor change of contact
97
+ # in addition to contact field
98
+ send(property) <=> [contact_presenter, "current_contact.#{field}"]
99
+
100
+ on_key_pressed do |event|
101
+ save_contact if event.keyCode == swt(:cr)
102
+ end
103
+ }
104
+ end
105
+
106
+ def save_contact
107
+ if contact_presenter.save_current_contact
108
+ reset_validations
109
+ else
110
+ show_validations
111
+ end
112
+ end
113
+
114
+ def reset_validations
115
+ contact_presenter.current_contact.attributes.keys.each do |attribute_name|
116
+ @form_field_labels[attribute_name.to_sym]&.foreground = :black
117
+ @form_field_labels[attribute_name.to_sym]&.tool_tip_text = nil
118
+ end
119
+ focus_first_field
120
+ end
121
+
122
+ def show_validations
123
+ contact_presenter.current_contact.errors.errors.each do |error|
124
+ @form_field_labels[error.attribute].foreground = :red
125
+ @form_field_labels[error.attribute].tool_tip_text = error.full_message
126
+ end
127
+ end
128
+
129
+ def focus_first_field
130
+ @form_field_texts[:first_name].set_focus
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,63 @@
1
+ class ContactManager
2
+ module View
3
+ class ContactManagerMenuBar
4
+ include Glimmer::UI::CustomWidget
5
+
6
+ ACCELERATOR_KEY = OS.mac? ? :command : :ctrl
7
+
8
+ options :contact_presenter, :about_action, :save_contact_action, :reset_validations_action
9
+
10
+ body {
11
+ menu_bar {
12
+ menu {
13
+ text '&Contact'
14
+
15
+ menu_item {
16
+ text '&New'
17
+ accelerator ACCELERATOR_KEY, :n
18
+
19
+ on_widget_selected do
20
+ contact_presenter.renew_current_contact
21
+ reset_validations_action.call
22
+ end
23
+ }
24
+
25
+ menu_item {
26
+ text '&Save'
27
+ accelerator ACCELERATOR_KEY, :s
28
+
29
+ on_widget_selected do
30
+ save_contact_action.call
31
+ end
32
+ }
33
+
34
+ menu_item {
35
+ text '&Delete All...'
36
+
37
+ on_widget_selected do
38
+ result = message_box(:yes, :no) {
39
+ text 'Delete All'
40
+ message 'Are you sure you want to delete all your contacts?'
41
+ }.open
42
+ contact_presenter.destroy_all_contacts if result == swt(:yes)
43
+ end
44
+ }
45
+ }
46
+
47
+ menu {
48
+ text '&Help'
49
+
50
+ menu_item {
51
+ text '&About...'
52
+
53
+ on_widget_selected do
54
+ about_action.call
55
+ end
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,73 @@
1
+ class ContactManager
2
+ module View
3
+ class ContactTable
4
+ include Glimmer::UI::CustomWidget
5
+
6
+ options :contact_presenter, :reset_validations_action
7
+
8
+ body {
9
+ composite {
10
+ grid_layout {
11
+ margin_height 0
12
+ }
13
+
14
+ text(:search) {
15
+ layout_data {
16
+ horizontal_alignment :fill
17
+ grab_excess_horizontal_space true
18
+ }
19
+
20
+ text <=> [contact_presenter, :query]
21
+ }
22
+
23
+ table {
24
+ layout_data {
25
+ height_hint 250
26
+ horizontal_alignment :fill
27
+ grab_excess_horizontal_space true
28
+ vertical_alignment :fill
29
+ grab_excess_vertical_space true
30
+ }
31
+
32
+ table_column {
33
+ text 'First Name'
34
+ width 120
35
+ }
36
+ table_column {
37
+ text 'Last Name'
38
+ width 120
39
+ }
40
+ table_column {
41
+ text 'Email'
42
+ width 180
43
+ }
44
+ table_column {
45
+ text 'Phone'
46
+ width 120
47
+ }
48
+ table_column {
49
+ text 'Address'
50
+ width 320
51
+ }
52
+
53
+ # Ensure converting to Array on read because contacts is an ActiveRecord collection,
54
+ # but an Array object is required by Glimmer DSL for SWT table data-binding logic
55
+ items <=> [contact_presenter, :contacts, on_read: :to_a, column_properties: [:first_name, :last_name, :email, :phone, :address]]
56
+
57
+ selection <=> [contact_presenter, :current_contact, after_write: reset_validations_action]
58
+
59
+ menu {
60
+ menu_item {
61
+ text '&Delete'
62
+
63
+ on_widget_selected do
64
+ contact_presenter.destroy_current_contact
65
+ end
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
2
+ $LOAD_PATH.unshift(File.expand_path('../..', __FILE__))
3
+
4
+ begin
5
+ require 'bundler/setup'
6
+ Bundler.require(:default)
7
+ rescue
8
+ # this runs when packaged as a gem (no bundler)
9
+ require 'glimmer-dsl-swt'
10
+ # add more gems if needed
11
+ require 'active_record'
12
+ require 'activerecord-jdbcsqlite3-adapter'
13
+ end
14
+
15
+ class ContactManager
16
+ include Glimmer
17
+
18
+ APP_ROOT = File.expand_path('../..', __FILE__)
19
+ VERSION = File.read(File.join(APP_ROOT, 'VERSION'))
20
+ LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
21
+ Display.app_name = 'Contact Manager'
22
+ Display.app_version = ContactManager::VERSION
23
+ end
24
+
25
+ require 'contact_manager/view/app_view'
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env jruby
2
+
3
+ runner = File.expand_path('../../app/contact_manager/launch.rb', __FILE__)
4
+
5
+ # Detect if inside a JAR file or not
6
+ if runner.include?('uri:classloader')
7
+ require runner
8
+ else
9
+ require 'glimmer/launcher'
10
+
11
+ launcher = Glimmer::Launcher.new([runner] + ARGV)
12
+ launcher.launch
13
+ end
data/config/warble.rb ADDED
@@ -0,0 +1,183 @@
1
+
2
+ # Disable Rake-environment-task framework detection by uncommenting/setting to false
3
+ # Warbler.framework_detection = false
4
+
5
+ # Warbler web application assembly configuration file
6
+ Warbler::Config.new do |config|
7
+ # Features: additional options controlling how the jar is built.
8
+ # Currently the following features are supported:
9
+ # - *gemjar*: package the gem repository in a jar file in WEB-INF/lib
10
+ # - *executable*: embed a web server and make the war executable
11
+ # - *runnable*: allows to run bin scripts e.g. `java -jar my.war -S rake -T`
12
+ # - *compiled*: compile .rb files to .class files
13
+ # config.features = %w(gemjar)
14
+
15
+ # Application directories to be included in the webapp.
16
+ config.dirs = %w(app bin config db docs fonts icons images lib package script sounds vendor videos)
17
+
18
+ # Additional files/directories to include, above those in config.dirs
19
+ config.includes = FileList['LICENSE.txt', 'VERSION']
20
+
21
+ # Additional files/directories to exclude
22
+ # config.excludes = FileList["lib/tasks/*"]
23
+
24
+ # Additional Java .jar files to include. Note that if .jar files are placed
25
+ # in lib (and not otherwise excluded) then they need not be mentioned here.
26
+ # JRuby and JRuby-Rack are pre-loaded in this list. Be sure to include your
27
+ # own versions if you directly set the value
28
+ # config.java_libs += FileList["lib/java/*.jar"]
29
+
30
+ # Loose Java classes and miscellaneous files to be included.
31
+ # config.java_classes = FileList["target/classes/**.*"]
32
+
33
+ # One or more pathmaps defining how the java classes should be copied into
34
+ # the archive. The example pathmap below accompanies the java_classes
35
+ # configuration above. See http://rake.rubyforge.org/classes/String.html#M000017
36
+ # for details of how to specify a pathmap.
37
+ # config.pathmaps.java_classes << "%{target/classes/,}p"
38
+
39
+ # Bundler support is built-in. If Warbler finds a Gemfile in the
40
+ # project directory, it will be used to collect the gems to bundle
41
+ # in your application. If you wish to explicitly disable this
42
+ # functionality, uncomment here.
43
+ # config.bundler = false
44
+
45
+ # An array of Bundler groups to avoid including in the war file.
46
+ # Defaults to ["development", "test", "assets"].
47
+ # config.bundle_without = []
48
+
49
+ # Other gems to be included. If you don't use Bundler or a gemspec
50
+ # file, you need to tell Warbler which gems your application needs
51
+ # so that they can be packaged in the archive.
52
+ # For Rails applications, the Rails gems are included by default
53
+ # unless the vendor/rails directory is present.
54
+ # config.gems += ["activerecord-jdbcmysql-adapter", "jruby-openssl"]
55
+ # config.gems << "tzinfo"
56
+
57
+ # Uncomment this if you don't want to package rails gem.
58
+ # config.gems -= ["rails"]
59
+
60
+ # The most recent versions of gems are used.
61
+ # You can specify versions of gems by using a hash assignment:
62
+ # config.gems["rails"] = "4.2.5"
63
+
64
+ # You can also use regexps or Gem::Dependency objects for flexibility or
65
+ # finer-grained control.
66
+ # config.gems << /^sinatra-/
67
+ # config.gems << Gem::Dependency.new("sinatra", "= 1.4.7")
68
+
69
+ # Include gem dependencies not mentioned specifically. Default is
70
+ # true, uncomment to turn off.
71
+ # config.gem_dependencies = false
72
+
73
+ # Array of regular expressions matching relative paths in gems to be
74
+ # excluded from the war. Defaults to empty, but you can set it like
75
+ # below, which excludes test files.
76
+ # config.gem_excludes = [/^(test|spec)\//]
77
+
78
+ # Pathmaps for controlling how application files are copied into the archive
79
+ # config.pathmaps.application = ["WEB-INF/%p"]
80
+
81
+ # Name of the archive (without the extension). Defaults to the basename
82
+ # of the project directory.
83
+ # config.jar_name = "mywar"
84
+
85
+ # File extension for the archive. Defaults to either 'jar' or 'war'.
86
+ # config.jar_extension = "jar"
87
+
88
+ # Destionation for the created archive. Defaults to project's root directory.
89
+ config.autodeploy_dir = "dist/"
90
+
91
+ # Name of the MANIFEST.MF template for the war file. Defaults to a simple
92
+ # MANIFEST.MF that contains the version of Warbler used to create the war file.
93
+ # config.manifest_file = "config/MANIFEST.MF"
94
+
95
+ # When using the 'compiled' feature and specified, only these Ruby
96
+ # files will be compiled. Default is to compile all \.rb files in
97
+ # the application.
98
+ # config.compiled_ruby_files = FileList['app/**/*.rb']
99
+
100
+ # Determines if ruby files in supporting gems will be compiled.
101
+ # Ignored unless compile feature is used.
102
+ # config.compile_gems = false
103
+
104
+ # When set it specify the bytecode version for compiled class files
105
+ # config.bytecode_version = "1.6"
106
+
107
+ # When set to true, Warbler will override the value of ENV['GEM_HOME'] even it
108
+ # has already been set. When set to false it will use any existing value of
109
+ # GEM_HOME if it is set.
110
+ # config.override_gem_home = true
111
+
112
+ # Allows for specifing custom executables
113
+ # config.executable = ["rake", "bin/rake"]
114
+
115
+ # Sets default (prefixed) parameters for the executables
116
+ # config.executable_params = "do:something"
117
+
118
+ # If set to true, moves jar files into WEB-INF/lib. Prior to version 1.4.2 of Warbler this was done
119
+ # by default. But since 1.4.2 this config defaults to false. It may need to be set to true for
120
+ # web servers that do not explode the WAR file.
121
+ # Alternatively, this option can be set to a regular expression, which will
122
+ # act as a jar selector -- only jar files that match the pattern will be
123
+ # included in the archive.
124
+ # config.move_jars_to_webinf_lib = false
125
+
126
+ # === War files only below here ===
127
+
128
+ # Embedded webserver to use with the 'executable' feature. Currently supported
129
+ # webservers are:
130
+ # - *jetty* - Embedded Jetty from Eclipse
131
+ # config.webserver = 'jetty'
132
+
133
+ # Path to the pre-bundled gem directory inside the war file. Default
134
+ # is 'WEB-INF/gems'. Specify path if gems are already bundled
135
+ # before running Warbler. This also sets 'gem.path' inside web.xml.
136
+ # config.gem_path = "WEB-INF/vendor/bundler_gems"
137
+
138
+ # Files for WEB-INF directory (next to web.xml). This contains
139
+ # web.xml by default. If there is an .erb-File it will be processed
140
+ # with webxml-config. You may want to exclude this file via
141
+ # config.excludes.
142
+ # config.webinf_files += FileList["jboss-web.xml"]
143
+
144
+ # Files to be included in the root of the webapp. Note that files in public
145
+ # will have the leading 'public/' part of the path stripped during staging.
146
+ # config.public_html = FileList["public/**/*", "doc/**/*"]
147
+
148
+ # Pathmaps for controlling how public HTML files are copied into the .war
149
+ # config.pathmaps.public_html = ["%{public/,}p"]
150
+
151
+ # Value of RAILS_ENV for the webapp -- default as shown below
152
+ # config.webxml.rails.env = ENV['RAILS_ENV'] || 'production'
153
+
154
+ # Public ROOT mapping, by default assets are copied into .war ROOT directory.
155
+ # config.public.root = ''
156
+
157
+ # Application booter to use, either :rack or :rails (autodetected by default)
158
+ # config.webxml.booter = :rails
159
+
160
+ # When using the :rack booter, "Rackup" script to use.
161
+ # - For 'rackup.path', the value points to the location of the rackup
162
+ # script in the web archive file. You need to make sure this file
163
+ # gets included in the war, possibly by adding it to config.includes
164
+ # or config.webinf_files above.
165
+ # - For 'rackup', the rackup script you provide as an inline string
166
+ # is simply embedded in web.xml.
167
+ # The script is evaluated in a Rack::Builder to load the application.
168
+ # Examples:
169
+ # config.webxml.rackup.path = 'WEB-INF/hello.ru'
170
+ # config.webxml.rackup = %{require './lib/demo'; run Rack::Adapter::Camping.new(Demo)}
171
+ # config.webxml.rackup = require 'cgi' && CGI::escapeHTML(File.read("config.ru"))
172
+
173
+ # Control the pool of Rails runtimes. Leaving unspecified means
174
+ # the pool will grow as needed to service requests. It is recommended
175
+ # that you fix these values when running a production server!
176
+ # If you're using threadsafe! mode, you probably don't want to set these values,
177
+ # since 1 runtime(default for threadsafe mode) will be enough.
178
+ # config.webxml.jruby.min.runtimes = 2
179
+ # config.webxml.jruby.max.runtimes = 4
180
+
181
+ # JNDI data source name
182
+ # config.webxml.jndi = 'jdbc/rails'
183
+ end
Binary file
data/db/db.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'active_record'
2
+ require 'jdbc/sqlite3'
3
+ Jdbc::SQLite3.load_driver
4
+ require 'activerecord-jdbcsqlite3-adapter' if defined? JRUBY_VERSION
5
+ @connection = ActiveRecord::Base.establish_connection(
6
+ adapter: 'sqlite3',
7
+ database: File.join(Dir.home, 'db/contact_manager.sqlite3')
8
+ )
9
+ require 'db/migrate'
@@ -0,0 +1,17 @@
1
+ require 'active_record'
2
+
3
+ class CreateContacts < ActiveRecord::Migration[6.1]
4
+ def change
5
+ create_table :contacts do |t|
6
+ t.string :first_name
7
+ t.string :last_name
8
+ t.string :email
9
+ t.string :phone
10
+ t.string :street
11
+ t.string :city
12
+ t.string :state_or_province
13
+ t.string :zip_or_postal_code
14
+ t.string :country
15
+ end
16
+ end
17
+ end
data/db/migrate.rb ADDED
@@ -0,0 +1,10 @@
1
+ migrate_dir = File.expand_path('../migrate', __FILE__)
2
+ Dir.glob(File.join(migrate_dir, '**', '*.rb')).each {|migration| require migration}
3
+
4
+ ActiveRecord::Migration[6.1].descendants.each do |migration|
5
+ begin
6
+ migration.migrate(:up)
7
+ rescue => e
8
+ raise e unless e.full_message.match(/table "[^"]+" already exists/)
9
+ end
10
+ end
Binary file
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: contact_manager
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Andy Maleh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: 4.23.1.4
19
+ name: glimmer-dsl-swt
20
+ prerelease: false
21
+ type: :runtime
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.23.1.4
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 6.1.5
33
+ name: activerecord
34
+ prerelease: false
35
+ type: :runtime
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 6.1.5
41
+ - !ruby/object:Gem::Dependency
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '61.1'
47
+ name: activerecord-jdbcsqlite3-adapter
48
+ prerelease: false
49
+ type: :runtime
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '61.1'
55
+ - !ruby/object:Gem::Dependency
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 3.5.0
61
+ name: rspec
62
+ prerelease: false
63
+ type: :development
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.5.0
69
+ - !ruby/object:Gem::Dependency
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '='
73
+ - !ruby/object:Gem::Version
74
+ version: 2.4.9
75
+ name: juwelier
76
+ prerelease: false
77
+ type: :development
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '='
81
+ - !ruby/object:Gem::Version
82
+ version: 2.4.9
83
+ - !ruby/object:Gem::Dependency
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - '='
87
+ - !ruby/object:Gem::Version
88
+ version: 2.0.5
89
+ name: warbler
90
+ prerelease: false
91
+ type: :development
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '='
95
+ - !ruby/object:Gem::Version
96
+ version: 2.0.5
97
+ - !ruby/object:Gem::Dependency
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ name: simplecov
104
+ prerelease: false
105
+ type: :development
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Contact Manager is a Glimmer DSL for SWT sample leveraging SQLite DB
112
+ via ActiveRecord.
113
+ email: andy.am@gmail.com
114
+ executables:
115
+ - contact_manager
116
+ extensions: []
117
+ extra_rdoc_files:
118
+ - LICENSE.txt
119
+ - README.md
120
+ files:
121
+ - LICENSE.txt
122
+ - README.md
123
+ - VERSION
124
+ - app/contact_manager.rb
125
+ - app/contact_manager/launch.rb
126
+ - app/contact_manager/model/contact.rb
127
+ - app/contact_manager/model/contact_presenter.rb
128
+ - app/contact_manager/model/contact_repository.rb
129
+ - app/contact_manager/view/app_view.rb
130
+ - app/contact_manager/view/contact_form.rb
131
+ - app/contact_manager/view/contact_manager_menu_bar.rb
132
+ - app/contact_manager/view/contact_table.rb
133
+ - bin/contact_manager
134
+ - config/warble.rb
135
+ - db/database.sqlite3
136
+ - db/db.rb
137
+ - db/migrate.rb
138
+ - db/migrate/20220411211513_create_contacts.rb
139
+ - icons/linux/Contact Manager.png
140
+ - icons/macosx/Contact Manager.icns
141
+ - icons/windows/Contact Manager.ico
142
+ - vendor/jars/org/yaml/snakeyaml/1.28/snakeyaml-1.28.jar
143
+ homepage: http://github.com/AndyObtiva/contact_manager
144
+ licenses:
145
+ - MIT
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - vendor
151
+ - lib
152
+ - app
153
+ required_ruby_version: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - ">="
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ">="
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ requirements: []
164
+ rubygems_version: 3.2.29
165
+ signing_key:
166
+ specification_version: 4
167
+ summary: Contact Manager
168
+ test_files: []