contact_manager 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []