├── .Rbuildignore
├── .github
├── .gitignore
└── workflows
│ └── pkgdown.yaml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── DESCRIPTION
├── LICENSE.md
├── NAMESPACE
├── NEWS.md
├── R
├── ModStore.R
├── Store.R
├── TidyModule.R
├── UtilityModule.R
├── add_module.R
├── examples.R
├── pipes.R
├── snippets.R
├── tidymodules.R
├── utility.R
└── verbs.R
├── README.Rmd
├── README.md
├── inst
├── .DS_Store
├── rstudio
│ └── r.snippets
└── shiny
│ └── examples
│ ├── 1_simple_addition
│ ├── Addition.R
│ ├── AdditionSM.R
│ ├── DESCRIPTION
│ └── app.R
│ ├── 2_linked_scatter
│ ├── DESCRIPTION
│ ├── app.R
│ └── linked_scatter.R
│ ├── 3_nested_modules
│ ├── ColorPicker.R
│ ├── DESCRIPTION
│ ├── Kmeans.R
│ └── app.R
│ ├── 4_communication
│ ├── DESCRIPTION
│ ├── app.R
│ ├── mod_BasePlot.R
│ ├── mod_BoxPlot.R
│ ├── mod_ClosablePanel.R
│ ├── mod_ColSelector.R
│ ├── mod_DataFilter.R
│ ├── mod_DatasetSelector.R
│ ├── mod_LinePlot.R
│ ├── mod_Panel.R
│ ├── mod_PlotGenerator.R
│ ├── mod_Scatter3DPlot.R
│ ├── mod_ScatterPlot.R
│ ├── model
│ │ ├── ERD.graphml
│ │ ├── ERD.svg
│ │ ├── ports.graphml
│ │ └── ports.svg
│ ├── module
│ │ ├── BasePlot.R
│ │ ├── BoxPlot.R
│ │ ├── ClosablePanel.R
│ │ ├── ColSelector.R
│ │ ├── DataFilter.R
│ │ ├── DatasetSelector.R
│ │ ├── LinePlot.R
│ │ ├── Panel.R
│ │ ├── PlotGenerator.R
│ │ ├── Scatter3DPlot.R
│ │ └── ScatterPlot.R
│ └── www
│ │ ├── ERD.svg
│ │ └── ports.svg
│ └── 5_counter
│ ├── Counter.R
│ ├── DESCRIPTION
│ ├── app.R
│ └── counterSM.R
├── man
├── ModStore.Rd
├── Store.Rd
├── TidyModule.Rd
├── UtilityModule.Rd
├── add_module.Rd
├── add_tm_snippets.Rd
├── callModules.Rd
├── check_and_load.Rd
├── combine_ports.Rd
├── defineEdges.Rd
├── figures
│ ├── QR_tidymodules.svg
│ └── logo.svg
├── getCacheOption.Rd
├── getMod.Rd
├── getSessionId.Rd
├── get_R6CG_list.Rd
├── global_options.Rd
├── grapes-colon-c-greater-than-colon-grapes.Rd
├── grapes-colon-greater-than-colon-grapes.Rd
├── grapes-colon-greater-than-greater-than-colon-grapes.Rd
├── grapes-colon-i-colon-grapes.Rd
├── grapes-colon-pi-colon-grapes.Rd
├── grapes-greater-than-grapes.Rd
├── grapes-greater-than-greater-than-grapes.Rd
├── grapes-greater-than-greater-than-y-grapes.Rd
├── grapes-greater-than-y-grapes.Rd
├── grapes-x-greater-than-greater-than-y-grapes.Rd
├── grapes-x-greater-than-y-grapes.Rd
├── grapes-x-less-than-less-than-y-grapes.Rd
├── grapes-x-less-than-y-grapes.Rd
├── iport.Rd
├── listModules.Rd
├── make_double_pipe.Rd
├── make_single_pipe.Rd
├── map_ports.Rd
├── mod.Rd
├── multi_port_map.Rd
├── oport.Rd
├── port.Rd
├── port_map.Rd
├── race_ports.Rd
├── session_type.Rd
├── showExamples.Rd
└── tidymodules-package.Rd
├── pkgdown
├── _pkgdown.yml
└── favicon
│ ├── apple-touch-icon-120x120.png
│ ├── apple-touch-icon-152x152.png
│ ├── apple-touch-icon-180x180.png
│ ├── apple-touch-icon-60x60.png
│ ├── apple-touch-icon-76x76.png
│ ├── apple-touch-icon.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ └── favicon.ico
├── tests
├── testthat.R
└── testthat
│ └── test_dummy.R
├── tidymodules.Rproj
└── vignettes
├── .gitignore
├── communication.Rmd
├── figures
├── ERD.png
├── mod_network.png
├── ports.svg
└── ports_intro.png
├── inheritance.Rmd
├── intro.Rmd
├── namespace.Rmd
├── session.Rmd
└── tidymodules.Rmd
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^CODE_OF_CONDUCT\.md$
2 | ^README\.Rmd$
3 | ^LICENSE\.md$
4 | ^.*\.Rproj$
5 | ^\.Rproj\.user$
6 | ^data-raw$
7 | dev_history.R
8 | ^dev$
9 | $run_dev.*
10 | .travis.yml
11 | ^_pkgdown\.yml$
12 | ^pkgdown$
13 | ^docs$
14 | ^docs$
15 | ^\.github$
16 |
--------------------------------------------------------------------------------
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 | release:
9 | types: [published]
10 | workflow_dispatch:
11 |
12 | name: pkgdown
13 |
14 | jobs:
15 | pkgdown:
16 | runs-on: ubuntu-latest
17 | # Only restrict concurrency for non-PR jobs
18 | concurrency:
19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
20 | env:
21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
22 | steps:
23 | - uses: actions/checkout@v3
24 |
25 | - uses: r-lib/actions/setup-pandoc@v2
26 |
27 | - uses: r-lib/actions/setup-r@v2
28 | with:
29 | use-public-rspm: true
30 |
31 | - uses: r-lib/actions/setup-r-dependencies@v2
32 | with:
33 | extra-packages: any::pkgdown, local::.
34 | needs: website
35 |
36 | - name: Build site
37 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
38 | shell: Rscript {0}
39 |
40 | - name: Deploy to GitHub pages 🚀
41 | if: github.event_name != 'pull_request'
42 | uses: JamesIves/github-pages-deploy-action@v4.4.1
43 | with:
44 | clean: false
45 | branch: gh-pages
46 | folder: docs
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | docs
2 | .Rproj.user
3 | .Rhistory
4 | .RData
5 | .Ruserdata
6 | .Rprofile
7 | renv
8 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Code of Conduct
2 |
3 | As contributors and maintainers of this project, we pledge to respect all people who
4 | contribute through reporting issues, posting feature requests, updating documentation,
5 | submitting pull requests or patches, and other activities.
6 |
7 | We are committed to making participation in this project a harassment-free experience for
8 | everyone, regardless of level of experience, gender, gender identity and expression,
9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
10 |
11 | Examples of unacceptable behavior by participants include the use of sexual language or
12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment,
13 | insults, or other unprofessional conduct.
14 |
15 | Project maintainers have the right and responsibility to remove, edit, or reject comments,
16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this
17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
18 | from the project team.
19 |
20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
21 | opening an issue or contacting one or more of the project maintainers.
22 |
23 | This Code of Conduct is adapted from the Contributor Covenant
24 | (http://contributor-covenant.org), version 1.0.0, available at
25 | http://contributor-covenant.org/version/1/0/0/
26 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: tidymodules
2 | Title: A robust framework for developing shiny modules
3 | Version: 0.1.6
4 | Authors@R: c(
5 | person('Mustapha', 'Larbaoui', email = 'mustapha.larbaoui@novartis.com', role = c('cre', 'aut')),
6 | person('Douglas', 'Robinson', email = 'douglas.robinson@novartis.com', role = c('ctb')),
7 | person('Xiao', 'Ni', email = 'xiao.ni@novartis.com', role = c('ctb')),
8 | person('David', 'Granjon', email = 'david.granjon@novartis.com', role = c('ctb')),
9 | person('Stephen', 'Eng', email = 'stefaneng13@gmail.com', role = c('ctb')),
10 | person('Marzieh', 'Eslami Rasekh', email = 'marzie.rasekh@gmail.com', role = c('ctb')),
11 | person('Renan', 'Sauteraud', email = 'rxs575@psu.edu', role = c('ctb')))
12 | Description: tidymodules offers a robust framework for developing shiny modules based on R6 classes which should facilitates inter-modules communication.
13 | License: Apache License (>= 2.0)
14 | Encoding: UTF-8
15 | LazyData: true
16 | Imports:
17 | miniUI,
18 | stringi,
19 | digest,
20 | R6,
21 | methods,
22 | snippr,
23 | cli,
24 | dplyr,
25 | fs,
26 | purrr,
27 | visNetwork
28 | Depends:
29 | shiny
30 | RoxygenNote: 7.2.1
31 | Roxygen: list(markdown = TRUE)
32 | URL: https://github.com/Novartis/tidymodules,
33 | http://opensource.nibr.com/tidymodules/
34 | BugReports: https://github.com/Novartis/tidymodules/issues
35 | Suggests:
36 | rstudioapi,
37 | testthat,
38 | knitr,
39 | rmarkdown,
40 | ggplot2,
41 | shinycssloaders,
42 | RColorBrewer,
43 | shinyWidgets,
44 | plotly
45 | Remotes:
46 | dgrtwo/snippr@29c1813
47 | VignetteBuilder: knitr
48 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Apache License
2 | ==============
3 |
4 | _Version 2.0, January 2004_
5 | _<>_
6 |
7 | ### Terms and Conditions for use, reproduction, and distribution
8 |
9 | #### 1. Definitions
10 |
11 | “License” shall mean the terms and conditions for use, reproduction, and
12 | distribution as defined by Sections 1 through 9 of this document.
13 |
14 | “Licensor” shall mean the copyright owner or entity authorized by the copyright
15 | owner that is granting the License.
16 |
17 | “Legal Entity” shall mean the union of the acting entity and all other entities
18 | that control, are controlled by, or are under common control with that entity.
19 | For the purposes of this definition, “control” means **(i)** the power, direct or
20 | indirect, to cause the direction or management of such entity, whether by
21 | contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
22 | outstanding shares, or **(iii)** beneficial ownership of such entity.
23 |
24 | “You” (or “Your”) shall mean an individual or Legal Entity exercising
25 | permissions granted by this License.
26 |
27 | “Source” form shall mean the preferred form for making modifications, including
28 | but not limited to software source code, documentation source, and configuration
29 | files.
30 |
31 | “Object” form shall mean any form resulting from mechanical transformation or
32 | translation of a Source form, including but not limited to compiled object code,
33 | generated documentation, and conversions to other media types.
34 |
35 | “Work” shall mean the work of authorship, whether in Source or Object form, made
36 | available under the License, as indicated by a copyright notice that is included
37 | in or attached to the work (an example is provided in the Appendix below).
38 |
39 | “Derivative Works” shall mean any work, whether in Source or Object form, that
40 | is based on (or derived from) the Work and for which the editorial revisions,
41 | annotations, elaborations, or other modifications represent, as a whole, an
42 | original work of authorship. For the purposes of this License, Derivative Works
43 | shall not include works that remain separable from, or merely link (or bind by
44 | name) to the interfaces of, the Work and Derivative Works thereof.
45 |
46 | “Contribution” shall mean any work of authorship, including the original version
47 | of the Work and any modifications or additions to that Work or Derivative Works
48 | thereof, that is intentionally submitted to Licensor for inclusion in the Work
49 | by the copyright owner or by an individual or Legal Entity authorized to submit
50 | on behalf of the copyright owner. For the purposes of this definition,
51 | “submitted” means any form of electronic, verbal, or written communication sent
52 | to the Licensor or its representatives, including but not limited to
53 | communication on electronic mailing lists, source code control systems, and
54 | issue tracking systems that are managed by, or on behalf of, the Licensor for
55 | the purpose of discussing and improving the Work, but excluding communication
56 | that is conspicuously marked or otherwise designated in writing by the copyright
57 | owner as “Not a Contribution.”
58 |
59 | “Contributor” shall mean Licensor and any individual or Legal Entity on behalf
60 | of whom a Contribution has been received by Licensor and subsequently
61 | incorporated within the Work.
62 |
63 | #### 2. Grant of Copyright License
64 |
65 | Subject to the terms and conditions of this License, each Contributor hereby
66 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
67 | irrevocable copyright license to reproduce, prepare Derivative Works of,
68 | publicly display, publicly perform, sublicense, and distribute the Work and such
69 | Derivative Works in Source or Object form.
70 |
71 | #### 3. Grant of Patent License
72 |
73 | Subject to the terms and conditions of this License, each Contributor hereby
74 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
75 | irrevocable (except as stated in this section) patent license to make, have
76 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where
77 | such license applies only to those patent claims licensable by such Contributor
78 | that are necessarily infringed by their Contribution(s) alone or by combination
79 | of their Contribution(s) with the Work to which such Contribution(s) was
80 | submitted. If You institute patent litigation against any entity (including a
81 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a
82 | Contribution incorporated within the Work constitutes direct or contributory
83 | patent infringement, then any patent licenses granted to You under this License
84 | for that Work shall terminate as of the date such litigation is filed.
85 |
86 | #### 4. Redistribution
87 |
88 | You may reproduce and distribute copies of the Work or Derivative Works thereof
89 | in any medium, with or without modifications, and in Source or Object form,
90 | provided that You meet the following conditions:
91 |
92 | * **(a)** You must give any other recipients of the Work or Derivative Works a copy of
93 | this License; and
94 | * **(b)** You must cause any modified files to carry prominent notices stating that You
95 | changed the files; and
96 | * **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
97 | all copyright, patent, trademark, and attribution notices from the Source form
98 | of the Work, excluding those notices that do not pertain to any part of the
99 | Derivative Works; and
100 | * **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
101 | Derivative Works that You distribute must include a readable copy of the
102 | attribution notices contained within such NOTICE file, excluding those notices
103 | that do not pertain to any part of the Derivative Works, in at least one of the
104 | following places: within a NOTICE text file distributed as part of the
105 | Derivative Works; within the Source form or documentation, if provided along
106 | with the Derivative Works; or, within a display generated by the Derivative
107 | Works, if and wherever such third-party notices normally appear. The contents of
108 | the NOTICE file are for informational purposes only and do not modify the
109 | License. You may add Your own attribution notices within Derivative Works that
110 | You distribute, alongside or as an addendum to the NOTICE text from the Work,
111 | provided that such additional attribution notices cannot be construed as
112 | modifying the License.
113 |
114 | You may add Your own copyright statement to Your modifications and may provide
115 | additional or different license terms and conditions for use, reproduction, or
116 | distribution of Your modifications, or for any such Derivative Works as a whole,
117 | provided Your use, reproduction, and distribution of the Work otherwise complies
118 | with the conditions stated in this License.
119 |
120 | #### 5. Submission of Contributions
121 |
122 | Unless You explicitly state otherwise, any Contribution intentionally submitted
123 | for inclusion in the Work by You to the Licensor shall be under the terms and
124 | conditions of this License, without any additional terms or conditions.
125 | Notwithstanding the above, nothing herein shall supersede or modify the terms of
126 | any separate license agreement you may have executed with Licensor regarding
127 | such Contributions.
128 |
129 | #### 6. Trademarks
130 |
131 | This License does not grant permission to use the trade names, trademarks,
132 | service marks, or product names of the Licensor, except as required for
133 | reasonable and customary use in describing the origin of the Work and
134 | reproducing the content of the NOTICE file.
135 |
136 | #### 7. Disclaimer of Warranty
137 |
138 | Unless required by applicable law or agreed to in writing, Licensor provides the
139 | Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
141 | including, without limitation, any warranties or conditions of TITLE,
142 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
143 | solely responsible for determining the appropriateness of using or
144 | redistributing the Work and assume any risks associated with Your exercise of
145 | permissions under this License.
146 |
147 | #### 8. Limitation of Liability
148 |
149 | In no event and under no legal theory, whether in tort (including negligence),
150 | contract, or otherwise, unless required by applicable law (such as deliberate
151 | and grossly negligent acts) or agreed to in writing, shall any Contributor be
152 | liable to You for damages, including any direct, indirect, special, incidental,
153 | or consequential damages of any character arising as a result of this License or
154 | out of the use or inability to use the Work (including but not limited to
155 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or
156 | any and all other commercial damages or losses), even if such Contributor has
157 | been advised of the possibility of such damages.
158 |
159 | #### 9. Accepting Warranty or Additional Liability
160 |
161 | While redistributing the Work or Derivative Works thereof, You may choose to
162 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or
163 | other liability obligations and/or rights consistent with this License. However,
164 | in accepting such obligations, You may act only on Your own behalf and on Your
165 | sole responsibility, not on behalf of any other Contributor, and only if You
166 | agree to indemnify, defend, and hold each Contributor harmless for any liability
167 | incurred by, or claims asserted against, such Contributor by reason of your
168 | accepting any such warranty or additional liability.
169 |
170 | _END OF TERMS AND CONDITIONS_
171 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | export("%->%")
4 | export("%->>%")
5 | export("%1<1%")
6 | export("%2<1%")
7 | export("%3<1%")
8 | export("%4<1%")
9 | export("%5<1%")
10 | export("%6<1%")
11 | export("%7<1%")
12 | export("%8<1%")
13 | export("%9<1%")
14 | export("%10<1%")
15 | export("%1<2%")
16 | export("%2<2%")
17 | export("%3<2%")
18 | export("%4<2%")
19 | export("%5<2%")
20 | export("%6<2%")
21 | export("%7<2%")
22 | export("%8<2%")
23 | export("%9<2%")
24 | export("%10<2%")
25 | export("%1<3%")
26 | export("%2<3%")
27 | export("%3<3%")
28 | export("%4<3%")
29 | export("%5<3%")
30 | export("%6<3%")
31 | export("%7<3%")
32 | export("%8<3%")
33 | export("%9<3%")
34 | export("%10<3%")
35 | export("%1<4%")
36 | export("%2<4%")
37 | export("%3<4%")
38 | export("%4<4%")
39 | export("%5<4%")
40 | export("%6<4%")
41 | export("%7<4%")
42 | export("%8<4%")
43 | export("%9<4%")
44 | export("%10<4%")
45 | export("%1<5%")
46 | export("%2<5%")
47 | export("%3<5%")
48 | export("%4<5%")
49 | export("%5<5%")
50 | export("%6<5%")
51 | export("%7<5%")
52 | export("%8<5%")
53 | export("%9<5%")
54 | export("%10<5%")
55 | export("%1<6%")
56 | export("%2<6%")
57 | export("%3<6%")
58 | export("%4<6%")
59 | export("%5<6%")
60 | export("%6<6%")
61 | export("%7<6%")
62 | export("%8<6%")
63 | export("%9<6%")
64 | export("%10<6%")
65 | export("%1<7%")
66 | export("%2<7%")
67 | export("%3<7%")
68 | export("%4<7%")
69 | export("%5<7%")
70 | export("%6<7%")
71 | export("%7<7%")
72 | export("%8<7%")
73 | export("%9<7%")
74 | export("%10<7%")
75 | export("%1<8%")
76 | export("%2<8%")
77 | export("%3<8%")
78 | export("%4<8%")
79 | export("%5<8%")
80 | export("%6<8%")
81 | export("%7<8%")
82 | export("%8<8%")
83 | export("%9<8%")
84 | export("%10<8%")
85 | export("%1<9%")
86 | export("%2<9%")
87 | export("%3<9%")
88 | export("%4<9%")
89 | export("%5<9%")
90 | export("%6<9%")
91 | export("%7<9%")
92 | export("%8<9%")
93 | export("%9<9%")
94 | export("%10<9%")
95 | export("%1<10%")
96 | export("%2<10%")
97 | export("%3<10%")
98 | export("%4<10%")
99 | export("%5<10%")
100 | export("%6<10%")
101 | export("%7<10%")
102 | export("%8<10%")
103 | export("%9<10%")
104 | export("%10<10%")
105 | export("%1<<1%")
106 | export("%2<<1%")
107 | export("%3<<1%")
108 | export("%4<<1%")
109 | export("%5<<1%")
110 | export("%6<<1%")
111 | export("%7<<1%")
112 | export("%8<<1%")
113 | export("%9<<1%")
114 | export("%10<<1%")
115 | export("%1<<2%")
116 | export("%2<<2%")
117 | export("%3<<2%")
118 | export("%4<<2%")
119 | export("%5<<2%")
120 | export("%6<<2%")
121 | export("%7<<2%")
122 | export("%8<<2%")
123 | export("%9<<2%")
124 | export("%10<<2%")
125 | export("%1<<3%")
126 | export("%2<<3%")
127 | export("%3<<3%")
128 | export("%4<<3%")
129 | export("%5<<3%")
130 | export("%6<<3%")
131 | export("%7<<3%")
132 | export("%8<<3%")
133 | export("%9<<3%")
134 | export("%10<<3%")
135 | export("%1<<4%")
136 | export("%2<<4%")
137 | export("%3<<4%")
138 | export("%4<<4%")
139 | export("%5<<4%")
140 | export("%6<<4%")
141 | export("%7<<4%")
142 | export("%8<<4%")
143 | export("%9<<4%")
144 | export("%10<<4%")
145 | export("%1<<5%")
146 | export("%2<<5%")
147 | export("%3<<5%")
148 | export("%4<<5%")
149 | export("%5<<5%")
150 | export("%6<<5%")
151 | export("%7<<5%")
152 | export("%8<<5%")
153 | export("%9<<5%")
154 | export("%10<<5%")
155 | export("%1<<6%")
156 | export("%2<<6%")
157 | export("%3<<6%")
158 | export("%4<<6%")
159 | export("%5<<6%")
160 | export("%6<<6%")
161 | export("%7<<6%")
162 | export("%8<<6%")
163 | export("%9<<6%")
164 | export("%10<<6%")
165 | export("%1<<7%")
166 | export("%2<<7%")
167 | export("%3<<7%")
168 | export("%4<<7%")
169 | export("%5<<7%")
170 | export("%6<<7%")
171 | export("%7<<7%")
172 | export("%8<<7%")
173 | export("%9<<7%")
174 | export("%10<<7%")
175 | export("%1<<8%")
176 | export("%2<<8%")
177 | export("%3<<8%")
178 | export("%4<<8%")
179 | export("%5<<8%")
180 | export("%6<<8%")
181 | export("%7<<8%")
182 | export("%8<<8%")
183 | export("%9<<8%")
184 | export("%10<<8%")
185 | export("%1<<9%")
186 | export("%2<<9%")
187 | export("%3<<9%")
188 | export("%4<<9%")
189 | export("%5<<9%")
190 | export("%6<<9%")
191 | export("%7<<9%")
192 | export("%8<<9%")
193 | export("%9<<9%")
194 | export("%10<<9%")
195 | export("%1<<10%")
196 | export("%2<<10%")
197 | export("%3<<10%")
198 | export("%4<<10%")
199 | export("%5<<10%")
200 | export("%6<<10%")
201 | export("%7<<10%")
202 | export("%8<<10%")
203 | export("%9<<10%")
204 | export("%10<<10%")
205 | export("%1>1%")
206 | export("%2>1%")
207 | export("%3>1%")
208 | export("%4>1%")
209 | export("%5>1%")
210 | export("%6>1%")
211 | export("%7>1%")
212 | export("%8>1%")
213 | export("%9>1%")
214 | export("%10>1%")
215 | export("%1>2%")
216 | export("%2>2%")
217 | export("%3>2%")
218 | export("%4>2%")
219 | export("%5>2%")
220 | export("%6>2%")
221 | export("%7>2%")
222 | export("%8>2%")
223 | export("%9>2%")
224 | export("%10>2%")
225 | export("%1>3%")
226 | export("%2>3%")
227 | export("%3>3%")
228 | export("%4>3%")
229 | export("%5>3%")
230 | export("%6>3%")
231 | export("%7>3%")
232 | export("%8>3%")
233 | export("%9>3%")
234 | export("%10>3%")
235 | export("%1>4%")
236 | export("%2>4%")
237 | export("%3>4%")
238 | export("%4>4%")
239 | export("%5>4%")
240 | export("%6>4%")
241 | export("%7>4%")
242 | export("%8>4%")
243 | export("%9>4%")
244 | export("%10>4%")
245 | export("%1>5%")
246 | export("%2>5%")
247 | export("%3>5%")
248 | export("%4>5%")
249 | export("%5>5%")
250 | export("%6>5%")
251 | export("%7>5%")
252 | export("%8>5%")
253 | export("%9>5%")
254 | export("%10>5%")
255 | export("%1>6%")
256 | export("%2>6%")
257 | export("%3>6%")
258 | export("%4>6%")
259 | export("%5>6%")
260 | export("%6>6%")
261 | export("%7>6%")
262 | export("%8>6%")
263 | export("%9>6%")
264 | export("%10>6%")
265 | export("%1>7%")
266 | export("%2>7%")
267 | export("%3>7%")
268 | export("%4>7%")
269 | export("%5>7%")
270 | export("%6>7%")
271 | export("%7>7%")
272 | export("%8>7%")
273 | export("%9>7%")
274 | export("%10>7%")
275 | export("%1>8%")
276 | export("%2>8%")
277 | export("%3>8%")
278 | export("%4>8%")
279 | export("%5>8%")
280 | export("%6>8%")
281 | export("%7>8%")
282 | export("%8>8%")
283 | export("%9>8%")
284 | export("%10>8%")
285 | export("%1>9%")
286 | export("%2>9%")
287 | export("%3>9%")
288 | export("%4>9%")
289 | export("%5>9%")
290 | export("%6>9%")
291 | export("%7>9%")
292 | export("%8>9%")
293 | export("%9>9%")
294 | export("%10>9%")
295 | export("%1>10%")
296 | export("%2>10%")
297 | export("%3>10%")
298 | export("%4>10%")
299 | export("%5>10%")
300 | export("%6>10%")
301 | export("%7>10%")
302 | export("%8>10%")
303 | export("%9>10%")
304 | export("%10>10%")
305 | export("%1>>1%")
306 | export("%2>>1%")
307 | export("%3>>1%")
308 | export("%4>>1%")
309 | export("%5>>1%")
310 | export("%6>>1%")
311 | export("%7>>1%")
312 | export("%8>>1%")
313 | export("%9>>1%")
314 | export("%10>>1%")
315 | export("%1>>2%")
316 | export("%2>>2%")
317 | export("%3>>2%")
318 | export("%4>>2%")
319 | export("%5>>2%")
320 | export("%6>>2%")
321 | export("%7>>2%")
322 | export("%8>>2%")
323 | export("%9>>2%")
324 | export("%10>>2%")
325 | export("%1>>3%")
326 | export("%2>>3%")
327 | export("%3>>3%")
328 | export("%4>>3%")
329 | export("%5>>3%")
330 | export("%6>>3%")
331 | export("%7>>3%")
332 | export("%8>>3%")
333 | export("%9>>3%")
334 | export("%10>>3%")
335 | export("%1>>4%")
336 | export("%2>>4%")
337 | export("%3>>4%")
338 | export("%4>>4%")
339 | export("%5>>4%")
340 | export("%6>>4%")
341 | export("%7>>4%")
342 | export("%8>>4%")
343 | export("%9>>4%")
344 | export("%10>>4%")
345 | export("%1>>5%")
346 | export("%2>>5%")
347 | export("%3>>5%")
348 | export("%4>>5%")
349 | export("%5>>5%")
350 | export("%6>>5%")
351 | export("%7>>5%")
352 | export("%8>>5%")
353 | export("%9>>5%")
354 | export("%10>>5%")
355 | export("%1>>6%")
356 | export("%2>>6%")
357 | export("%3>>6%")
358 | export("%4>>6%")
359 | export("%5>>6%")
360 | export("%6>>6%")
361 | export("%7>>6%")
362 | export("%8>>6%")
363 | export("%9>>6%")
364 | export("%10>>6%")
365 | export("%1>>7%")
366 | export("%2>>7%")
367 | export("%3>>7%")
368 | export("%4>>7%")
369 | export("%5>>7%")
370 | export("%6>>7%")
371 | export("%7>>7%")
372 | export("%8>>7%")
373 | export("%9>>7%")
374 | export("%10>>7%")
375 | export("%1>>8%")
376 | export("%2>>8%")
377 | export("%3>>8%")
378 | export("%4>>8%")
379 | export("%5>>8%")
380 | export("%6>>8%")
381 | export("%7>>8%")
382 | export("%8>>8%")
383 | export("%9>>8%")
384 | export("%10>>8%")
385 | export("%1>>9%")
386 | export("%2>>9%")
387 | export("%3>>9%")
388 | export("%4>>9%")
389 | export("%5>>9%")
390 | export("%6>>9%")
391 | export("%7>>9%")
392 | export("%8>>9%")
393 | export("%9>>9%")
394 | export("%10>>9%")
395 | export("%1>>10%")
396 | export("%2>>10%")
397 | export("%3>>10%")
398 | export("%4>>10%")
399 | export("%5>>10%")
400 | export("%6>>10%")
401 | export("%7>>10%")
402 | export("%8>>10%")
403 | export("%9>>10%")
404 | export("%10>>10%")
405 | export("%:>:%")
406 | export("%:>>:%")
407 | export("%:c>:%")
408 | export("%:i:%")
409 | export("%:pi:%")
410 | export("%>1%")
411 | export("%>2%")
412 | export("%>3%")
413 | export("%>4%")
414 | export("%>5%")
415 | export("%>6%")
416 | export("%>7%")
417 | export("%>8%")
418 | export("%>9%")
419 | export("%>10%")
420 | export("%>>1%")
421 | export("%>>2%")
422 | export("%>>3%")
423 | export("%>>4%")
424 | export("%>>5%")
425 | export("%>>6%")
426 | export("%>>7%")
427 | export("%>>8%")
428 | export("%>>9%")
429 | export("%>>10%")
430 | export(ModStore)
431 | export(Store)
432 | export(TidyModule)
433 | export(add_module)
434 | export(add_tm_snippets)
435 | export(callModules)
436 | export(check_and_load)
437 | export(combine_ports)
438 | export(defineEdges)
439 | export(getCacheOption)
440 | export(getMod)
441 | export(getSessionId)
442 | export(iport)
443 | export(listModules)
444 | export(map_ports)
445 | export(mod)
446 | export(oport)
447 | export(port)
448 | export(race_ports)
449 | export(session_type)
450 | export(showExamples)
451 | import(R6)
452 | import(dplyr)
453 | import(shiny)
454 | import(snippr)
455 | importFrom(cli,cat_boxx)
456 | importFrom(cli,cat_bullet)
457 | importFrom(fs,dir_create)
458 | importFrom(fs,dir_exists)
459 | importFrom(fs,file_create)
460 | importFrom(fs,file_exists)
461 | importFrom(fs,path)
462 | importFrom(fs,path_abs)
463 | importFrom(fs,path_ext_set)
464 | importFrom(fs,path_file)
465 | importFrom(fs,path_home_r)
466 | importFrom(methods,is)
467 | importFrom(purrr,discard)
468 | importFrom(purrr,keep)
469 | importFrom(purrr,map)
470 | importFrom(snippr,snippets_read)
471 | importFrom(utils,capture.output)
472 | importFrom(utils,file.edit)
473 | importFrom(utils,menu)
474 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # tidymodules 0.1.6
2 |
3 | - ```deep``` option : optional argument passed to the module ```destroy``` method. Allows the destruction of child module. ```FALSE``` by default.
4 | - Pipes updates : new ```%c>%```, copy all inputs; updated ```%:i:%``` and ```%:pi:%``` pipes.
5 | - Clear some check warnings/notes
6 | - Fix other issues
7 |
8 | # tidymodules 0.1.5
9 |
10 | - ```obser``` attribute : Now all {tm} modules have an obser attribute which is a list of all server observers. This is a convenient location to explore existing observers and helps in garbage collection. Users need to use ```self$obser$[observer_id]``` as a variable name when initializing the observers. All {tm} examples updated with ```obser```.
11 |
12 | - ```destroy``` method : This function destroys the module, it removes all the module's references from the ```ModStore``` (session module and edges) and destroy module observers stored in the ```obser``` attribute mentioned above. Note : This functionality rely on module developers to systematically store observers in the ```obser``` list.
13 |
14 | - ```suspend``` method : This function suspends module's observers stored in the ```obser``` attribute mentioned above. Note : This functionality rely on module developers to systematically store observers in the ```obser``` list.
15 |
16 | - ```resume``` method : This function resumes module's observers stored in the ```obser``` attribute mentioned above. Note : This functionality rely on module developers to systematically store observers in the ```obser``` list.
17 |
18 | # tidymodules 0.1.4
19 |
20 | - ```collision``` option : By default {tidymodules} doesn't allow the creation of two modules with same id at the same time (same timestamp). It fails with a collision error. This option which is ```FALSE``` by default, allows the user to disable collision check. This could be useful when users create module in an observer that get triggered twice at the same time.
21 |
22 | - ```react``` attribute : Now all {tm} modules have a react attribute which is a list to conveniently store server reactive objects (reactive / reactiveVal / reactiveValues). This list help store module reactive objects and facilitate their access from anywhere within the module object. Users need to use ```self$react$[reactive_id]``` as a variable name when initializing the objects. All {tm} examples updated with ```react```.
23 |
24 | - Debug mode with ```TM_DEBUG``` option : display a debug button and highlight module UI. Clicking the button allows to explore module environment in debug mode. try this ```options(TM_DEBUG=TRUE)```.
25 |
26 | - Fix issue with parent module look-up function. Now the code takes the parent option provided by the user as the source of truth.
27 |
28 | - Fix bug in calculating port length and pipe operators
29 |
30 | - Apply styler and fix some issues found with lintr
31 |
32 | # tidymodules 0.1.1 -> 0.1.3
33 |
34 | - Mainly bug fixes and some improvements here and there in the code
35 |
36 | # tidymodules 0.1.0.9007
37 |
38 | - Correct port attibutes assignment
39 | - Rename TidyModule field `parent_ports` to `pass_ports`
40 | - Make `assignPort` function work in dynamic context
41 | - Add `inherit` parameter to `addPort` function to better control ports inheritance
42 | - Add extra warnings and exceptions related to nested modules and port inheritance
43 | - Improve module console printing to highlight inherited ports
44 |
45 | # tidymodules 0.1.0.9006
46 |
47 | - Fix a problem where there is no shiny session argument in app server and calling modules' callModule & callModules.
48 | - Add warning to module get port functions for some specific cases (global vs user session)
49 | - fix module iport & oport functions
50 | - doc fix
51 |
52 | # tidymodules 0.1.0.9005
53 |
54 | - switch to Apache-2.0 Licence
55 | - fix doc
56 |
57 |
58 | # tidymodules 0.1.0.9004
59 |
60 | - add_module function
61 | - snippets file & function to inject them into RStudio configuration
62 | - new defineEdges() function for parsing module communication instructions
63 |
64 | # tidymodules 0.1.0.9003
65 |
66 | - Improve how the ports are moved around
67 | - Restrict port assignment to reactive function only. No more reactiveValues as this can be modified by module. tidymodules derived ports are an exception.
68 | - Clean-up pipe operators code
69 | - New '%->>%' pipe
70 | - Move input (i) and ouput (o) ports lists into public field to facilitate port lookup from a module reference
71 | - Add oport/iport to be consistent with the corresponding utility functions
72 | - Add exec`In/Out`put functions
73 | - Fix for Store module when edges are empty
74 | - Add check in ModStore for duplicated edges
75 |
76 |
77 | # tidymodules 0.1.0.9002
78 |
79 | - Adding shiny module code in example 1
80 |
81 |
82 | # tidymodules 0.1.0.9001
83 |
84 | - Support for nested modules stored in parent module attribute list
85 | - Sanitize namespace and group ID when provided
86 | - Switch to shiny getDefaultReactiveDomain to retrieve ShinySession
87 | - Update namespace vignette
88 |
89 | # tidymodules 0.1.0.9000
90 |
91 | - Add travis-CI for building pkgdsown site
92 | - Remove docs
93 | - Fix & complete docs/vignettes
94 | - Add new TidyModule fields : name & order
95 | - Fix issue for creating nested module in console & setting parent namespace
96 |
97 | # tidymodules 0.1.0
98 |
99 | - Github release
100 |
--------------------------------------------------------------------------------
/R/ModStore.R:
--------------------------------------------------------------------------------
1 |
2 | #' R6 Class Representing a ModStore
3 | #'
4 | #' @description
5 | #' This class is used to create a storage for tidymodules objects.
6 | #'
7 | #' @details
8 | #' Manage applications, sessions and modules.
9 | #'
10 | #' @import shiny
11 | #'
12 | #' @export
13 | ModStore <- R6::R6Class(
14 | "ModStore",
15 | public = list(
16 | #' @description
17 | #' Create a new ModStore object.
18 | #' Should be called once by the TidyModule class.
19 | #' Not to be called directly outside TidyModule.
20 | #' The ModStore object can be retrieved from any TidyModule object, see example below.
21 | #' @examples
22 | #' MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
23 | #' m <- MyModule$new()
24 | #' s <- m$getStore()
25 | #' @return A new `ModStore` object.
26 | initialize = function() {},
27 | #' @description
28 | #' Check if a module is stored in the current session.
29 | #' @param m TidyModule object.
30 | #' @examples
31 | #' MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
32 | #' m <- MyModule$new()
33 | #' s <- m$getStore()
34 | #' s$isStored(m)
35 | isStored = function(m) {
36 | s <- self$getSession(m)
37 | mod <- isolate(s$collection[[m$module_ns]])
38 | if (is.null(mod)) {
39 | return(FALSE)
40 | } else {
41 | return(TRUE)
42 | }
43 | },
44 | #' @description
45 | #' Retrieve the global session 'global_session'.
46 | #' This is the session that exists outside the application server function
47 | getGlobalSession = function() {
48 | sid <- "global_session"
49 | self$getSession(sid)
50 | },
51 | #' @description
52 | #' Retrieve a module session.
53 | #' This could be the global session or a user session.
54 | #' @param m TidyModule object.
55 | getSession = function(m) {
56 | isolate({
57 | return(private$getS(m))
58 | })
59 | },
60 | #' @description
61 | #' Retrieve all sessions.
62 | getSessions = function() {
63 | return(private$sessions)
64 | },
65 | #' @description
66 | #' Retrieve all modules.
67 | #' @param m TidyModule object.
68 | getMods = function(m) {
69 | s <- self$getSession(m)
70 | return(s$collection)
71 | },
72 | #' @description
73 | #' Retrieve modules connections.
74 | #' @param m TidyModule object.
75 | getEdges = function(m) {
76 | s <- self$getSession(m)
77 | return(s$edges)
78 | },
79 | #' @description
80 | #' Add modules connections into ModStore.
81 | #' An edge is either a connection between a reactive object and a module
82 | #' or between two modules.
83 | #' @param from list with three elements: m -> module, type -> input or output, port -> port Id.
84 | #' @param to list with three elements: m -> module, type -> input or output, port -> port Id.
85 | #' @param mode The type of edge, default to 'direct'.
86 | #' @param comment Any additional comment.
87 | addEdge = function(from,
88 | to,
89 | mode = "direct",
90 | comment = NA) {
91 | fromId <- fname <- fport <- ftype <- fclass <- NA
92 | toId <- tname <- tport <- ttype <- tclass <- NA
93 | s <- e <- d <- NULL
94 |
95 | isolate({
96 | if (is(to$m, "TidyModule")) {
97 | s <- to$m$getSession()
98 | e <- self$getEdges(to$m)
99 |
100 | toId <- to$m$module_ns
101 | tport <- to$port
102 | tname <- to$m$getPortName(to$port, to$type)
103 | ttype <- to$type
104 | tclass <- "TidyModule"
105 | }
106 |
107 | if (is(from$m, "TidyModule")) {
108 | if (is.null(s)) {
109 | s <- from$m$getSession()
110 | e <- self$getEdges(from$m)
111 | }
112 |
113 | fromId <- from$m$module_ns
114 | fport <- from$port
115 | fname <- from$m$getPortName(from$port, from$type)
116 | ftype <- from$type
117 | fclass <- "TidyModule"
118 |
119 | # Handle tidymodules derived ports
120 | } else if (!is.null(attr(from$m, "tidymodules")) &&
121 | attr(from$m, "tidymodules")) {
122 | mod <- attr(from$m, "tidymodules_operation")
123 | if (!is.null(mod) && mod == "combine") {
124 | mode <- mod
125 | combinedPorts <- reactiveValuesToList(from$m)
126 | for (key in names(combinedPorts)) {
127 | f <- combinedPorts[[key]]
128 | comment <- key
129 | fromId <- attr(f, "tidymodules_module_ns")
130 | fport <- attr(f, "tidymodules_port_id")
131 | ftype <- attr(f, "tidymodules_port_type")
132 | fname <- attr(f, "tidymodules_port_name")
133 | fclass <- "TidyModule"
134 |
135 | comb_row <- data.frame(
136 | from = fromId,
137 | fclass = fclass,
138 | fport = fport,
139 | ftype = ftype,
140 | fname = fname,
141 | to = toId,
142 | tclass = tclass,
143 | tport = tport,
144 | ttype = ttype,
145 | tname = tname,
146 | mode = mode,
147 | comment = comment
148 | )
149 |
150 | if (is.null(d)) {
151 | d <- comb_row
152 | } else {
153 | d <- rbind(d, comb_row)
154 | }
155 | }
156 | } else {
157 | fromId <- attr(from$m, "tidymodules_module_ns")
158 | fport <- attr(from$m, "tidymodules_port_id")
159 | ftype <- attr(from$m, "tidymodules_port_type")
160 | fname <- attr(from$m, "tidymodules_port_name")
161 | fclass <- "TidyModule"
162 | }
163 | } else if (is.reactive(from$m)) {
164 | fromId <- attr(from$m, "observable")$.reactId
165 | comment <- attr(from$m, "observable")$.label
166 | # support for previous shiny version that don't have reactId (don't work with shiny 1.0.5)
167 | if (is.null(fromId)) {
168 | fromId <- comment
169 | }
170 | fclass <- "reactive"
171 | } else {
172 | stop("Unknown 'from' entity in addEdge function ", class(from$m), "/n")
173 | }
174 |
175 | if (is.null(d)) {
176 | d <- data.frame(
177 | from = fromId,
178 | fclass = fclass,
179 | fport = fport,
180 | ftype = ftype,
181 | fname = fname,
182 | to = toId,
183 | tclass = tclass,
184 | tport = tport,
185 | ttype = ttype,
186 | tname = tname,
187 | mode = mode,
188 | comment = comment,
189 | stringsAsFactors = FALSE
190 | )
191 | }
192 |
193 | if (is.null(s) || s$sid == "global_session") {
194 | stop("addEdge function error! Module has no session or session is global [", s$sid, "]")
195 | }
196 |
197 | # track update time
198 | s$updated <- Sys.time()
199 |
200 | if (length(s$edges) == 0) {
201 | s$edges <- d
202 | } else {
203 | key <- paste0(as.character(d[1, ]), collapse = "|")
204 | keys <- apply(e, 1, paste0, collapse = "|")
205 | if (key %in% keys) {
206 | warning(paste0("Module mapping already exist!\n", key))
207 | } else {
208 | s$edges <- rbind(e, d)
209 | }
210 | }
211 | })
212 | },
213 | #' @description
214 | #' Remove module edges
215 | #' @param m TidyModule object.
216 | delEdges = function(m){
217 | isolate({
218 | s <- private$getS(m)
219 | ns <- as.character(m$module_ns)
220 | if (length(s$edges) != 0) {
221 | s$edges <- s$edges %>% filter(from != ns & to != ns)
222 | }
223 | })
224 | },
225 | #' @description
226 | #' Add module into the ModStore.
227 | #' @param m TidyModule object.
228 | addMod = function(m) {
229 | isolate({
230 | s <- private$getS(m)
231 | ns <- as.character(m$module_ns)
232 |
233 | # if(!is.null(s$collection[[ns]]))
234 | # stop(paste0("Module namespace ",ns," already stored!"))
235 | s$collection[[ns]] <- m
236 | if (!is.null(m$group)) {
237 | g <- as.character(m$group)
238 | if (is.null(s$g_collection[[g]])) {
239 | s$g_collection[[g]] <- list()
240 | }
241 | s$g_collection[[g]][[ns]] <- m
242 | }
243 | if (!is.null(m$parent_ns)) {
244 | p <- as.character(m$parent_ns)
245 | if (is.null(s$n_collection[[p]])) {
246 | s$n_collection[[p]] <- list()
247 | }
248 | s$n_collection[[p]][[ns]] <- m
249 | }
250 | # track update time
251 | s$updated <- Sys.time()
252 | # TODO : Do we really need this line below ?
253 | s$ns <- c(s$ns, as.character(m$module_ns))
254 | })
255 | },
256 | #' @description
257 | #' Delete a module from the ModStore.
258 | #' @param m TidyModule object.
259 | delMod = function(m) {
260 | isolate({
261 | s <- private$getS(m)
262 | ns <- as.character(m$module_ns)
263 | s$collection[[ns]] <- NULL
264 | if (!is.null(m$group)) {
265 | g <- as.character(m$group)
266 | if (!is.null(s$g_collection[[g]]))
267 | s$g_collection[[g]][[ns]] <- NULL
268 | }
269 | if (!is.null(m$parent_ns)) {
270 | p <- as.character(m$parent_ns)
271 | if (!is.null(s$n_collection[[p]]))
272 | s$n_collection[[p]][[ns]] <- NULL
273 | }
274 | s$ns <- s$ns[-grep(as.character(m$module_ns),s$ns)]
275 | # delete edges
276 | self$delEdges(m)
277 | # track update time
278 | s$updated <- Sys.time()
279 | })
280 | },
281 | #' @description
282 | #' Print the ModStore object.
283 | print = function() {
284 | aid <- private$getAID()
285 | isolate({
286 | str(private$sessions[[aid]]$global_session$collection)
287 | })
288 | }
289 | ),
290 | private = list(
291 | sessions = reactiveValues(),
292 | sessionExist = function(sid) {
293 | aid <- private$getAID()
294 | return(
295 | !is.null(private$sessions[[aid]]) &&
296 | !is.null(private$sessions[[aid]][[sid]])
297 | )
298 | },
299 | addSession = function(sid) {
300 | aid <- private$getAID()
301 | if (is.null(private$sessions[[aid]])) {
302 | private$sessions[[aid]] <- reactiveValues()
303 | }
304 |
305 | if (is.null(private$sessions[[aid]][[sid]])) {
306 | private$sessions[[aid]][[sid]] <- reactiveValues(
307 | aid = aid,
308 | path = getwd(),
309 | sid = sid,
310 | count = 0,
311 | created = Sys.time(),
312 | updated = Sys.time(),
313 | collection = list(),
314 | ns = c(),
315 | edges = data.frame()
316 | )
317 | } else {
318 | FALSE
319 | }
320 | },
321 | getS = function(m) {
322 | sid <- m
323 | if (is(m, "TidyModule")) {
324 | sid <- m$getSessionId()
325 | }
326 | aid <- private$getAID()
327 | if (!private$sessionExist(sid)) {
328 | private$addSession(sid)
329 | }
330 | return(private$sessions[[aid]][[sid]])
331 | },
332 | getAID = function() {
333 | return(digest::digest(getwd(), algo = "md5"))
334 | }
335 | )
336 | )
337 |
--------------------------------------------------------------------------------
/R/Store.R:
--------------------------------------------------------------------------------
1 |
2 | #'
3 | #' This TidyModule is used to explore the content of the ModStore.
4 | #'
5 | #' @description
6 | #' Store is a TidyModule that can be used in your application to list existing applications, sessions and display your session's modules and edges.
7 | #'
8 | #' @details
9 | #' Should be initialized and injected in your application.
10 | #'
11 | #' @export
12 | Store <- R6::R6Class(
13 | classname = "Store",
14 | inherit = TidyModule,
15 | public = list(
16 | #' @description
17 | #' Store's ui function.
18 | #' @return UI elements.
19 | ui = function() {
20 | tagList(
21 | tabsetPanel(
22 | id = "store_ID",
23 | type = "tabs",
24 | tabPanel(
25 | "Sessions",
26 | fluidRow(
27 | br(),
28 | DT::dataTableOutput(self$ns("sessions"))
29 | )
30 | ),
31 | tabPanel(
32 | "Mods",
33 | fluidRow(
34 | br(),
35 | DT::dataTableOutput(self$ns("mods"))
36 | )
37 | ),
38 | tabPanel(
39 | "Edges",
40 | fluidRow(
41 | br(),
42 | DT::dataTableOutput(self$ns("edges"))
43 | )
44 | ),
45 | tabPanel(
46 | "Port Mapping",
47 | fluidRow(
48 | br(),
49 | visNetwork::visNetworkOutput(self$ns("portD"), width = "100%", height = "800px")
50 | )
51 | )
52 | )
53 | )
54 | },
55 | #' @description
56 | #' Store's server function.
57 | #' @param input Shiny input.
58 | #' @param output Shiny output
59 | #' @param session Shiny session
60 | server = function(input, output, session) {
61 | # Mandatory
62 | super$server(input, output, session)
63 |
64 | self$react$session_df <- reactive({
65 | s <- self$getStore()
66 | d <- data.frame(aid = NULL, path = NULL, sid = NULL, created = NULL, mod_cnt = NULL, edge_cnt = NULL)
67 |
68 | for (aid in names(s$getSessions())) {
69 | for (sid in names(s$getSessions()[[aid]])) {
70 | ses <- s$getSessions()[[aid]][[sid]]
71 | mcount <- length(ses$collection)
72 | ecount <- nrow(ses$edges)
73 | d <- rbind(d, data.frame(
74 | aid = aid,
75 | path = ses$path,
76 | sid = sid,
77 | created = ses$created,
78 | updated = ses$updated,
79 | mod_cnt = mcount,
80 | edge_cnt = ecount
81 | ))
82 | }
83 | }
84 | rownames(d) <- NULL
85 |
86 | d
87 | })
88 |
89 | self$react$mods_df <- reactive({
90 | s <- self$getStore()
91 | d <- do.call(
92 | rbind,
93 | lapply(
94 | s$getMods(self),
95 | function(l) {
96 | data.frame(
97 | namespace = l$module_ns,
98 | class = paste(class(l), collapse = " <- "),
99 | parent = ifelse(is.null(l$parent_ns), "", l$parent_ns),
100 | created = l$created,
101 | in_ports = l$countInputPort(),
102 | out_ports = l$countOutputPort()
103 | )
104 | }
105 | )
106 | )
107 |
108 | rownames(d) <- seq_len(nrow(d))
109 |
110 | d
111 | })
112 |
113 | self$react$edges_df <- reactive({
114 | s <- self$getStore()
115 | e <- s$getEdges(self)
116 | req(nrow(e) != 0)
117 |
118 | e
119 | })
120 |
121 | output$sessions <- DT::renderDataTable({
122 | self$react$session_df()
123 | })
124 |
125 | output$edges <- DT::renderDataTable({
126 | self$react$edges_df()
127 | })
128 |
129 | output$mods <- DT::renderDataTable({
130 | self$react$mods_df()
131 | })
132 |
133 | output$portD <- visNetwork::renderVisNetwork({
134 | edges <- self$react$edges_df()
135 | nodes <- self$react$mods_df()
136 |
137 | e <- edges %>%
138 | mutate(
139 | font.size = 5,
140 | label = paste0(fport, " ", mode, ifelse(is.na(comment), "", paste0("(", comment, ")")), " ", tport)
141 | ) %>%
142 | select(from, to, label, font.size)
143 |
144 | # # minimal example
145 | # nodes <- data.frame(id = 1:3)
146 | # edges <- data.frame(from = c(1,2), to = c(1,3))
147 |
148 | nId <- c(as.vector(e$from), as.vector(e$to), as.vector(nodes$namespace)) %>% unique()
149 | nType <- rbind(
150 | data.frame(name = edges$from, class = edges$fclass),
151 | data.frame(name = edges$to, class = edges$tclass)
152 | ) %>% unique()
153 | nClass <- as.character(nType[match(nId, nType$name), "class"])
154 | nShape <- ifelse(nClass == "TidyModule" | is.na(nClass), "square",
155 | ifelse(nClass == "reactive", "box", "box")
156 | )
157 | nColor <- ifelse(nClass == "TidyModule" | is.na(nClass), "lightblue",
158 | ifelse(nClass == "reactive", "orange", "grey")
159 | )
160 |
161 | nGroup <- ifelse(nClass == "TidyModule" | is.na(nClass), "A",
162 | ifelse(nClass == "reactive", "B", "C")
163 | )
164 |
165 | visNetwork::visNetwork(
166 | data.frame(
167 | id = nId,
168 | label = nId,
169 | group = nGroup,
170 | shape = nShape,
171 | # color = nColor,
172 | shadow = TRUE,
173 | value = 10
174 | ),
175 | e,
176 | height = "100%",
177 | width = "100%"
178 | ) %>%
179 | visNetwork::visEdges(
180 | shadow = TRUE,
181 | arrows = list(to = list(enabled = TRUE, scaleFactor = 2)),
182 | color = list(color = "lightblue", highlight = "yellow")
183 | ) %>%
184 | # visHierarchicalLayout(direction = "RL", levelSeparation = 500)
185 | visNetwork::visLayout(randomSeed = 12)
186 | })
187 | }
188 | )
189 | )
190 |
--------------------------------------------------------------------------------
/R/UtilityModule.R:
--------------------------------------------------------------------------------
1 |
2 | #' TidyModule utility class
3 | #'
4 | #' @description
5 | #' A module used by some of the utility functions to retrieve the ModStore object.
6 | #'
7 | #' @details
8 | #' This utility module is a special TidyModule class that doesn't get registered in the ModStore.
9 | #' It is used to retrieve ModStore objects, like sessions and modules.
10 | #' @examples
11 | #' \dontrun{
12 | #' # Print current session Id
13 | #' UtilityModule$new()$getSessionId()
14 | #' }
15 | UtilityModule <- R6::R6Class(
16 | "UtilityModule",
17 | inherit = TidyModule,
18 | public = list(
19 | #' @description
20 | #' Initialize function.
21 | #' @return `UtilityModule` object.
22 | initialize = function() {
23 | if (is.null(private$shared$store)) {
24 | private$shared$store <- ModStore$new()
25 | }
26 |
27 | #### Set Shiny Session Here #######
28 | private$shiny_session <- shiny::getDefaultReactiveDomain()
29 | }
30 | )
31 | )
32 |
--------------------------------------------------------------------------------
/R/add_module.R:
--------------------------------------------------------------------------------
1 | #' Create a module
2 | #'
3 | #' This function creates a `{tm}` module class inside the current folder.
4 | #'
5 | #' @param name The class name of the module.
6 | #' @param path Where to created the file. Default is `getwd()`. The function will add `R` to the path if the sub-folder exists.
7 | #' @param prefix filename prefix. Default is `tm`. Set to `NULL`` to disable.
8 | #' @param inherit Parent module class. Default is TidyModule.
9 | #' @param open Should the file be opened?
10 | #' @param dir_create Creates the directory if it doesn't exist, default is `TRUE`.
11 | #' @param export Logical. Should the module be exported? Default is `FALSE`.
12 | #' @note As a convention, this function will automatically capitalize the first character of the `name` argument.
13 | #'
14 | #' @importFrom cli cat_bullet
15 | #' @importFrom utils file.edit
16 | #' @importFrom fs path_abs path file_create
17 | #' @importFrom snippr snippets_read
18 | #'
19 | #' @export
20 | add_module <- function(name,
21 | inherit = "TidyModule",
22 | path = getwd(),
23 | prefix = "tm",
24 | open = TRUE,
25 | dir_create = TRUE,
26 | export = FALSE) {
27 | name <- file_path_sans_ext(name)
28 | # Capitalize
29 | name <- paste0(toupper(substring(name, 1, 1)), substring(name, 2))
30 |
31 | dir_created <- create_if_needed(
32 | fs::path(path),
33 | type = "directory"
34 | )
35 | if (!dir_created) {
36 | cat_red_bullet(
37 | "File not added (needs a valid directory)"
38 | )
39 | return(invisible(FALSE))
40 | }
41 |
42 | if (dir.exists(fs::path(path, "R"))) {
43 | path <- fs::path(path, "R")
44 | }
45 |
46 | old <- setwd(path_abs(path))
47 | on.exit(setwd(old))
48 |
49 | where <- fs::path(
50 | paste0(ifelse(is.null(prefix), "", paste0(prefix, "_")), name, ".R")
51 | )
52 |
53 | if (!check_file_exist(where)) {
54 | cat_red_bullet(
55 | "File not created (already exists)"
56 | )
57 | return(invisible(FALSE))
58 | }
59 |
60 | # make sure the provided parent module is valid
61 | import <- NULL
62 | parent <- inherit
63 | # TidyModule object
64 | if (is(parent, "TidyModule")) {
65 | parent <- class(parent)[1]
66 | }
67 | # Load the class generator from the name
68 | if (class(parent) == "character") {
69 | tryCatch(
70 | {
71 | parent <- eval(parse(text = parent))
72 | },
73 | error = function(e) {
74 | cat_red_bullet(
75 | paste0("Could not find module defined with 'inherit' = ", inherit)
76 | )
77 | return(invisible(FALSE))
78 | }
79 | )
80 | }
81 | # Retrieve package dependency and parent module name
82 | if (is(parent, "R6ClassGenerator")) {
83 | clist <- get_R6CG_list(parent)
84 | if ("TidyModule" %in% clist) {
85 | import <- environmentName(parent$parent_env)
86 | if (import == "R_GlobalEnv") {
87 | import <- NULL
88 | }
89 | parent <- clist[1]
90 | } else {
91 | cat_red_bullet(
92 | paste0("Could not find module defined with 'inherit' = ", deparse(substitute(inherit)))
93 | )
94 | return(invisible(FALSE))
95 | }
96 | }
97 |
98 |
99 | # Retrieve content from package snippet
100 | file_content <- snippr::snippets_read(path = system.file("rstudio/r.snippets", package = "tidymodules"))$tm.mod.new
101 | file_content <- unlist(strsplit(file_content, "\\n"))
102 | for (l in seq_len(length(file_content))) {
103 | # remove $ escapes \\
104 | file_content[l] <- sub("\\$", "$", file_content[l], fixed = TRUE)
105 | # remove tabs
106 | file_content[l] <- sub("\t", "", file_content[l])
107 | # remove snippet placeholders
108 | file_content[l] <- gsub("\\$\\{\\d+:(\\w+)\\}", "%\\1", file_content[l])
109 | # remove cursor pointer
110 | file_content[l] <- sub("\\$\\{0\\}", "", file_content[l])
111 | # substitute module name
112 | if (grepl("MyModule", file_content[l])) {
113 | file_content[l] <- gsub("MyModule", "s", file_content[l])
114 | file_content[l] <- sprintf(file_content[l], name)
115 | }
116 | # substitute parent module
117 | if (grepl("TidyModule", file_content[l])) {
118 | file_content[l] <- gsub("TidyModule", "s", file_content[l])
119 | file_content[l] <- sprintf(file_content[l], parent)
120 | }
121 | # manage export
122 | if (grepl("@export", file_content[l])) {
123 | if (!export) {
124 | file_content[l] <- "#' @noRd "
125 | }
126 | if (!is.null(import)) {
127 | file_content[l] <- paste0("#'\n#' @import ", import, "\n", file_content[l])
128 | }
129 | }
130 | }
131 | writeLines(file_content, where, sep = "\n")
132 |
133 | cat_created(fs::path(path, where))
134 | open_or_go_to(where, open)
135 | }
136 |
137 | # bunch of utility functions copied from golem
138 | # WILL FACILITATE MIGRATING THIS FUNCTION TO GOLEM
139 |
140 | #' @importFrom utils menu
141 | yesno <- function(...) {
142 | cat(paste0(..., collapse = ""))
143 | menu(c("Yes", "No")) == 1
144 | }
145 |
146 | #' @importFrom fs file_exists
147 | check_file_exist <- function(file) {
148 | res <- TRUE
149 | if (file_exists(file)) {
150 | cat_orange_bullet(file)
151 | res <- yesno("This file already exists, override?")
152 | }
153 | return(res)
154 | }
155 |
156 | #' @importFrom fs dir_create file_create
157 | create_if_needed <- function(path,
158 | type = c("file", "directory"),
159 | content = NULL) {
160 | type <- match.arg(type)
161 | # Check if file or dir already exist
162 | if (type == "file") {
163 | dont_exist <- file_not_exist(path)
164 | } else if (type == "directory") {
165 | dont_exist <- dir_not_exist(path)
166 | }
167 | # If it doesn't exist, ask if we are allowed
168 | # to create it
169 | if (dont_exist) {
170 | ask <- yesno(
171 | sprintf(
172 | "The %s %s doesn't exist, create?",
173 | basename(path),
174 | type
175 | )
176 | )
177 | # Return early if the user doesn't allow
178 | if (!ask) {
179 | return(FALSE)
180 | } else {
181 | # Create the file
182 | if (type == "file") {
183 | if (dir_not_exist(dirname(path))) {
184 | dir_create(dirname(path), recurse = TRUE)
185 | }
186 | file_create(path)
187 | write(content, path, append = TRUE)
188 | } else if (type == "directory") {
189 | dir_create(path, recurse = TRUE)
190 | }
191 | }
192 | }
193 |
194 | # TRUE means that file exists (either
195 | # created or already there)
196 | return(TRUE)
197 | }
198 |
199 |
200 | #' @importFrom cli cat_bullet
201 | cat_green_tick <- function(...) {
202 | cat_bullet(
203 | ...,
204 | bullet = "tick",
205 | bullet_col = "green"
206 | )
207 | }
208 |
209 | #' @importFrom cli cat_bullet
210 | cat_red_bullet <- function(...) {
211 | cat_bullet(
212 | ...,
213 | bullet = "bullet",
214 | bullet_col = "red"
215 | )
216 | }
217 |
218 | #' @importFrom cli cat_bullet
219 | cat_orange_bullet <- function(...) {
220 | cat_bullet(
221 | ...,
222 | bullet = "bullet",
223 | bullet_col = "orange"
224 | )
225 | }
226 |
227 | #' @importFrom cli cat_bullet
228 | cat_info <- function(...) {
229 | cat_bullet(
230 | ...,
231 | bullet = "arrow_right",
232 | bullet_col = "grey"
233 | )
234 | }
235 |
236 | #' @importFrom fs path_file
237 | cat_exists <- function(where) {
238 | cat_red_bullet(
239 | sprintf(
240 | "%s already exists, skipping the copy.",
241 | path_file(where)
242 | )
243 | )
244 | cat_info(
245 | sprintf(
246 | "If you want replace it, remove the %s file first.",
247 | path_file(where)
248 | )
249 | )
250 | }
251 |
252 | cat_created <- function(where,
253 | file = "File") {
254 | cat_green_tick(
255 | sprintf(
256 | "%s created at %s",
257 | file,
258 | where
259 | )
260 | )
261 | }
262 |
263 | open_or_go_to <- function(where,
264 | open) {
265 | if (
266 | rstudioapi::isAvailable() &&
267 | open &&
268 | rstudioapi::hasFun("navigateToFile")
269 | ) {
270 | rstudioapi::navigateToFile(where)
271 | } else {
272 | cat_red_bullet(
273 | sprintf(
274 | "Go to %s",
275 | where
276 | )
277 | )
278 | }
279 | }
280 |
281 | desc_exist <- function(pkg) {
282 | file_exists(
283 | paste0(pkg, "/DESCRIPTION")
284 | )
285 | }
286 |
287 |
288 | file_created_dance <- function(where,
289 | fun,
290 | pkg,
291 | dir,
292 | name,
293 | open) {
294 | cat_created(where)
295 |
296 | fun(pkg, dir, name)
297 |
298 | open_or_go_to(where, open)
299 | }
300 |
301 | if_not_null <- function(x, ...) {
302 | if (!is.null(x)) {
303 | force(...)
304 | }
305 | }
306 |
307 | set_name <- function(x, y) {
308 | names(x) <- y
309 | x
310 | }
311 |
312 | # FROM tools::file_path_sans_ext() & tools::file_ext
313 | file_path_sans_ext <- function(x) {
314 | sub("([^.]+)\\.[[:alnum:]]+$", "\\1", x)
315 | }
316 |
317 | file_ext <- function(x) {
318 | pos <- regexpr("\\.([[:alnum:]]+)$", x)
319 | ifelse(pos > -1L, substring(x, pos + 1L), "")
320 | }
321 |
322 | #' @importFrom fs dir_exists file_exists
323 | dir_not_exist <- Negate(dir_exists)
324 | file_not_exist <- Negate(file_exists)
325 |
--------------------------------------------------------------------------------
/R/examples.R:
--------------------------------------------------------------------------------
1 | #' @title Launcher for the tidymodules examples
2 | #'
3 | #' @description Helper function to launch the tidymodules examples.
4 | #'
5 | #' @param id Example ID. If null display list of examples with ID.
6 | #' @param server boolean. Is this a server call?
7 | #' @param options list of options to be passed to shinyApps or shinyDir
8 | #'
9 | #' @export
10 | #'
11 | #' @examples
12 | #'
13 | #' if (interactive()) {
14 | #' showExamples(1)
15 | #' }
16 | showExamples <- function(id = NULL, server = F, options = NULL) {
17 | examples <- list.dirs(system.file(package = "tidymodules", "shiny/examples"), recursive = F)
18 | if (is.null(id)) {
19 | names(examples) <- seq_len(length(examples))
20 | basename(examples)
21 | } else {
22 | if (!is.numeric(id)) {
23 | stop("Please provide a numeric value")
24 | }
25 | if (id > length(examples) || id < 1) {
26 | stop("Wrong ID provided")
27 | }
28 |
29 | if (server) {
30 | setwd(examples[id])
31 | if (!is.null(options)) {
32 | shiny::shinyAppDir(examples[id], options = options)
33 | } else {
34 | shiny::shinyAppDir(examples[id])
35 | }
36 | } else {
37 | shiny::runApp(examples[id])
38 | }
39 | }
40 | }
41 |
42 | #' @title check if list of package namespaces exist, load them or display relevant information
43 | #'
44 | #' @description Utility function for managing package dependencies for tidymodules examples
45 | #'
46 | #' @param packages character vector of package names
47 | #'
48 | #' @export
49 | #'
50 | #' @examples
51 | #'
52 | #' check_and_load("ggplot2")
53 | #'
54 | check_and_load <- function(packages) {
55 | missing <- NULL
56 | for (p in packages) {
57 | if (!requireNamespace(p, quietly = TRUE)) {
58 | missing <- c(missing, p)
59 | } else {
60 | library(p, character.only = TRUE)
61 | }
62 | }
63 |
64 | if (!is.null(missing)) {
65 | stop(
66 | "The package(s) above are needed for this shiny example to work, please install them first...\n",
67 | cli::cat_bullet(missing, bullet_col = "red"),
68 | call. = FALSE
69 | )
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/R/snippets.R:
--------------------------------------------------------------------------------
1 | #'
2 | #' @title Add `{tm}` snippets to RStudio
3 | #'
4 | #' @description This function adds useful `{tm}` code snippets to RStudio.
5 | #'
6 | #' @param force Force the re-installation when the snippets are already installed.
7 | #'
8 | #' @import snippr
9 | #' @import dplyr
10 | #' @importFrom fs path_home_r path_ext_set
11 | #' @importFrom cli cat_bullet
12 | #' @importFrom purrr keep discard map
13 | #' @export
14 | add_tm_snippets <- function(force = FALSE) {
15 | # R snippets file
16 | path <- path_home_r(".R", "snippets", path_ext_set("r", "snippets"))
17 |
18 | if (!create_if_needed(path)) {
19 | cat_bullet("Skip installation of snippets",
20 | bullet_col = "red",
21 | bullet = "bullet"
22 | )
23 | }
24 |
25 | # retrieve current and new snippets
26 | current_all_snippets <- snippets_get(path = path)
27 | current_non_tm_snippets <- current_all_snippets[!grepl("^tm\\.", names(current_all_snippets), perl = TRUE)]
28 | current_tm_snippets <- current_all_snippets[grepl("^tm\\.", names(current_all_snippets), perl = TRUE)]
29 | new_tm_snippets <- snippets_read(path = system.file("rstudio/r.snippets", package = "tidymodules"))
30 | # calculate differences
31 | del_snippets <- setdiff(names(current_tm_snippets), names(new_tm_snippets))
32 | keep_snippets <- intersect(names(current_tm_snippets), names(new_tm_snippets))
33 | add_snippets <- setdiff(names(new_tm_snippets), names(current_tm_snippets))
34 | # print some informations
35 | if (length(del_snippets) > 0) {
36 | cat_bullet(paste0("Deleting ", length(del_snippets), " snippet(s):"),
37 | bullet_col = "orange",
38 | bullet = "bullet"
39 | )
40 | invisible(map(del_snippets, cat_bullet, bullet = "dot"))
41 | }
42 | existing_snippets <- current_non_tm_snippets
43 | save_snippets <- NULL
44 | if (length(keep_snippets) > 0) {
45 | if (force) {
46 | cat_bullet(paste0("Re-installing ", length(keep_snippets), " existing snippet(s):"),
47 | bullet_col = "green",
48 | bullet = "tick"
49 | )
50 | invisible(map(keep_snippets, cat_bullet, bullet = "dot"))
51 | save_snippets <- new_tm_snippets[keep_snippets]
52 | } else {
53 | cat_bullet(paste0("Skip installation of ", length(keep_snippets), " existing snippet(s):"),
54 | bullet_col = "red",
55 | bullet = "bullet"
56 | )
57 | invisible(map(keep_snippets, cat_bullet, bullet = "dot"))
58 | existing_snippets <- c(existing_snippets, current_tm_snippets[keep_snippets])
59 | }
60 | }
61 | if (length(add_snippets) > 0) {
62 | cat_bullet(paste0("Installing ", length(add_snippets), " new snippets:"),
63 | bullet_col = "green",
64 | bullet = "tick"
65 | )
66 | invisible(map(add_snippets, cat_bullet, bullet = "dot"))
67 | save_snippets <- c(save_snippets, new_tm_snippets[add_snippets])
68 | }
69 |
70 | final_snippets <- existing_snippets
71 | if (!is.null(save_snippets)) {
72 | final_snippets <- c(final_snippets, save_snippets)
73 | }
74 |
75 | snippets_write(final_snippets, path = path)
76 | }
77 |
--------------------------------------------------------------------------------
/R/tidymodules.R:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Novartis AG
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | #' @importFrom methods is
16 | #'
17 | #' @keywords internal
18 | "_PACKAGE"
19 |
--------------------------------------------------------------------------------
/R/utility.R:
--------------------------------------------------------------------------------
1 |
2 | #'
3 | #' @title Retrieve module from ModStore
4 | #'
5 | #' @description This utility function retrieve tidymodules from the central ModStore
6 | #' using module namespace/id and/or group
7 | #'
8 | #' @param id Name or Id of the module
9 | #' @param group Group name
10 | #'
11 | #' @import shiny
12 | #'
13 | #' @export
14 | #'
15 | #' @examples
16 | #'
17 | #' MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
18 | #' MyModule$new("MyFirst")
19 | #' MyModule$new("MySecond")
20 | #' MyModule$new("MyThird", group = "B")
21 | #'
22 | #' # MyFirst
23 | #' getMod(1)
24 | #' getMod("MyFirst")
25 | #'
26 | #' # MySecond
27 | #' getMod(2)
28 | #'
29 | #' # MyThird
30 | #' getMod(2)
31 | #' getMod("B-MyThird")
32 | #' getMod(1, group = "B")
33 | #'
34 | getMod <- function(id = 1, group = NULL) {
35 | m <- UtilityModule$new()
36 | mod <- NULL
37 | c <- isolate(m$getSession()$collection)
38 | gc <- isolate(m$getSession()$g_collection)
39 |
40 | if (!is.null(group) && !is.numeric(id)) {
41 | id <- paste0(id, "-G-", group)
42 | }
43 |
44 | if (is.null(group)) {
45 | mod <- c[[id]]
46 | } else {
47 | mod <- gc[[group]][[id]]
48 | }
49 |
50 | if (is.null(mod)) {
51 | warning(paste0("Module ", id, " not found!"))
52 | }
53 |
54 | mod
55 | }
56 | #'
57 | #' @title Alias to getMod
58 | #'
59 | #' @description See \code{\link{getMod}}
60 | #'
61 | #' @param id Name or Id of the module
62 | #' @param group Group name
63 | #'
64 | #' @import shiny
65 | #'
66 | #' @export
67 | mod <- function(id = 1, group = NULL) {
68 | getMod(id, group)
69 | }
70 |
71 | #'
72 | #' @title Retrieve module's port
73 | #'
74 | #' @description This utility function retrieve the tidymodules port specified in the arguments.
75 | #'
76 | #' @param id Name or Id of the module
77 | #' @param g Module group name
78 | #' @param t Port type, in or out
79 | #' @param p Port Id or name
80 | #'
81 | #' @import shiny
82 | #'
83 | #' @export
84 | port <- function(id = 1, p = 1, t = "in", g = NULL) {
85 | m <- getMod(id, g)
86 | if (is.null(m)) {
87 | return(NULL)
88 | } else {
89 | if (t == "in") {
90 | return(m$getInputPort(p))
91 | } else {
92 | return(m$getOutputPort(p))
93 | }
94 | }
95 | }
96 | #'
97 | #' @title Retrieve input module's port
98 | #'
99 | #' @description This utility function retrieve the tidymodules input port specified in the arguments.
100 | #'
101 | #' @param id Name or Id of the module
102 | #' @param g Module group name
103 | #' @param p Port Id or name
104 | #'
105 | #' @import shiny
106 | #'
107 | #' @export
108 | iport <- function(id = 1, p = 1, g = NULL) {
109 | port(id, p, "in", g)
110 | }
111 | #'
112 | #' @title Retrieve output module's port
113 | #'
114 | #' @description This utility function retrieve the tidymodules output port specified in the arguments.
115 | #'
116 | #' @param id Name or Id of the module
117 | #' @param g Module group name
118 | #' @param p Port Id or name
119 | #'
120 | #' @import shiny
121 | #'
122 | #' @export
123 | oport <- function(id = 1, p = 1, g = NULL) {
124 | port(id, p, "out", g)
125 | }
126 |
127 | #'
128 | #' @title List modules in current session
129 | #'
130 | #' @description This function list module objects found in the current session
131 | #'
132 | #' @param verbose Display module description as well
133 | #' @param global use the global session? Default to FALSE
134 | #'
135 | #' @importFrom cli cat_bullet cat_boxx
136 | #' @importFrom utils capture.output
137 | #' @import shiny
138 | #'
139 | #' @export
140 | listModules <- function(verbose = FALSE, global = FALSE) {
141 | currentSession <- UtilityModule$new()$getSession()
142 | if(global)
143 | currentSession <- UtilityModule$new()$getGlobalSession()
144 | isolate({
145 | if (length(currentSession$collection) == 0) {
146 | cat_bullet(paste0("No module found!"),
147 | bullet_col = "orange",
148 | bullet = "cross"
149 | )
150 | } else {
151 | cat_bullet(paste0("Found ", length(currentSession$collection), " module(s)!"),
152 | bullet_col = "green",
153 | bullet = "tick"
154 | )
155 | }
156 | invisible(for (mod in currentSession$collection) {
157 | cat_bullet(mod$module_ns, bullet = "circle_dotted")
158 | if (verbose) {
159 | cat_boxx(capture.output(mod))
160 | }
161 | })
162 | })
163 | }
164 |
165 | #'
166 | #' @title Call modules function
167 | #'
168 | #' @description This utility function call all modules initialized in the global session.
169 | #' The global session is the session shared outside the server function of the application.
170 | #' All the modules initialized in the global session can be called with this function in a single call.
171 | #' The function take care of cloning and attaching them to the current user session.
172 | #'
173 | #' Note that this function can only be called in the app server function at the moment.
174 | #' We are working on supporting callModules within module server function for invoking nested modules.
175 | #'
176 | #'
177 | #' @import shiny
178 | #'
179 | #' @export
180 | callModules <- function() {
181 | currentSession <- UtilityModule$new()$getSession()
182 | globalSession <- UtilityModule$new()$getGlobalSession()
183 | disable_cache <- getCacheOption()
184 |
185 | calls <- c()
186 |
187 | isolate({
188 | # re-initialize current session
189 | currentSession$edges <- data.frame()
190 | currentSession$count <- globalSession$count
191 |
192 | lapply(globalSession$collection, function(mod) {
193 | if (is.null(currentSession$collection[[mod$module_ns]]) || disable_cache) {
194 | ######## Try to capture server function arguments here ########
195 | serverEnv <- parent.frame(3)
196 | o <- i <- s <- NULL
197 | if (!is.null(serverEnv)) {
198 | if (!is.null(serverEnv$input) &&
199 | is(serverEnv$input, "reactivevalues")) {
200 | i <- serverEnv$input
201 | }
202 | if (!is.null(serverEnv$output) &&
203 | is(serverEnv$output, "shinyoutput")) {
204 | o <- serverEnv$output
205 | }
206 | if (!is.null(serverEnv$session) &&
207 | is(serverEnv$session, "ShinySession")) {
208 | s <- serverEnv$session
209 | }
210 | if (is.null(s)) {
211 | s <- getDefaultReactiveDomain()
212 | }
213 | }
214 | cloned <- mod$deepClone(o, i, s)
215 | }
216 | # Don't invoke nested modules as they will be invoked by parents
217 | # TODO : Change function to allow callModules within Module server (inject nested modules)
218 | if (is.null(currentSession$collection[[mod$module_ns]]$parent_ns)) {
219 | calls <<- c(calls, currentSession$collection[[mod$module_ns]])
220 | }
221 | })
222 | })
223 | lapply(calls, function(m) m$callModule())
224 | }
225 | #'
226 | #' @title Function wrapper for ports connection expression.
227 | #'
228 | #' @description Used in server functions to define how modules are connected to each other.
229 | #'
230 | #' @param x expression
231 | #'
232 | #' @import shiny
233 | #'
234 | #' @export
235 | defineEdges <- function(x) {
236 | observe({
237 | isolate(x)
238 | })
239 | }
240 |
241 |
242 | #'
243 | #' @title Retrieve cache option from the environment
244 | #'
245 | #' @description The cache option `tm_disable_cache` is a global options that enable or disable the use of existing modules from the current session.
246 | #' This option is `FALSE` by default and should be used in concordance with the `tm_session_type` global option. See \code{\link{session_type}} for a list of possible session type.
247 | #'
248 | #' @export
249 | getCacheOption <- function() {
250 | disable_cache <- getOption("tm_disable_cache")
251 | if (is.null(disable_cache)) {
252 | disable_cache <- FALSE
253 | }
254 | disable_cache <- as.logical(disable_cache)
255 |
256 | if (is.na(disable_cache)) {
257 | stop("Option 'tm_disable_cache' should be set to a logical value or unset.")
258 | }
259 |
260 | disable_cache
261 | }
262 | #'
263 | #' @title List of possible session types
264 | #'
265 | #' @description tidymodules offers the ability to manage application sessions.
266 | #' At the moment the three options below are available.
267 | #'
268 | #' \itemize{
269 | #'
270 | #' \item{SHINY}{ : The default behaviour of shiny application and the default for tidymodules. Every time you access an application
271 | #' you get a new token Id that defines your application user session.}
272 | #'
273 | #' \item{USER}{ : This method defines a session based on the information available in the request object of shiny output.
274 | #' It is a concatenation of the variables REMOTE_ADDR, HTTP_HOST and PATH_INFO like below.
275 | #'
276 | #' \code{sid <- paste0(r$REMOTE_ADDR,"@",r$HTTP_HOST,r$PATH_INFO))}
277 | #'
278 | #' Note that the method is actually not working properly for now as the information available via the request object
279 | #' are not reflecting the actual user. We are working on a better method to uniquely identify a remote user.}
280 | #'
281 | #' \item{CUSTOM}{ : This method allow the developper to provide a custom function for generating the session Id.
282 | #' It relies on the global options `tm_session_custom` being set and pointing to a function taking a shiny output as argument.}
283 | #'
284 | #' }
285 | #'
286 | #' @export
287 | session_type <- list(
288 | SHINY = 1,
289 | USER = 2,
290 | CUSTOM = 3
291 | )
292 |
293 | #'
294 | #' @title tidymodules options
295 | #'
296 | #' @name global_options
297 | #'
298 | #' @description List of global options used to adjust tidymodules configuration.
299 | #'
300 | #' \itemize{
301 | #' \item{**tm_session_type**}{ : Define the type of the session, See available session types in \code{\link{session_type}} }
302 | #' \item{**tm_session_custom**}{ : Used to set a custom function for generating the session Id. Used in concordance with the `CUSTOM` session type.}
303 | #' \item{**tm_disable_cache**}{ : Disable caching of modules. This option is set to FALSE by default but is only relevant when user's session is managed properly. See also \code{\link{getCacheOption}}}
304 | #' }
305 | #'
306 | #' @rdname global_options
307 | #'
308 | NULL
309 |
310 | #'
311 | #' @title Function that generates session Id
312 | #'
313 | #' @description tidymodules offers the ability to manage application sessions.
314 | #' This function is the main function used by tidymodules to find the current session Id.
315 | #' It takes an optional ShinySession object as argument. If null, default to the global_session.
316 | #'
317 | #' @param session A shiny session as provide by the shiny server function.
318 | #'
319 | #' @return A session ID
320 | #'
321 | #' @import shiny
322 | #'
323 | #' @export
324 | getSessionId <- function(session = getDefaultReactiveDomain()) {
325 | if (is.null(session)) {
326 | return("global_session")
327 | } else {
328 | stype <- getOption("tm_session_type")
329 | sid <- NULL
330 | if (is.null(stype)) {
331 | stype <- session_type$SHINY
332 | }
333 | switch(stype,
334 | # SHINY
335 | {
336 | sid <- session$token
337 | },
338 | # USER
339 | {
340 | r <- session$request
341 | sid <- paste0(r$REMOTE_ADDR, "@", r$HTTP_HOST, r$PATH_INFO)
342 | },
343 | # CUSTOM
344 | {
345 | fct <- getOption("tm_session_custom")
346 | if (is.null(fct) || class(fct) != "function") {
347 | stop("Option 'tm_session_custom' should be set to a function taking a ShinySession object as option and generating a custom session ID used by tidymodules to identify module sessions.")
348 | }
349 | sid <- fct(session)
350 | }
351 | )
352 | return(sid)
353 | }
354 | }
355 |
356 |
357 | #'
358 | #' @title Recursive function for retrieving R6ClassGenerator inheritance
359 | #'
360 | #' @description This function is used to retrieve a list of class name that a R6ClassGenerator object inherit from.
361 | #'
362 | #' @param r6cg A R6ClassGenerator object.
363 | #'
364 | #' @return vector of class names
365 | #'
366 | #' @keywords internal
367 | get_R6CG_list <- function(r6cg) {
368 | if (!is(r6cg, "R6ClassGenerator")) {
369 | stop("provide a R6ClassGenerator object!")
370 | }
371 | clist <- r6cg$classname
372 | if (!is.null(r6cg$get_inherit())) {
373 | clist <- c(clist, get_R6CG_list(r6cg$get_inherit()))
374 | }
375 |
376 | return(clist)
377 | }
378 |
--------------------------------------------------------------------------------
/R/verbs.R:
--------------------------------------------------------------------------------
1 | #' connect ports from two different modules
2 | #'
3 | #'
4 | #' @title Ports mapping function
5 | #'
6 | #' @description This function maps a module's outpout port to another module's input port.
7 | #'
8 | #' @param leftModule The left module object
9 | #' @param leftPort Port name or Id of the left module's output port
10 | #' @param rightModule The right module object
11 | #' @param rightPort Port name or Id of the right module's input port
12 | #' @param reverse ligical value indicating which module to return. Default to FALSE, the right module
13 | #'
14 | #' @export
15 | map_ports <- function(leftModule = NULL, leftPort = 1,
16 | rightModule = NULL, rightPort = 1,
17 | reverse = FALSE) {
18 | if (!is.numeric(leftPort)) {
19 | stop("Left port ID 'leftPort' should be numeric")
20 | }
21 | if (!is.numeric(rightPort)) {
22 | stop("Right port ID 'rightPort' should be numeric")
23 | }
24 |
25 | fct <- make_double_pipe(leftPort, rightPort, rev = reverse)
26 | fct(leftModule, rightModule)
27 | }
28 |
29 |
30 | #' @title Combine ports function
31 | #'
32 | #' @description This function combines ports into a reactive list (reactiveValues)
33 | #'
34 | #' @param ... key/value pairs of ports
35 | #'
36 | #' @examples
37 | #' \dontrun{
38 | #' # Somewhere in the app...
39 | #' MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
40 | #' MyModule$new("Mod1")
41 | #' MyModule$new("Mod2")
42 | #' MyModule$new("Mod3")
43 | #'
44 | #' # Must be in the server code and after calling the modules!
45 | #' callModules()
46 | #' observe({
47 | #' combine_ports(
48 | #' input_1 = mod(1)$getOutput(1),
49 | #' input_2 = mod(2)$getOutput(1)
50 | #' ) %>1% mod(3)
51 | #' })
52 | #' }
53 | #'
54 | #' @import shiny
55 | #'
56 | #' @export
57 | combine_ports <- function(...) {
58 | args <- list(...)
59 | r <- NULL
60 | if (length(args)) {
61 | if (is.null(names(args))) {
62 | names(args) <- seq_len(length(args))
63 | }
64 | r <- do.call(reactiveValues, args)
65 | } else {
66 | r <- reactiveValues()
67 | }
68 |
69 | # Make this reactive aware of its tidymoduleness
70 | attr(r, "tidymodules") <- TRUE
71 | attr(r, "tidymodules_operation") <- "combine"
72 |
73 | return(r)
74 | }
75 |
76 | #'
77 | #' @title Race ports function
78 | #'
79 | #' @description This function collapse ports into a single port and make them race (i.e. always return the last one updated)
80 | #'
81 | #' @param ... List of racing ports
82 | #'
83 | #' @examples
84 | #' \dontrun{
85 | #' # Somewhere in the app...
86 | #' MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
87 | #' MyModule$new("Mod1")
88 | #' MyModule$new("Mod2")
89 | #' MyModule$new("Mod3")
90 | #'
91 | #' # Must be in the server code and after calling the modules!
92 | #' callModules()
93 | #' observe({
94 | #' race_ports(
95 | #' mod(1)$getOutput(1),
96 | #' mod(2)$getOutput(1)
97 | #' ) %>1% mod(3)
98 | #' })
99 | #' }
100 | #'
101 | #' @import shiny
102 | #'
103 | #' @export
104 | race_ports <- function(...) {
105 | racers <- list(...)
106 |
107 | if (is.null(racers) || length(racers) == 0) {
108 | stop("In order to start a race, we need some ports!")
109 | }
110 |
111 | p <- length(racers) + 1
112 | r <- reactiveVal(
113 | label = "race",
114 | value = reactive({
115 | })
116 | )
117 |
118 | lapply(seq_len(length(racers)), function(r) {
119 | reac <- racers[[r]]
120 | observeEvent(
121 | {
122 | reac()
123 | },
124 | {
125 | req(reac())
126 | r(reac)
127 | },
128 | priority = p
129 | )
130 | p <- p - 1
131 | })
132 |
133 | reactive_racer <- reactive({
134 | r()
135 | isolate({
136 | o <- r()
137 | o()
138 | })
139 | })
140 |
141 | # Make this reactive aware of its tidymoduleness
142 | attr(reactive_racer, "tidymodules") <- TRUE
143 | attr(reactive_racer, "tidymodules_operation") <- "race"
144 |
145 | return(reactive_racer)
146 | }
147 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output: md_document
3 | always_allow_html: yes
4 | ---
5 |
6 |
7 |
8 | ```{r setup, include = FALSE}
9 | knitr::opts_chunk$set(
10 | collapse = TRUE,
11 | comment = "#>",
12 | fig.path = "man/figures/README-",
13 | out.width = "100%"
14 | )
15 | ```
16 |
17 | ```{r, include=FALSE}
18 | htmltools::tagList(rmarkdown::html_dependency_font_awesome())
19 | ```
20 |
21 | # tidymodules
22 |
23 | [](https://github.com/Novartis/tidymodules/actions)
24 | [](https://www.tidyverse.org/lifecycle/#experimental)
25 |
26 | The `{tidymodules}` R package is built on top of shiny module using `{R6}` to provide a new object-oriented programming (OOP) approach for module development, new module interface using input/output ports and a set of tidy operators for handling cross-module communication.
27 |
28 | The main features of tidymodules and its comparison with conventional Shiny modules are presented in the table below.
29 |
30 | | Features | | tidymodules | Conventional modules |
31 | |----|----------------------|---------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
32 | | [](articles/intro.html) | Programming style | - `{R6}` OOP *
- Semantic reference | Functional |
33 | | [](articles/namespace.html) | Namespace management | - automatic/generated
- ID based lookup
- Grouping | - manual management
- must match between ui and server |
34 | | [](articles/communication.html) | Module communication | - new module input/output port structure
- module ports linked via tidy operators
- automatic network diagram | - parameter passing via module server()
- challenging to manage for complex app |
35 | | [](articles/inheritance.html) | Inheritance | - class inheritance
- port inheritance for nested modules | NA |
36 | | [](articles/session.html) | Session management | - flexible user session management
- Caching of modules coming soon | NA |
37 |
38 | \* OOP = Object Oriented Programming
39 |
40 | ## Installation
41 |
42 | You can install the most recent version of `{tidymodules}` from [GitHub](https://github.com/Novartis/tidymodules) with:
43 |
44 | ``` r
45 | library(devtools)
46 | install_github("Novartis/tidymodules")
47 | ```
48 |
49 | ## Examples
50 |
51 | You can quickly launch an example after installing the R package by running the following.
52 | ``` r
53 | tidymodules::showExamples(4)
54 | ```
55 | Some examples have been deployed on shinyapp.io, such as:
56 |
57 | - Example 1: [Simple addition ](https://tidymodules.shinyapps.io/1_simple_addition/)
58 | - Example 2: [Linked scatter ](https://tidymodules.shinyapps.io/2_linked_scatter/)
59 | - Example 2: [Nested module ](https://tidymodules.shinyapps.io/3_nested_modules/)
60 | - Example 4: [Module communication ](https://tidymodules.shinyapps.io/4_communication/)
61 |
62 | ## Learning More
63 |
64 | Please review the [Get Started](https://opensource.nibr.com/tidymodules/articles/tidymodules.html) page for a high level introduction to `{tidymodules}` and its usage in developing Shiny apps.
65 |
66 | If you are interested to develop modules using `{tidymodules}`, we recommend reading the vignettes under "Articles".
67 |
68 | ## Code of Conduct
69 |
70 | Please note that the `{tidymodules}` is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms.
71 |
72 | ## Acknowledgment
73 |
74 | - The SCC team members @ NVS for their valuable feedbacks
75 | - 2019 Summer interns (Marzi, Stephen and Renan) for contributing to testing the framework and implementing the demo example 4 listed above.
76 | - Eric Nantz for accepting to introduce tidymodules in his [e-poster](https://rpodcast.shinyapps.io/highlights-shiny) @ rstudio::conf 2020
77 |
78 | ## Licence
79 |
80 | Copyright 2020 Novartis AG
81 |
82 | Licensed under the Apache License, Version 2.0 (the "License");
83 | you may not use this file except in compliance with the License.
84 | You may obtain a copy of the License at
85 |
86 | http://www.apache.org/licenses/LICENSE-2.0
87 |
88 | Unless required by applicable law or agreed to in writing, software
89 | distributed under the License is distributed on an "AS IS" BASIS,
90 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
91 | See the License for the specific language governing permissions and
92 | limitations under the License.
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # tidymodules
4 |
5 | [](https://github.com/Novartis/tidymodules/actions)
7 | [](https://www.tidyverse.org/lifecycle/#experimental)
8 |
9 | The `{tidymodules}` R package is built on top of shiny module using
10 | `{R6}` to provide a new object-oriented programming (OOP) approach for
11 | module development, new module interface using input/output ports and a
12 | set of tidy operators for handling cross-module communication.
13 |
14 | The main features of tidymodules and its comparison with conventional
15 | Shiny modules are presented in the table below.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 | |
36 | Programming style |
37 | - {R6} OOP * - Semantic reference |
38 | Functional |
39 |
40 |
41 | |
43 | Namespace management |
44 | - automatic/generated - ID based lookup - Grouping |
45 | - manual management - must match between ui and server |
46 |
47 |
48 | |
50 | Module communication |
51 | - new module input/output port structure - module ports linked
52 | via tidy operators - automatic network diagram |
53 | - parameter passing via module server() - challenging to
54 | manage for complex app |
55 |
56 |
57 | |
59 | Inheritance |
60 | - class inheritance - port inheritance for nested modules |
61 | NA |
62 |
63 |
64 | |
66 | Session management |
67 | - flexible user session management - Caching of modules coming
68 | soon |
69 | NA |
70 |
71 |
72 |
73 |
74 | \* OOP = Object Oriented Programming
75 |
76 | ## Installation
77 |
78 | You can install the most recent version of `{tidymodules}` from
79 | [GitHub](https://github.com/Novartis/tidymodules) with:
80 |
81 | library(devtools)
82 | install_github("Novartis/tidymodules")
83 |
84 | ## Examples
85 |
86 | You can quickly launch an example after installing the R package by
87 | running the following.
88 |
89 | tidymodules::showExamples(4)
90 |
91 | Some examples have been deployed on shinyapp.io, such as:
92 |
93 | - Example 1: [Simple addition
94 | ](https://tidymodules.shinyapps.io/1_simple_addition/)
95 | - Example 2: [Linked scatter
96 | ](https://tidymodules.shinyapps.io/2_linked_scatter/)
97 | - Example 2: [Nested module
98 | ](https://tidymodules.shinyapps.io/3_nested_modules/)
99 | - Example 4: [Module communication
100 | ](https://tidymodules.shinyapps.io/4_communication/)
101 |
102 | ## Learning More
103 |
104 | Please review the [Get
105 | Started](https://opensource.nibr.com/tidymodules/articles/tidymodules.html)
106 | page for a high level introduction to `{tidymodules}` and its usage in
107 | developing Shiny apps.
108 |
109 | If you are interested to develop modules using `{tidymodules}`, we
110 | recommend reading the vignettes under “Articles”.
111 |
112 | ## Code of Conduct
113 |
114 | Please note that the `{tidymodules}` is released with a [Contributor
115 | Code of Conduct](CODE_OF_CONDUCT.md). By contributing to this project,
116 | you agree to abide by its terms.
117 |
118 | ## Acknowledgment
119 |
120 | - The SCC team members @ NVS for their valuable feedbacks
121 | - 2019 Summer interns (Marzi, Stephen and Renan) for contributing to
122 | testing the framework and implementing the demo example 4 listed
123 | above.
124 | - Eric Nantz for accepting to introduce tidymodules in his
125 | [e-poster](https://rpodcast.shinyapps.io/highlights-shiny) @
126 | rstudio::conf 2020
127 |
128 | ## Licence
129 |
130 | Copyright 2020 Novartis AG
131 |
132 | Licensed under the Apache License, Version 2.0 (the "License");
133 | you may not use this file except in compliance with the License.
134 | You may obtain a copy of the License at
135 |
136 | http://www.apache.org/licenses/LICENSE-2.0
137 |
138 | Unless required by applicable law or agreed to in writing, software
139 | distributed under the License is distributed on an "AS IS" BASIS,
140 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141 | See the License for the specific language governing permissions and
142 | limitations under the License.
143 |
--------------------------------------------------------------------------------
/inst/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/inst/.DS_Store
--------------------------------------------------------------------------------
/inst/rstudio/r.snippets:
--------------------------------------------------------------------------------
1 | snippet tm.mod.new
2 | #'
3 | #' ${1:MyModule} Module.
4 | #'
5 | #' @description
6 | #' This \href{https://opensource.nibr.com/tidymodules}{`{tm}`} module is a R6 class representing a ${1:MyModule}.
7 | #'
8 | #' @family tm
9 | #'
10 | #' @details
11 | #' More details about your module here.
12 | #'
13 | #' @export
14 | ${1:MyModule} <- R6::R6Class(
15 | classname = "${1:MyModule}",
16 | inherit = ${2:TidyModule},
17 | public = list(
18 | #' @description
19 | #' Module's initialization function.
20 | #' @param ... options
21 | #' @return An instance of ${1:MyModule}
22 | initialize = function(...){
23 | # Don't remove the line below
24 | super\$initialize(...)
25 |
26 | # Ports definition starts here...
27 | ${0}
28 | },
29 | #' @description
30 | #' Module's ui function.
31 | #' @return HTML tags list.
32 | ui = function(){
33 | # Module's representation starts here ...
34 | tagList()
35 | },
36 | #' @description
37 | #' Module's server function.
38 | #' @param input Shiny input
39 | #' @param output Shiny output
40 | #' @param session Shiny session
41 | server = function(input, output, session){
42 | # Don't remove the line below
43 | super\$server(input,output,session)
44 |
45 | # Module server logic starts here ...
46 |
47 | }
48 | )
49 | )
50 | snippet tm.port.define
51 | self\$definePort({
52 | ${0}
53 | })
54 | snippet tm.port.in
55 | self\$addInputPort(
56 | name = "${1:port_name}",
57 | description = "A clear description for this input port${0}",
58 | sample = data.frame(x = rnorm(10), y = rnorm(10))
59 | )
60 | snippet tm.port.out
61 | self\$addOutputPort(
62 | name = "${1:port_name}",
63 | description = "A clear description for this output port${0}",
64 | sample = data.frame(x = rnorm(10), y = rnorm(10))
65 | )
66 | snippet tm.port.assign
67 | self\$assignPort({
68 | ${0}
69 | })
70 | snippet tm.port.edges
71 | defineEdges({
72 | ${0}
73 | })
74 |
--------------------------------------------------------------------------------
/inst/shiny/examples/1_simple_addition/Addition.R:
--------------------------------------------------------------------------------
1 | #'
2 | #' Addition module implemented with {tidymodules}
3 | #'
4 | #' @description
5 | #' Take a number as input and add it to the user selection.
6 | #'
7 | #' @details
8 | #' Should be initialized and injected in your application.
9 | #' Input port:
10 | #' - left
11 | #' Output port:
12 | #' - total
13 | #'
14 | #' @example
15 | #'
16 | #' a <- Addition$new()
17 | #'
18 | #'
19 | Addition <- R6::R6Class(
20 | "Addition",
21 | inherit = tidymodules::TidyModule,
22 | public = list(
23 | initialize = function(...) {
24 | # mandatory
25 | super$initialize(...)
26 |
27 | self$definePort({
28 | self$addInputPort(
29 | name = "left",
30 | description = "input value to add to the user selected number",
31 | sample = 5
32 | )
33 |
34 | self$addOutputPort(
35 | name = "total",
36 | description = "Sum of the two numbers",
37 | sample = 6
38 | )
39 | })
40 | },
41 | #' @description
42 | #' Store's ui function.
43 | #' @return UI elements.
44 | ui = function() {
45 | div(
46 | style = paste0(
47 | "width:30%;",
48 | "background:lightgrey;",
49 | "border: solid;",
50 | "border-color: grey;",
51 | "padding: 20px;"),
52 | "Module input : ",
53 | textOutput(self$ns("left")),
54 | " + ",
55 | sliderInput(
56 | self$ns("right"),
57 | label = "Number to add",
58 | min = 1,
59 | max = 100,
60 | value = 1),
61 | " = ",
62 | textOutput(self$ns("total"))
63 | )
64 | },
65 | #' @description
66 | #' Store's server function.
67 | #' @param input Shiny input.
68 | #' @param output Shiny output
69 | #' @param session Shiny session
70 | server = function(input, output, session) {
71 | # Mandatory
72 | super$server(input, output, session)
73 |
74 | self$react$sum_numbers <- reactive({
75 | req(input$right)
76 | left <- self$execInput(1)
77 | as.numeric(left) + as.numeric(input$right)
78 | })
79 |
80 | output$left <- renderText({
81 | self$execInput(1)
82 | })
83 |
84 | output$total <- renderText({
85 | self$react$sum_numbers()
86 | })
87 |
88 | self$assignPort({
89 | self$updateOutputPort(
90 | id = "total",
91 | output = self$react$sum_numbers
92 | )
93 | })
94 | }
95 | )
96 | )
97 |
--------------------------------------------------------------------------------
/inst/shiny/examples/1_simple_addition/AdditionSM.R:
--------------------------------------------------------------------------------
1 | #' UI function of the Shiny Addition module
2 | #'
3 | #' @description
4 | #' Generated the UI elements of the Addition module.
5 | #'
6 | AdditionSM_UI <- function(ns_id) {
7 | ns <- NS(ns_id)
8 |
9 | div(
10 | style = paste0(
11 | "width:30%;",
12 | "background:lightgrey;",
13 | "border: solid;",
14 | "border-color: grey;",
15 | "padding: 20px;"),
16 | "Module input : ",
17 | textOutput(ns("left")),
18 | " + ",
19 | sliderInput(
20 | ns("right"),
21 | label = "Number to add",
22 | min = 1,
23 | max = 100,
24 | value = 1),
25 | " = ",
26 | textOutput(ns("total"))
27 | )
28 | }
29 |
30 | #' Server function of the Shiny Addition module
31 | #'
32 | #' @description
33 | #' Server logic of the Shiny Addition module.
34 | #' Add an input number to a number selected by the user with a slider.
35 | #'
36 | AdditionSM_Server <- function(input, output, session, number) {
37 | sum_numbers <- reactive({
38 | req(input$right)
39 | req(number)
40 | as.numeric(number()) + as.numeric(input$right)
41 | })
42 |
43 | output$left <- renderText({
44 | req(number)
45 | number()
46 | })
47 |
48 | output$total <- renderText({
49 | sum_numbers()
50 | })
51 |
52 | return(sum_numbers)
53 | }
54 |
--------------------------------------------------------------------------------
/inst/shiny/examples/1_simple_addition/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Title: tidymodules - Simple Addition
2 | Author: Mustapha Larbaoui
3 | License: Apache
4 | DisplayMode: Showcase
5 | Tags: module, ggplot
6 | Type: Shiny
7 |
--------------------------------------------------------------------------------
/inst/shiny/examples/1_simple_addition/app.R:
--------------------------------------------------------------------------------
1 | library(shiny)
2 |
3 | # SM -----------> Shiny module
4 |
5 | source(
6 | system.file(
7 | package = "tidymodules",
8 | "shiny/examples/1_simple_addition/AdditionSM.R")
9 | )
10 |
11 | add_html_1 <- AdditionSM_UI("Addition_1")
12 | add_html_2 <- AdditionSM_UI("Addition_2")
13 |
14 | SM_UI <- shiny::basicPage(
15 | h2("Shiny module : Simple Addition"),
16 | sliderInput(
17 | "first_number",
18 | label = "Enter your first number",
19 | min = 1,
20 | max = 100,
21 | value = 1),
22 | br(),
23 | add_html_1, br(),
24 | add_html_2, br(),
25 | "Total: ",
26 | textOutput("total_result")
27 | )
28 |
29 | SM_Server <- function(input, output, session) {
30 | first <- reactive({
31 | req(input$first_number)
32 | })
33 |
34 | second <- callModule(module = AdditionTM_Server, id = "Addition_1", first)
35 | result <- callModule(module = AdditionTM_Server, id = "Addition_2", second)
36 |
37 | output$total_result <- renderText({
38 | result()
39 | })
40 | }
41 |
42 | # TM -----------> tidymodules
43 |
44 | library(tidymodules)
45 |
46 | source(
47 | system.file(
48 | package = "tidymodules",
49 | "shiny/examples/1_simple_addition/Addition.R")
50 | )
51 |
52 |
53 | Addition$new()
54 | Addition$new()
55 |
56 | TM_UI <- shiny::basicPage(
57 | h2("tidymodules : Simple Addition"),
58 | sliderInput(
59 | "first_number",
60 | label = "Enter your first number",
61 | min = 1,
62 | max = 100,
63 | value = 1), br(),
64 | mod(1)$ui(), br(),
65 | mod(2)$ui(), br(),
66 | "Total: ", textOutput("total_result")
67 | )
68 |
69 | TM_Server <- function(input, output, session) {
70 | callModules()
71 |
72 | first <- reactive({
73 | req(input$first_number)
74 | })
75 |
76 | observe({
77 | first %>1% mod(1) %1>1% mod(2)
78 | })
79 |
80 | output$total_result <- renderText({
81 | mod(2)$execOutput(1)
82 | })
83 | }
84 |
85 | ### Start the app
86 |
87 | # shinyApp(SM_UI, SM_Server)
88 | shinyApp(TM_UI, TM_Server)
89 |
--------------------------------------------------------------------------------
/inst/shiny/examples/2_linked_scatter/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Title: tidymodules - Linked scatter
2 | Author: Mustapha Larbaoui
3 | License: Apache
4 | DisplayMode: Showcase
5 | Tags: module, ggplot
6 | Type: Shiny
7 |
--------------------------------------------------------------------------------
/inst/shiny/examples/2_linked_scatter/app.R:
--------------------------------------------------------------------------------
1 | library(tidymodules)
2 |
3 | check_and_load(c("ggplot2", "dplyr", "shinycssloaders"))
4 |
5 |
6 | source(system.file(package = "tidymodules", "shiny/examples/2_linked_scatter/linked_scatter.R"))
7 |
8 | lsObj1 <- LinkedScatter$new()
9 | lsObj2 <- LinkedScatter$new()
10 | lsObj3 <- LinkedScatter$new()
11 | lsObj4 <- LinkedScatter$new()
12 | store <- Store$new()
13 |
14 | ui <- fixedPage(
15 | h2("tidymodules : linked scatter"),
16 | tabsetPanel(
17 | type = "tabs",
18 | tabPanel(
19 | "App",
20 | br(),
21 | lsObj1$ui(),
22 | textOutput("summary1"),
23 | lsObj2$ui(),
24 | textOutput("summary2"),
25 | lsObj3$ui(),
26 | lsObj4$ui()
27 | ),
28 | tabPanel(
29 | "Store",
30 | br(),
31 | fluidRow(
32 | column(12, store$ui())
33 | )
34 | )
35 | )
36 | )
37 |
38 |
39 | server <- function(input, output, session) {
40 | d <- reactive({
41 | mpg
42 | })
43 |
44 | o <- reactive({
45 | list(
46 | left = c("cty", "hwy"),
47 | right = c("drv", "hwy")
48 | )
49 | })
50 |
51 | # callModules()
52 | lsObj1$callModule()
53 | lsObj2$callModule()
54 | lsObj3$callModule()
55 | lsObj4$callModule()
56 | store$callModule()
57 |
58 | observe({
59 | o %>>2% lsObj1 %>>2% lsObj2 %>>2% lsObj3 %>>2% lsObj4
60 | d %>1% lsObj1 %2>1% lsObj2 %2>1% lsObj3 %2>1% lsObj4
61 | })
62 |
63 | output$summary1 <- renderText({
64 | df <- getMod(1)$getOutput(1)
65 | sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
66 | })
67 |
68 | output$summary2 <- renderText({
69 | df <- getMod(2)$getOutput(1)
70 | sprintf("%d observation(s) selected", nrow(dplyr::filter(df(), selected_)))
71 | })
72 | }
73 |
74 | shinyApp(ui, server)
75 |
--------------------------------------------------------------------------------
/inst/shiny/examples/2_linked_scatter/linked_scatter.R:
--------------------------------------------------------------------------------
1 |
2 | input_sample <- head(mpg)
3 |
4 | LinkedScatter <- R6::R6Class(
5 | "LinkedScatter",
6 | inherit = tidymodules::TidyModule,
7 | public = list(
8 | initialize = function(...) {
9 | super$initialize(...)
10 |
11 | self$definePort({
12 | self$addInputPort(
13 | name = "data",
14 | description = "Any rectangular data frame",
15 | sample = input_sample
16 | )
17 |
18 | self$addInputPort(
19 | name = "option",
20 | description = "The module options are two vectors of (x,y) column names defining the left and right plot",
21 | sample = list(
22 | left = c("cty", "hwy"),
23 | right = c("drv", "hwy")
24 | )
25 | )
26 |
27 | self$addOutputPort(
28 | name = "selection",
29 | description = "The input data frame with a new column for selected rows",
30 | sample = input_sample
31 | )
32 |
33 |
34 | self$addOutputPort(
35 | name = "selection_only",
36 | description = "Only the selected rows from ggplot brushing",
37 | sample = input_sample
38 | )
39 | })
40 | },
41 | ui = function() {
42 | fluidRow(
43 | column(
44 | 6,
45 | shinycssloaders::withSpinner(
46 | plotOutput(self$ns("plot1"), brush = self$ns("brush")),
47 | type = 3, color.background = "white"
48 | )
49 | ),
50 | column(
51 | 6,
52 | shinycssloaders::withSpinner(
53 | plotOutput(self$ns("plot2"), brush = self$ns("brush")),
54 | type = 3, color.background = "white"
55 | )
56 | )
57 | )
58 | },
59 | server = function(input, output, session, ...) {
60 | super$server(input, output, session, ...)
61 |
62 | args <- list(...)
63 |
64 | self$assignPort({
65 | self$updateInputPort(
66 | id = "data",
67 | input = args$data
68 | )
69 |
70 | self$updateInputPort(
71 | id = "option",
72 | input = args$options
73 | )
74 | })
75 |
76 | self$react$dataWithSelection <- reactive({
77 | data <- self$execInput("data")
78 | req(nrow(data) != 0)
79 | brushedPoints(data, input$brush, allRows = TRUE)
80 | })
81 |
82 | self$react$dataWithSelectionOnly <- reactive({
83 | data <- self$execInput("data")
84 | req(nrow(data) != 0)
85 | brushedPoints(data, input$brush, allRows = FALSE)
86 | })
87 |
88 | output$plot1 <- renderPlot({
89 | o <- self$execInput("option")
90 | req(o)
91 | private$scatterPlot(self$react$dataWithSelection(), o$left)
92 | })
93 |
94 | output$plot2 <- renderPlot({
95 | o <- self$execInput("option")
96 | req(o)
97 | private$scatterPlot(self$react$dataWithSelection(), o$right)
98 | })
99 |
100 | self$assignPort({
101 | self$updateOutputPort(
102 | id = "selection",
103 | output = self$react$dataWithSelection
104 | )
105 |
106 | self$updateOutputPort(
107 | id = "selection_only",
108 | output = self$react$dataWithSelectionOnly
109 | )
110 | })
111 |
112 | return(self$react$dataWithSelection)
113 | }
114 | ),
115 | private = list(
116 | scatterPlot = function(data, cols) {
117 | ggplot(data, aes_string(x = cols[1], y = cols[2])) +
118 | geom_point(aes(color = selected_)) +
119 | scale_color_manual(values = c("black", "#66D65C"), guide = "none")
120 | }
121 | )
122 | )
123 |
--------------------------------------------------------------------------------
/inst/shiny/examples/3_nested_modules/ColorPicker.R:
--------------------------------------------------------------------------------
1 |
2 | ColorPicker <- R6::R6Class(
3 | "ColorPicker",
4 | inherit = Panel,
5 | public = list(
6 | initialize = function(...) {
7 | super$initialize(...)
8 |
9 | self$definePort({
10 | self$addOutputPort(
11 | name = "scheme",
12 | description = "string defining color scheme",
13 | sample = list(scheme = "Dark2", reverse = FALSE, transparency = 1)
14 | )
15 | })
16 | },
17 | ui = function(label = "Coloring") {
18 | super$ui(
19 | status = "primary",
20 | tagList(
21 | selectInput(self$ns("scheme"), label = label, choices = c("Dark2" = "Dark2", "Set1" = "Set1", "Set2" = "Set2"), selected = "Dark2"),
22 | checkboxInput(self$ns("reverse"), label = "Reverse scheme"),
23 | sliderInput(self$ns("transparency"), label = "Transparency", min = 0, max = 1, value = 1)
24 | )
25 | )
26 | },
27 | server = function(input, output, session) {
28 | super$server(input, output, session)
29 |
30 | self$obser$log <- observe({
31 | msg <- paste("Color scheme was selected", input$scheme)
32 | cat(msg, "\n")
33 | })
34 |
35 | self$react$input_values <- reactive({
36 | reactiveValuesToList(input)
37 | })
38 |
39 | self$assignPort({
40 | self$updateOutputPort(
41 | id = "scheme",
42 | output = self$react$input_values
43 | )
44 | })
45 |
46 | return(input)
47 | }
48 | )
49 | )
50 |
--------------------------------------------------------------------------------
/inst/shiny/examples/3_nested_modules/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Title: tidymodules - Nesting modules
2 | Author: Mustapha Larbaoui
3 | License: Apache
4 | DisplayMode: Showcase
5 | Tags: module, ggplot
6 | Type: Shiny
7 |
--------------------------------------------------------------------------------
/inst/shiny/examples/3_nested_modules/Kmeans.R:
--------------------------------------------------------------------------------
1 |
2 | Kmeans <- R6::R6Class(
3 | "Kmeans",
4 | inherit = tidymodules::TidyModule,
5 | public = list(
6 | # Example with nested module in list
7 | # nested_mods = list(CP = NULL),
8 | nested_mod = NULL,
9 | initialize = function(...) {
10 | super$initialize(...)
11 | # self$nested_mods$CP <- ColorPicker$new("CP")
12 | self$nested_mod <- ColorPicker$new("CP")
13 |
14 | km_sample <- kmeans(matrix(rnorm(100), ncol = 2), 3)
15 | self$definePort({
16 | self$addOutputPort(
17 | name = "km",
18 | description = "object of class 'kmeans'",
19 | sample = km_sample
20 | )
21 | })
22 | },
23 | ui = function(label) {
24 | # col <- self$nested_mods$CP$ui("Color scheme")
25 | col <- self$nested_mod$ui("Color scheme")
26 | tags <- tagList(
27 | selectInput(self$ns("xcol"), "X Variable", names(iris)),
28 | selectInput(self$ns("ycol"), "Y Variable", names(iris), selected = names(iris)[[2]]),
29 | numericInput(self$ns("clusters"), "Cluster count", 3, min = 1, max = 9)
30 | )
31 |
32 | fluidRow(
33 | column(4, tags, col),
34 | column(8, plotOutput(self$ns("plot1")))
35 | )
36 | },
37 | server = function(input, output, session) {
38 | super$server(input, output, session)
39 |
40 | # self$nested_mods$CP$callModule()
41 | self$nested_mod$callModule()
42 |
43 | # Combine the selected variables into a new data frame
44 | self$react$selectedData <- reactive({
45 | iris[, c(input$xcol, input$ycol)]
46 | })
47 |
48 | self$react$clusters <- reactive({
49 | kmeans(self$react$selectedData(), input$clusters)
50 | })
51 |
52 | output$plot1 <- renderPlot({
53 | # Get ColorPicker output, default to first output port
54 | # cp <- self$nested_mods$CP$getOutput()
55 | cp <- self$nested_mod$execOutput()
56 | cols <- brewer.pal(input$clusters, cp$scheme)
57 | cols <- adjustcolor(cols, alpha.f = cp$transparency)
58 | if (cp$reverse) {
59 | cols <- rev(cols)
60 | }
61 | cols <- cols[self$react$clusters()$cluster]
62 | par(mar = c(5.1, 4.1, 0, 1))
63 | plot(self$react$selectedData(),
64 | col = cols,
65 | pch = 20, cex = 3
66 | )
67 | points(self$react$clusters()$centers, pch = 4, cex = 4, lwd = 4)
68 | })
69 |
70 | self$assignPort({
71 | self$updateOutputPort(
72 | id = "km",
73 | output = self$react$clusters
74 | )
75 | })
76 |
77 | return(self$react$clusters)
78 | }
79 | )
80 | )
81 |
--------------------------------------------------------------------------------
/inst/shiny/examples/3_nested_modules/app.R:
--------------------------------------------------------------------------------
1 | library(tidymodules)
2 |
3 | check_and_load("RColorBrewer")
4 |
5 | source(system.file(package = "tidymodules", "shiny/examples/4_communication/module/Panel.R"))
6 | source(system.file(package = "tidymodules", "shiny/examples/3_nested_modules/Kmeans.R"))
7 | source(system.file(package = "tidymodules", "shiny/examples/3_nested_modules/ColorPicker.R"))
8 |
9 | km_module <- Kmeans$new()
10 | store <- Store$new()
11 |
12 | ui <- fixedPage(
13 | h2("tidymodules : Nested module example"),
14 | tabsetPanel(
15 | type = "tabs",
16 | tabPanel(
17 | "App",
18 | br(),
19 | km_module$ui()
20 | ),
21 | tabPanel(
22 | "Store",
23 | br(),
24 | fluidRow(
25 | column(12, store$ui())
26 | )
27 | )
28 | )
29 | )
30 |
31 | server <- function(input, output, session) {
32 | store$callModule()
33 | km_module$callModule()
34 | }
35 |
36 | shinyApp(ui, server)
37 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Title: tidymodules - Orchestrating module communication
2 | Author: Mustapha Larbaoui
3 | License: Apache
4 | DisplayMode: Showcase
5 | Tags: module, ggplot
6 | Type: Shiny
7 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/app.R:
--------------------------------------------------------------------------------
1 | library(tidymodules)
2 |
3 | check_and_load(c("shinyWidgets", "ggplot2", "plotly", "DT"))
4 |
5 | # Load modules from ./module folder
6 | app_dir <- system.file(package = "tidymodules", "shiny/examples/4_communication")
7 |
8 | sapply(
9 | list.files(file.path(app_dir, "module"), include.dirs = F, pattern = ".R", ignore.case = T),
10 | function(f) {
11 | cat(paste0("Sourcing file :", f, "\n"))
12 | source(file.path(app_dir, "module", f))
13 | }
14 | )
15 |
16 | Store$new()
17 | Panel$new()
18 | DatasetSelector$new("Marzie")
19 | DataFilter$new("Stefan")
20 | ColSelector$new("Renan")
21 | PlotGenerator$new("Doug")
22 |
23 | ui <- shiny::fluidPage(
24 | h2("tidymodules : Communication is the key to success!"),
25 | tags$br(),
26 | tabsetPanel(
27 | type = "tabs",
28 | tabPanel(
29 | "App",
30 | br(),
31 | fluidRow(
32 | column(12, mod("Marzie")$ui())
33 | ),
34 | fluidRow(
35 | column(4, mod("Renan")$ui()),
36 | column(8, mod("Stefan")$ui())
37 | ),
38 | fluidRow(
39 | column(12, mod("Doug")$ui())
40 | )
41 | ),
42 | tabPanel(
43 | "Help",
44 | tabsetPanel(
45 | type = "tabs",
46 | tabPanel(
47 | "ModStore",
48 | br(),
49 | fluidRow(
50 | column(
51 | 12,
52 | mod(2)$ui(
53 | status = "warning",
54 | mod(1)$ui()
55 | )
56 | )
57 | )
58 | ),
59 | tabPanel("ERD", img(src = "ERD.svg", align = "center")),
60 | tabPanel("Ports", img(src = "ports.svg", align = "center"))
61 | )
62 | )
63 | )
64 | )
65 |
66 | server <- function(input, output, session) {
67 | # Add modules server logic
68 | callModules()
69 | # Configure modules communication by connecting ports
70 | defineEdges({
71 | # dataset selector provides data to
72 | # column mapper and row filter modules
73 | oport("Marzie", "dataset") %->>%
74 | iport("Renan", "data") %->%
75 | iport("Stefan", "data")
76 |
77 | # the mappings are then used by the plot generator
78 | mod("Renan") %1>1% mod("Doug")
79 | # plot generator also takes raw and filtered data as input
80 | # by combining the two output ports in a named reactive list
81 | combine_ports(
82 | raw = oport("Marzie", "dataset"), # output 1 of data selector module
83 | filter = oport("Stefan", "filtered") # output 2 of data filter module
84 | ) %->% iport("Doug", "tables")
85 | })
86 | }
87 |
88 |
89 | shinyApp(ui, server)
90 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_BasePlot.R:
--------------------------------------------------------------------------------
1 | module/BasePlot.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_BoxPlot.R:
--------------------------------------------------------------------------------
1 | module/BoxPlot.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_ClosablePanel.R:
--------------------------------------------------------------------------------
1 | module/ClosablePanel.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_ColSelector.R:
--------------------------------------------------------------------------------
1 | module/ColSelector.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_DataFilter.R:
--------------------------------------------------------------------------------
1 | module/DataFilter.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_DatasetSelector.R:
--------------------------------------------------------------------------------
1 | module/DatasetSelector.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_LinePlot.R:
--------------------------------------------------------------------------------
1 | module/LinePlot.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_Panel.R:
--------------------------------------------------------------------------------
1 | module/Panel.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_PlotGenerator.R:
--------------------------------------------------------------------------------
1 | module/PlotGenerator.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_Scatter3DPlot.R:
--------------------------------------------------------------------------------
1 | module/Scatter3DPlot.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/mod_ScatterPlot.R:
--------------------------------------------------------------------------------
1 | module/ScatterPlot.R
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/BasePlot.R:
--------------------------------------------------------------------------------
1 |
2 | BasePlot <- R6::R6Class(
3 | "BasePlot",
4 | inherit = ClosablePanel,
5 | public = list(
6 | initialize = function(...) {
7 | # Mandatory
8 | super$initialize(...)
9 |
10 | # Ports definition starts here
11 | self$definePort({
12 | # At least one input
13 | self$addInputPort(
14 | name = "data",
15 | description = "Any rectangular data frame",
16 | sample = head(mtcars)
17 | )
18 |
19 | self$addInputPort(
20 | name = "mapping",
21 | description = "vector of column names for the mapping",
22 | sample = colnames(mtcars)[1:3]
23 | )
24 |
25 |
26 | # Add an output port (optional)
27 | self$addOutputPort(
28 | name = "selection",
29 | description = "data points selected from brushing",
30 | sample = mtcars[5:6, ]
31 | )
32 | })
33 | },
34 | ui = function(outputFunc = NULL, header = NULL) {
35 | super$ui(
36 | fluidRow(
37 | ifelse(is.null(outputFunc),
38 | tagList(
39 | plotOutput(self$ns("plot"),
40 | brush = self$ns("brush")
41 | )
42 | ),
43 | tagList(
44 | outputFunc(self$ns("plot")),
45 | self$ns("brush")
46 | )
47 | )
48 | ),
49 | header = header
50 | )
51 | },
52 | server = function(input, output, session) {
53 | # Mandatory
54 | super$server(input, output, session)
55 |
56 | self$react$selection <- reactive({
57 | d <- self$getInput("data")
58 | data <- d()
59 | brushedPoints(data, input$brush, allRows = TRUE)
60 | })
61 |
62 | self$react$selectionOnly <- reactive({
63 | d <- self$getInput("data")
64 | data <- d()
65 | brushedPoints(data, input$brush, allRows = FALSE)
66 | })
67 |
68 | output$plot <- self$renderPlot(
69 | self$react$selection,
70 | self$getInput("mapping")
71 | )
72 |
73 | # Ports assignment starts here
74 | self$assignPort({
75 | self$updateOutputPort(
76 | id = "selection",
77 | output = self$react$selectionOnly
78 | )
79 | })
80 |
81 | return(self$react$selectionOnly)
82 | },
83 | renderPlot = function(data, cols) {
84 | renderPlot({
85 | self$chart(data, cols)()
86 | })
87 | },
88 | aes = function(cols) {
89 | shiny::req(length(cols) > 1)
90 | x <- cols[1]
91 | y <- cols[2]
92 | g <- cols[3]
93 |
94 | aes <- aes_string(x = x, y = y, group = g, color = g)
95 | if (is.na(g)) {
96 | aes <- aes_string(x = x, y = y)
97 | }
98 |
99 | return(aes)
100 | },
101 | chart = function(...) {
102 | warning("charting function need to be implemented in a child class, SHOULD RETURN A REACTIVE FUNCTION!")
103 | }
104 | )
105 | )
106 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/BoxPlot.R:
--------------------------------------------------------------------------------
1 |
2 | BoxPlot <- R6::R6Class(
3 | "BoxPlot",
4 | inherit = BasePlot,
5 | public = list(
6 | chart = function(data, cols) {
7 | return(reactive({
8 | aes <- self$aes(cols())
9 | ggplot(data(), aes) +
10 | geom_boxplot()
11 | }))
12 | }
13 | )
14 | )
15 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/ClosablePanel.R:
--------------------------------------------------------------------------------
1 | ClosablePanel <- R6::R6Class(
2 | "ClosablePanel",
3 | inherit = Panel,
4 | public = list(
5 | # TODO : Change the way we close closable panel
6 | # Add server logic to remove the module
7 | ui = function(..., status = "default", header = NULL) {
8 | header <- tagList(
9 | header,
10 | tags$span(
11 | style = "float: right;cursor: pointer;",
12 | id = self$ns("close_span"),
13 | actionButton(self$ns("close"),icon = shiny::icon("times", "fa-sm", verify_fa = FALSE), label = "")
14 | )
15 | )
16 | content <- tagList(
17 | ...,
18 | tags$head(
19 | tags$script(
20 | type = "text/javascript",
21 | paste0(
22 | "setTimeout(function(){ $('#", self$ns("close_span"), "').click(function(){
23 | $('#", self$module_ns, "').parent().parent().remove()
24 | }) }, 500);"
25 | )
26 | )
27 | )
28 | )
29 |
30 | super$ui(
31 | status = status,
32 | header = header,
33 | content
34 | )
35 | },
36 | # server logic
37 | server = function(input,output,session) {
38 | # Mandatory
39 | super$server(input, output, session)
40 |
41 | self$obser$close <- observe({
42 | req(input$close)
43 | self$destroy()
44 | })
45 | }
46 | )
47 | )
48 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/ColSelector.R:
--------------------------------------------------------------------------------
1 |
2 | ColSelector <- R6::R6Class(
3 | "ColSelector",
4 | inherit = Panel,
5 | public = list(
6 | initialize = function(...) {
7 | super$initialize(...)
8 | self$definePort({
9 | self$addInputPort(
10 | name = "data",
11 | description = "Any data table",
12 | sample = data.frame(a = 1:4, b = 1:4)
13 | )
14 | self$addOutputPort(
15 | name = "mapping",
16 | description = "dynamic list of column selections",
17 | sample = list(
18 | mapping1 = c("col1", "col2", "col3"),
19 | mapping2 = c("col2", "col4")
20 | )
21 | )
22 | })
23 | },
24 | ui = function() {
25 | super$ui(
26 | status = "primary",
27 | shiny::actionButton(self$ns("add"), label = "Add mapping", icon = icon("plus")),
28 | br(),
29 | tags$div(id = self$ns("uio_selector"))
30 | )
31 | },
32 | server = function(input, output, session) {
33 | # Mandatory
34 | super$server(input, output, session)
35 |
36 | self$react$cols <- reactiveValues(
37 | current = NULL,
38 | names = NULL,
39 | mapping = list()
40 | )
41 |
42 | self$obser$reset <- observe({
43 | dataPort <- self$getInput("data")
44 | req(dataPort)
45 | d <- dataPort()
46 | self$react$cols$mapping <- list()
47 | req(!is.null(d))
48 | self$react$cols$names <- colnames(d)
49 | self$react$cols$current <- 1
50 | shiny::removeUI(
51 | selector = paste0("#", self$ns("uio_selector div")),
52 | multiple = TRUE
53 | )
54 | })
55 |
56 | self$obser$add <- observeEvent(input$add, {
57 | d <- self$getInput("data")()
58 | req(!is.null(d))
59 | insertUI(
60 | selector = paste0("#", self$ns("uio_selector")),
61 | where = "beforeEnd",
62 | session = session,
63 | ui = tagList(
64 | selectizeInput(
65 | inputId = self$ns(paste0("mapping-", self$react$cols$current)),
66 | label = paste0("mapping-", self$react$cols$current),
67 | multiple = T,
68 | choices = self$react$cols$names,
69 | options = list(maxItems = 4L)
70 | )
71 | )
72 | )
73 |
74 | self$react$cols$mapping[[paste0("mapping-", self$react$cols$current)]] <- c("")
75 | self$react$cols$current <- self$react$cols$current + 1
76 | })
77 |
78 | self$obser$mappingKey <- observe({
79 | key <- paste0("mapping-", self$react$cols$current - 1)
80 | observeEvent(input[[key]], {
81 | self$react$cols$mapping[[key]] <- input[[key]]
82 | })
83 | })
84 |
85 | # Server logic
86 | # Ports assignment starts here
87 | self$assignPort({
88 | self$updateOutputPort(
89 | id = "mapping",
90 | output = reactive({
91 | self$react$cols$mapping
92 | })
93 | )
94 | })
95 |
96 | return(reactive(self$react$cols$mapping))
97 | }
98 | ),
99 | private = list(
100 | # any private functions?
101 | )
102 | )
103 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/DataFilter.R:
--------------------------------------------------------------------------------
1 |
2 | DataFilter <- R6::R6Class(
3 | "DataFilter",
4 | inherit = Panel,
5 | public = list(
6 | initialize = function(...) {
7 | # Mandatory
8 | super$initialize(...)
9 |
10 | # Ports definition starts here
11 | self$definePort({
12 | # At least one input
13 | self$addInputPort(
14 | name = "data",
15 | description = "Any rectangular data frame",
16 | sample = head(cars)
17 | )
18 |
19 | self$addOutputPort(
20 | name = "selected",
21 | description = "The original data frame with a new boolean column `selected` that indicates whether the new is selected.",
22 | sample = data.frame(a = c(1, 2), selected = c(TRUE, FALSE))
23 | )
24 |
25 | self$addOutputPort(
26 | name = "filtered",
27 | description = "The data frame containing only the selected rows.",
28 | sample = mtcars[c(1, 2, 5), ]
29 | )
30 | })
31 | },
32 | ui = function() {
33 | super$ui(
34 | status = "primary",
35 | tags$div(
36 | style = "overflow:auto",
37 | DT::dataTableOutput(self$ns("dtOutput"))
38 | )
39 | )
40 | },
41 | server = function(input, output, session) {
42 | # Mandatory
43 | super$server(input, output, session)
44 |
45 | # Server logic
46 | self$react$modOut <- reactive({
47 | d <- self$getInput("data")
48 | req(!is.null(d))
49 | })
50 |
51 | self$react$dtReturn <- reactive({
52 | d <- self$getInput("data")
53 | req(d)
54 | DT::datatable(d())
55 | })
56 |
57 | DTproxy <- DT::dataTableProxy("dtOutput", session = session)
58 | self$obser$clearRows <- observeEvent(input$clearRows, {
59 | DT::selectRows(DTproxy, NULL)
60 | })
61 |
62 | # Get a boolean vector indicating whether a row is selected
63 | self$react$selectedRowsBoolean <- reactive({
64 | d <- self$getInput("data")
65 | req(input$dtOutput_rows_selected, d)
66 |
67 | # Get all the row indices
68 | selected <- seq_len(nrow(d())) %in% input$dtOutput_rows_selected
69 |
70 | selected
71 | })
72 |
73 | # The data frame with only selected rows
74 | self$react$selectedRows <- reactive({
75 | d <- self$getInput("data")
76 | d()[self$react$selectedRowsBoolean(), ]
77 | })
78 |
79 | # The data frame with a `selected` boolean column
80 | self$react$dataSelected <- reactive({
81 | d <- self$getInput("data")
82 |
83 | cbind(d(), selected = self$react$selectedRowsBoolean())
84 | })
85 |
86 | output$dtOutput <- DT::renderDataTable({
87 | self$react$dtReturn()
88 | })
89 |
90 | # Ports assignment starts here
91 | self$assignPort({
92 | self$updateOutputPort(
93 | id = "selected",
94 | output = self$react$dataSelected
95 | )
96 |
97 | self$updateOutputPort(
98 | id = "filtered",
99 | output = self$react$selectedRows
100 | )
101 | })
102 | }
103 | ),
104 | private = list(
105 | # any private functions?
106 | )
107 | )
108 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/DatasetSelector.R:
--------------------------------------------------------------------------------
1 |
2 | DatasetSelector <- R6::R6Class(
3 | classname = "DatasetSelector",
4 | inherit = Panel,
5 | public = list(
6 | initialize = function(...) {
7 | # Mandatory
8 | super$initialize(...)
9 |
10 | # Ports definition starts here
11 | self$definePort({
12 | # Add an output port (optional)
13 | self$addOutputPort(
14 | name = "dataset",
15 | description = "the selected dataset from environment by data()",
16 | sample = head(mtcars)
17 | )
18 | })
19 | },
20 | ui = function() {
21 | super$ui(
22 | status = "primary",
23 | fluidRow(
24 | column(12, uiOutput(outputId = self$ns("selectDatasetUI"))),
25 | column(12,
26 | style = "overflow:auto",
27 | tableOutput(
28 | outputId = self$ns("selectedDataset")
29 | )
30 | )
31 | )
32 | )
33 | },
34 | server = function(input, output, session) {
35 | # Mandatory
36 | super$server(input, output, session)
37 |
38 | output$selectDatasetUI <- renderUI({
39 | allDatasets <- as.data.frame(data()$results)
40 | allDatasets <- as.character(allDatasets$Item)
41 | selectInput(
42 | inputId = self$ns("selectData"),
43 | label = "Select dataset",
44 | choices = allDatasets,
45 | selected = allDatasets[1],
46 | selectize = TRUE
47 | )
48 | })
49 | output$selectedDataset <- renderTable({
50 | allDatasets <- as.data.frame(data()$results)
51 | dataset <- self$react$getData()
52 | if (is(dataset, "data.frame")) {
53 | allDatasets[which(allDatasets$Item == input$selectData), ]
54 | } else {
55 | "Not a table!"
56 | }
57 | })
58 |
59 | self$react$getData <- reactive({
60 | tryCatch(
61 | {
62 | dataName <- input$selectData
63 | req(!is.null(dataName))
64 | print(dataName)
65 | dataset <- get(dataName)
66 | },
67 | error = function(e) {
68 | NULL
69 | }
70 | )
71 | })
72 |
73 | # Server logic
74 | self$react$modOut <- reactive({
75 | dataset <- self$react$getData()
76 | req(is(dataset, "data.frame"))
77 | print(dim(dataset))
78 | return(dataset)
79 | })
80 |
81 | # Ports assignment starts here
82 | self$assignPort({
83 | self$updateOutputPort(
84 | id = "dataset",
85 | output = self$react$modOut
86 | )
87 | })
88 |
89 | return(self$react$modOut)
90 | }
91 | ),
92 | private = list(
93 | # any private functions?
94 | )
95 | )
96 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/LinePlot.R:
--------------------------------------------------------------------------------
1 |
2 | LinePlot <- R6::R6Class(
3 | "LinePlot",
4 | inherit = BasePlot,
5 | public = list(
6 | chart = function(data, cols) {
7 | return(reactive({
8 | aes <- self$aes(cols())
9 | ggplot(data = data(), aes) +
10 | geom_point() +
11 | geom_line()
12 | }))
13 | }
14 | )
15 | )
16 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/Panel.R:
--------------------------------------------------------------------------------
1 | Panel <- R6::R6Class(
2 | "Panel",
3 | inherit = tidymodules::TidyModule,
4 | public = list(
5 | ui = function(..., status = "default", header = NULL) {
6 | shinyWidgets::panel(
7 | id = self$module_ns,
8 | heading = tagList(
9 | shiny::tags$h3(
10 | class = "panel-title",
11 | tagList(
12 | paste0("#", self$module_ns, " - ", class(self)[1]),
13 | header
14 | )
15 | )
16 | ),
17 | status = status,
18 | tagList(...)
19 | )
20 | }
21 | )
22 | )
23 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/PlotGenerator.R:
--------------------------------------------------------------------------------
1 |
2 | PlotGenerator <- R6::R6Class(
3 | classname = "PlotGenerator",
4 | inherit = Panel,
5 | public = list(
6 | plots = list(
7 | line = c("LinePlot", "Xiao"),
8 | box = c("BoxPlot", "David"),
9 | scatter = c("ScatterPlot", "Mustapha"),
10 | scatter3D = c("Scatter3DPlot", "Mustapha")
11 | ),
12 | initialize = function(...) {
13 | # Mandatory
14 | super$initialize(...)
15 |
16 | # Ports definition starts here
17 | self$definePort({
18 | # At least one input
19 | self$addInputPort(
20 | name = "mappings",
21 | description = "Named list of character vectors defining mapping of columns",
22 | sample = list(
23 | map1 = c("col1", "col2"),
24 | map2 = c("col4", "col7")
25 | )
26 | )
27 |
28 | self$addInputPort(
29 | name = "tables",
30 | description = "Named list of data tables forwarded to the plotting modules",
31 | sample = list(
32 | raw = data.frame(id = 1:10, val = 11:20),
33 | filtered = data.frame(id = 5:10, val = 15:20)
34 | )
35 | )
36 |
37 | # Add an output port (optional)
38 | self$addOutputPort(
39 | name = "selection",
40 | description = "Data table with user selected data points",
41 | sample = head(mtcars)
42 | )
43 | })
44 | },
45 | ui = function() {
46 | controls <- tagList(
47 | tags$br(), tags$br(),
48 | div(style = "display: inline-block;vertical-align:middle; width: 150px;", shiny::selectInput(self$ns("data"), label = "data", choices = c())),
49 | div(style = "display: inline-block;vertical-align:middle; width: 150px;", shiny::selectInput(self$ns("mapping"), label = "mapping", choices = c())),
50 | div(style = "display: inline-block;vertical-align:middle; width: 150px;", shiny::selectInput(self$ns("plot"), label = "plot type", choices = names(self$plots))),
51 | div(style = "display: inline-block;vertical-align:middle; width: 130px;", shiny::checkboxInput(self$ns("disconect_data"), label = "disconnect data", value = TRUE)),
52 | div(style = "display: inline-block;vertical-align:middle; width: 130px;", shiny::checkboxInput(self$ns("disconect_mapping"), label = "disconnect mapping", value = TRUE)),
53 | div(style = "display: inline-block;vertical-align:middle; width: 150px;", shiny::actionButton(self$ns("add"), label = NULL, icon = icon("plus")))
54 | )
55 |
56 | super$ui(
57 | status = "success",
58 | header = controls,
59 | fluidRow(
60 | id = self$ns("plotContainer")
61 | )
62 | )
63 | },
64 | server = function(input, output, session) {
65 | # Mandatory
66 | super$server(input, output, session)
67 |
68 | self$obser$updateData <- observe({
69 | t <- self$getInput("tables")
70 | shiny::req(t)
71 | shiny::updateSelectInput(session, "data", choices = names(t), selected = "raw")
72 | })
73 |
74 | self$obser$updateMapping <- observe({
75 | mPort <- self$getInput("mappings")
76 | req(mPort)
77 | options <- names(mPort())
78 | if (is.null(options)) {
79 | options <- list()
80 | }
81 | shiny::updateSelectInput(session, "mapping", choices = options)
82 | })
83 |
84 | self$obser$logAddClick <- observe({
85 | cat(paste0("\n\n\n", input$add, "\n\n\n"))
86 | })
87 |
88 | self$obser$add <- observeEvent(input$add, {
89 | reactive_mapping <- self$getInput("mappings")
90 | reactive_table <- self$getInput("tables")
91 |
92 | selected_mapping <- input$mapping
93 | selected_data <- input$data
94 | selected_plot <- input$plot
95 |
96 | req(reactive_mapping)
97 | req(reactive_table)
98 |
99 | current_mapping <- reactive_mapping()[[selected_mapping]]
100 | req(length(current_mapping) != 0)
101 |
102 | current_data <- reactive_table[[selected_data]]()
103 | req(!is.null(current_data))
104 |
105 | mod <- self$plots[[selected_plot]][1]
106 | author <- self$plots[[selected_plot]][2]
107 | mod <- eval(parse(text = mod))
108 |
109 | # dynamically create the selected charting module
110 | # Note that these are nested modules (module namespace includes Doug_)
111 | # Also note that the parent module "self" need to be specified when dynamically
112 | # creating a nested module
113 | mod <- mod$new(paste0(author, "_", input$add), parent = self)
114 |
115 | # feed a static version of the data/mapping to the module
116 | if (input$disconect_data) {
117 | reactive(current_data) %->% mod$iport("data")
118 | } else {
119 | reactive_table[[selected_data]] %->% mod$iport("data")
120 | }
121 |
122 | if (input$disconect_mapping) {
123 | reactive(current_mapping) %->% mod$iport("mapping")
124 | } else {
125 | reactive({
126 | reactive_mapping()[[selected_mapping]]
127 | }) %->% mod$iport("mapping")
128 | }
129 |
130 | # now call the module
131 | mod$callModule()
132 |
133 | mflag <- tagList(shiny::icon("bolt"), " ")
134 | if (input$disconect_mapping) {
135 | mflag <- ""
136 | }
137 |
138 | dflag <- tagList(shiny::icon("bolt"), " ")
139 | if (input$disconect_data) {
140 | dflag <- ""
141 | }
142 |
143 | header <- tagList(" - ", dflag, selected_data, " - ", mflag, selected_mapping)
144 |
145 | # render the module
146 | insertUI(
147 | selector = paste0("#", self$ns("plotContainer")),
148 | where = "afterBegin",
149 | immediate = TRUE,
150 | session = session,
151 | ui = tagList(
152 | column(6, mod$ui(header = header))
153 | )
154 | )
155 | })
156 |
157 | # Ports assignment starts here
158 | self$assignPort({
159 | # TODO: Add output port, e.g. brushing selection ?
160 | # self$updateOutputPort(
161 | # id = "dataset",
162 | # output = modOut)
163 | })
164 |
165 | return({})
166 | }
167 | )
168 | )
169 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/Scatter3DPlot.R:
--------------------------------------------------------------------------------
1 |
2 | Scatter3DPlot <- R6::R6Class(
3 | "Scatter3DPlot",
4 | inherit = BasePlot,
5 | public = list(
6 | ui = function(header = NULL) {
7 | super$ui(plotlyOutput, header)
8 | },
9 | renderPlot = function(d, cols) {
10 | shiny::req(length(cols()) > 3)
11 | renderPlotly({
12 | d <- as.data.frame(d())
13 | cols <- cols()
14 | plot_ly(x = d[, cols[1]], y = d[, cols[2]], z = d[, cols[3]], color = d[, cols[4]]) %>%
15 | layout(scene = list(
16 | xaxis = list(title = cols[1]),
17 | yaxis = list(title = cols[2]),
18 | zaxis = list(title = cols[3])
19 | ))
20 | })
21 | }
22 | )
23 | )
24 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/module/ScatterPlot.R:
--------------------------------------------------------------------------------
1 |
2 | ScatterPlot <- R6::R6Class(
3 | "ScatterPlot",
4 | inherit = BasePlot,
5 | public = list(
6 | chart = function(data, cols) {
7 | return(reactive({
8 | aes <- self$aes(cols())
9 | ggplot(data(), aes) +
10 | geom_point(aes(color = selected_)) +
11 | scale_color_manual(values = c("black", "#66D65C"), guide = "none")
12 | }))
13 | }
14 | )
15 | )
16 |
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/www/ERD.svg:
--------------------------------------------------------------------------------
1 | ../model/ERD.svg
--------------------------------------------------------------------------------
/inst/shiny/examples/4_communication/www/ports.svg:
--------------------------------------------------------------------------------
1 | ../model/ports.svg
--------------------------------------------------------------------------------
/inst/shiny/examples/5_counter/Counter.R:
--------------------------------------------------------------------------------
1 | #'
2 | #' Counter Module.
3 | #'
4 | #' @description
5 | #' This \href{https://opensource.nibr.com/tidymodules}{`{tm}`} module is a R6 class representing a Counter.
6 | #'
7 | #' @family tm
8 | #'
9 | #' @details
10 | #' More details about your module here.
11 | #'
12 | #'
13 | #' @import tidymodules
14 | #' @noRd
15 | Counter <- R6::R6Class(
16 | classname = "Counter",
17 | inherit = TidyModule,
18 | public = list(
19 | #' @description
20 | #' Module's initialization function.
21 | #' @param ... options
22 | #' @return An instance of Counter
23 | initialize = function(...) {
24 | # Don't remove the line below
25 | super$initialize(...)
26 |
27 | # Ports definition starts here...
28 | self$definePort({
29 | self$addInputPort(
30 | name = "reset",
31 | description = "An integer of class 'shinyActionButtonValue'",
32 | sample = 1
33 | )
34 |
35 | self$addOutputPort(
36 | name = "counter",
37 | description = "An integer representing the current counter value",
38 | sample = 3
39 | )
40 | })
41 | },
42 | #' @description
43 | #' Module's ui function.
44 | #' @return HTML tags list.
45 | ui = function(label = "Counter") {
46 | tagList(
47 | actionButton(self$ns("button"), label = label),
48 | verbatimTextOutput(self$ns("out"))
49 | )
50 | },
51 | #' @description
52 | #' Module's server function.
53 | #' @param input Shiny input
54 | #' @param output Shiny output
55 | #' @param session Shiny session
56 | server = function(input, output, session) {
57 | # Don't remove the line below
58 | super$server(input, output, session)
59 |
60 | # Module server logic starts here ...
61 | self$react$count <- reactiveVal(0)
62 | self$obser$add <- observeEvent(input$button, {
63 | self$react$count(self$react$count() + 1)
64 | })
65 | self$obser$reset <- observeEvent(self$execInput("reset"), {
66 | self$react$count(0)
67 | })
68 | output$out <- renderText({
69 | self$react$count()
70 | })
71 |
72 | self$assignPort({
73 | self$updateOutputPort("counter", self$react$count)
74 | })
75 | }
76 | )
77 | )
78 |
--------------------------------------------------------------------------------
/inst/shiny/examples/5_counter/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Title: tidymodules - Counter
2 | Author: Mustapha Larbaoui
3 | License: Apache
4 | DisplayMode: Showcase
5 | Tags: module, shiny
6 | Type: Shiny
7 |
--------------------------------------------------------------------------------
/inst/shiny/examples/5_counter/app.R:
--------------------------------------------------------------------------------
1 |
2 | # SM -----------> Shiny module
3 |
4 | library(shiny)
5 |
6 | source(system.file(package = "tidymodules", "shiny/examples/5_counter/counterSM.R"))
7 |
8 | SM_UI <- fluidPage(
9 | br(),
10 | shiny::fluidRow(
11 | column(1, actionButton("reset", label = "Reset")),
12 | column(1, counterButton("counter", "Counter")),
13 | column(1, "counter + 2 = ", textOutput("total_result"))
14 | )
15 | )
16 |
17 | SM_Server <- function(input, output, session) {
18 | reset <- reactive(input$reset)
19 | count <- callModule(counter, "counter", reset)
20 | output$total_result <- renderText({
21 | count() + 2
22 | })
23 | }
24 |
25 | # TM -----------> tidymodules
26 |
27 | library(tidymodules)
28 |
29 | source(system.file(package = "tidymodules", "shiny/examples/5_counter/Counter.R"))
30 |
31 | cnt <- Counter$new()
32 |
33 | TM_UI <- fluidPage(
34 | br(),
35 | shiny::fluidRow(
36 | column(1, actionButton("reset", label = "Reset")),
37 | column(1, cnt$ui("Counter")),
38 | column(1, "counter + 2 = ", textOutput("total_result"))
39 | )
40 | )
41 |
42 | TM_Server <- function(input, output, session) {
43 | reset <- reactive(input$reset)
44 |
45 | callModules()
46 | defineEdges({
47 | reset %>1% cnt
48 | })
49 |
50 | output$total_result <- renderText({
51 | cnt$execOutput("counter") + 2
52 | })
53 | }
54 |
55 | # shinyApp(SM_UI, SM_Server)
56 | shinyApp(TM_UI, TM_Server)
57 |
--------------------------------------------------------------------------------
/inst/shiny/examples/5_counter/counterSM.R:
--------------------------------------------------------------------------------
1 | # Example from Joe' blog (slightly modified)
2 | # https://shiny.rstudio.com/articles/modules.html
3 |
4 | library(shiny)
5 |
6 | counterButton <- function(id, label = "Counter") {
7 | ns <- NS(id)
8 | tagList(
9 | actionButton(ns("button"), label = label),
10 | verbatimTextOutput(ns("out"))
11 | )
12 | }
13 |
14 | counter <- function(input, output, session, reset) {
15 | count <- reactiveVal(0)
16 | observeEvent(input$button, {
17 | count(count() + 1)
18 | })
19 | observeEvent(reset(), {
20 | count(0)
21 | })
22 | output$out <- renderText({
23 | count()
24 | })
25 | count
26 | }
27 |
--------------------------------------------------------------------------------
/man/ModStore.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/ModStore.R
3 | \name{ModStore}
4 | \alias{ModStore}
5 | \title{R6 Class Representing a ModStore}
6 | \description{
7 | This class is used to create a storage for tidymodules objects.
8 | }
9 | \details{
10 | Manage applications, sessions and modules.
11 | }
12 | \examples{
13 |
14 | ## ------------------------------------------------
15 | ## Method `ModStore$new`
16 | ## ------------------------------------------------
17 |
18 | MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
19 | m <- MyModule$new()
20 | s <- m$getStore()
21 |
22 | ## ------------------------------------------------
23 | ## Method `ModStore$isStored`
24 | ## ------------------------------------------------
25 |
26 | MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
27 | m <- MyModule$new()
28 | s <- m$getStore()
29 | s$isStored(m)
30 | }
31 | \section{Methods}{
32 | \subsection{Public methods}{
33 | \itemize{
34 | \item \href{#method-ModStore-new}{\code{ModStore$new()}}
35 | \item \href{#method-ModStore-isStored}{\code{ModStore$isStored()}}
36 | \item \href{#method-ModStore-getGlobalSession}{\code{ModStore$getGlobalSession()}}
37 | \item \href{#method-ModStore-getSession}{\code{ModStore$getSession()}}
38 | \item \href{#method-ModStore-getSessions}{\code{ModStore$getSessions()}}
39 | \item \href{#method-ModStore-getMods}{\code{ModStore$getMods()}}
40 | \item \href{#method-ModStore-getEdges}{\code{ModStore$getEdges()}}
41 | \item \href{#method-ModStore-addEdge}{\code{ModStore$addEdge()}}
42 | \item \href{#method-ModStore-delEdges}{\code{ModStore$delEdges()}}
43 | \item \href{#method-ModStore-addMod}{\code{ModStore$addMod()}}
44 | \item \href{#method-ModStore-delMod}{\code{ModStore$delMod()}}
45 | \item \href{#method-ModStore-print}{\code{ModStore$print()}}
46 | \item \href{#method-ModStore-clone}{\code{ModStore$clone()}}
47 | }
48 | }
49 | \if{html}{\out{
}}
50 | \if{html}{\out{}}
51 | \if{latex}{\out{\hypertarget{method-ModStore-new}{}}}
52 | \subsection{Method \code{new()}}{
53 | Create a new ModStore object.
54 | Should be called once by the TidyModule class.
55 | Not to be called directly outside TidyModule.
56 | The ModStore object can be retrieved from any TidyModule object, see example below.
57 | \subsection{Usage}{
58 | \if{html}{\out{}}\preformatted{ModStore$new()}\if{html}{\out{
}}
59 | }
60 |
61 | \subsection{Returns}{
62 | A new \code{ModStore} object.
63 | }
64 | \subsection{Examples}{
65 | \if{html}{\out{}}
66 | \preformatted{MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
67 | m <- MyModule$new()
68 | s <- m$getStore()
69 | }
70 | \if{html}{\out{
}}
71 |
72 | }
73 |
74 | }
75 | \if{html}{\out{
}}
76 | \if{html}{\out{}}
77 | \if{latex}{\out{\hypertarget{method-ModStore-isStored}{}}}
78 | \subsection{Method \code{isStored()}}{
79 | Check if a module is stored in the current session.
80 | \subsection{Usage}{
81 | \if{html}{\out{}}\preformatted{ModStore$isStored(m)}\if{html}{\out{
}}
82 | }
83 |
84 | \subsection{Arguments}{
85 | \if{html}{\out{}}
86 | \describe{
87 | \item{\code{m}}{TidyModule object.}
88 | }
89 | \if{html}{\out{
}}
90 | }
91 | \subsection{Examples}{
92 | \if{html}{\out{}}
93 | \preformatted{MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
94 | m <- MyModule$new()
95 | s <- m$getStore()
96 | s$isStored(m)
97 | }
98 | \if{html}{\out{
}}
99 |
100 | }
101 |
102 | }
103 | \if{html}{\out{
}}
104 | \if{html}{\out{}}
105 | \if{latex}{\out{\hypertarget{method-ModStore-getGlobalSession}{}}}
106 | \subsection{Method \code{getGlobalSession()}}{
107 | Retrieve the global session 'global_session'.
108 | This is the session that exists outside the application server function
109 | \subsection{Usage}{
110 | \if{html}{\out{}}\preformatted{ModStore$getGlobalSession()}\if{html}{\out{
}}
111 | }
112 |
113 | }
114 | \if{html}{\out{
}}
115 | \if{html}{\out{}}
116 | \if{latex}{\out{\hypertarget{method-ModStore-getSession}{}}}
117 | \subsection{Method \code{getSession()}}{
118 | Retrieve a module session.
119 | This could be the global session or a user session.
120 | \subsection{Usage}{
121 | \if{html}{\out{}}\preformatted{ModStore$getSession(m)}\if{html}{\out{
}}
122 | }
123 |
124 | \subsection{Arguments}{
125 | \if{html}{\out{}}
126 | \describe{
127 | \item{\code{m}}{TidyModule object.}
128 | }
129 | \if{html}{\out{
}}
130 | }
131 | }
132 | \if{html}{\out{
}}
133 | \if{html}{\out{}}
134 | \if{latex}{\out{\hypertarget{method-ModStore-getSessions}{}}}
135 | \subsection{Method \code{getSessions()}}{
136 | Retrieve all sessions.
137 | \subsection{Usage}{
138 | \if{html}{\out{}}\preformatted{ModStore$getSessions()}\if{html}{\out{
}}
139 | }
140 |
141 | }
142 | \if{html}{\out{
}}
143 | \if{html}{\out{}}
144 | \if{latex}{\out{\hypertarget{method-ModStore-getMods}{}}}
145 | \subsection{Method \code{getMods()}}{
146 | Retrieve all modules.
147 | \subsection{Usage}{
148 | \if{html}{\out{}}\preformatted{ModStore$getMods(m)}\if{html}{\out{
}}
149 | }
150 |
151 | \subsection{Arguments}{
152 | \if{html}{\out{}}
153 | \describe{
154 | \item{\code{m}}{TidyModule object.}
155 | }
156 | \if{html}{\out{
}}
157 | }
158 | }
159 | \if{html}{\out{
}}
160 | \if{html}{\out{}}
161 | \if{latex}{\out{\hypertarget{method-ModStore-getEdges}{}}}
162 | \subsection{Method \code{getEdges()}}{
163 | Retrieve modules connections.
164 | \subsection{Usage}{
165 | \if{html}{\out{}}\preformatted{ModStore$getEdges(m)}\if{html}{\out{
}}
166 | }
167 |
168 | \subsection{Arguments}{
169 | \if{html}{\out{}}
170 | \describe{
171 | \item{\code{m}}{TidyModule object.}
172 | }
173 | \if{html}{\out{
}}
174 | }
175 | }
176 | \if{html}{\out{
}}
177 | \if{html}{\out{}}
178 | \if{latex}{\out{\hypertarget{method-ModStore-addEdge}{}}}
179 | \subsection{Method \code{addEdge()}}{
180 | Add modules connections into ModStore.
181 | An edge is either a connection between a reactive object and a module
182 | or between two modules.
183 | \subsection{Usage}{
184 | \if{html}{\out{}}\preformatted{ModStore$addEdge(from, to, mode = "direct", comment = NA)}\if{html}{\out{
}}
185 | }
186 |
187 | \subsection{Arguments}{
188 | \if{html}{\out{}}
189 | \describe{
190 | \item{\code{from}}{list with three elements: m -> module, type -> input or output, port -> port Id.}
191 |
192 | \item{\code{to}}{list with three elements: m -> module, type -> input or output, port -> port Id.}
193 |
194 | \item{\code{mode}}{The type of edge, default to 'direct'.}
195 |
196 | \item{\code{comment}}{Any additional comment.}
197 | }
198 | \if{html}{\out{
}}
199 | }
200 | }
201 | \if{html}{\out{
}}
202 | \if{html}{\out{}}
203 | \if{latex}{\out{\hypertarget{method-ModStore-delEdges}{}}}
204 | \subsection{Method \code{delEdges()}}{
205 | Remove module edges
206 | \subsection{Usage}{
207 | \if{html}{\out{}}\preformatted{ModStore$delEdges(m)}\if{html}{\out{
}}
208 | }
209 |
210 | \subsection{Arguments}{
211 | \if{html}{\out{}}
212 | \describe{
213 | \item{\code{m}}{TidyModule object.}
214 | }
215 | \if{html}{\out{
}}
216 | }
217 | }
218 | \if{html}{\out{
}}
219 | \if{html}{\out{}}
220 | \if{latex}{\out{\hypertarget{method-ModStore-addMod}{}}}
221 | \subsection{Method \code{addMod()}}{
222 | Add module into the ModStore.
223 | \subsection{Usage}{
224 | \if{html}{\out{}}\preformatted{ModStore$addMod(m)}\if{html}{\out{
}}
225 | }
226 |
227 | \subsection{Arguments}{
228 | \if{html}{\out{}}
229 | \describe{
230 | \item{\code{m}}{TidyModule object.}
231 | }
232 | \if{html}{\out{
}}
233 | }
234 | }
235 | \if{html}{\out{
}}
236 | \if{html}{\out{}}
237 | \if{latex}{\out{\hypertarget{method-ModStore-delMod}{}}}
238 | \subsection{Method \code{delMod()}}{
239 | Delete a module from the ModStore.
240 | \subsection{Usage}{
241 | \if{html}{\out{}}\preformatted{ModStore$delMod(m)}\if{html}{\out{
}}
242 | }
243 |
244 | \subsection{Arguments}{
245 | \if{html}{\out{}}
246 | \describe{
247 | \item{\code{m}}{TidyModule object.}
248 | }
249 | \if{html}{\out{
}}
250 | }
251 | }
252 | \if{html}{\out{
}}
253 | \if{html}{\out{}}
254 | \if{latex}{\out{\hypertarget{method-ModStore-print}{}}}
255 | \subsection{Method \code{print()}}{
256 | Print the ModStore object.
257 | \subsection{Usage}{
258 | \if{html}{\out{}}\preformatted{ModStore$print()}\if{html}{\out{
}}
259 | }
260 |
261 | }
262 | \if{html}{\out{
}}
263 | \if{html}{\out{}}
264 | \if{latex}{\out{\hypertarget{method-ModStore-clone}{}}}
265 | \subsection{Method \code{clone()}}{
266 | The objects of this class are cloneable with this method.
267 | \subsection{Usage}{
268 | \if{html}{\out{}}\preformatted{ModStore$clone(deep = FALSE)}\if{html}{\out{
}}
269 | }
270 |
271 | \subsection{Arguments}{
272 | \if{html}{\out{}}
273 | \describe{
274 | \item{\code{deep}}{Whether to make a deep clone.}
275 | }
276 | \if{html}{\out{
}}
277 | }
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/man/add_module.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/add_module.R
3 | \name{add_module}
4 | \alias{add_module}
5 | \title{Create a module}
6 | \usage{
7 | add_module(
8 | name,
9 | inherit = "TidyModule",
10 | path = getwd(),
11 | prefix = "tm",
12 | open = TRUE,
13 | dir_create = TRUE,
14 | export = FALSE
15 | )
16 | }
17 | \arguments{
18 | \item{name}{The class name of the module.}
19 |
20 | \item{inherit}{Parent module class. Default is TidyModule.}
21 |
22 | \item{path}{Where to created the file. Default is \code{getwd()}. The function will add \code{R} to the path if the sub-folder exists.}
23 |
24 | \item{prefix}{filename prefix. Default is \code{tm}. Set to `NULL`` to disable.}
25 |
26 | \item{open}{Should the file be opened?}
27 |
28 | \item{dir_create}{Creates the directory if it doesn't exist, default is \code{TRUE}.}
29 |
30 | \item{export}{Logical. Should the module be exported? Default is \code{FALSE}.}
31 | }
32 | \description{
33 | This function creates a \code{{tm}} module class inside the current folder.
34 | }
35 | \note{
36 | As a convention, this function will automatically capitalize the first character of the \code{name} argument.
37 | }
38 |
--------------------------------------------------------------------------------
/man/add_tm_snippets.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/snippets.R
3 | \name{add_tm_snippets}
4 | \alias{add_tm_snippets}
5 | \title{Add \code{{tm}} snippets to RStudio}
6 | \usage{
7 | add_tm_snippets(force = FALSE)
8 | }
9 | \arguments{
10 | \item{force}{Force the re-installation when the snippets are already installed.}
11 | }
12 | \description{
13 | This function adds useful \code{{tm}} code snippets to RStudio.
14 | }
15 |
--------------------------------------------------------------------------------
/man/callModules.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{callModules}
4 | \alias{callModules}
5 | \title{Call modules function}
6 | \usage{
7 | callModules()
8 | }
9 | \description{
10 | This utility function call all modules initialized in the global session.
11 | The global session is the session shared outside the server function of the application.
12 | All the modules initialized in the global session can be called with this function in a single call.
13 | The function take care of cloning and attaching them to the current user session.
14 |
15 | Note that this function can only be called in the app server function at the moment.
16 | We are working on supporting callModules within module server function for invoking nested modules.
17 | }
18 |
--------------------------------------------------------------------------------
/man/check_and_load.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/examples.R
3 | \name{check_and_load}
4 | \alias{check_and_load}
5 | \title{check if list of package namespaces exist, load them or display relevant information}
6 | \usage{
7 | check_and_load(packages)
8 | }
9 | \arguments{
10 | \item{packages}{character vector of package names}
11 | }
12 | \description{
13 | Utility function for managing package dependencies for tidymodules examples
14 | }
15 | \examples{
16 |
17 | check_and_load("ggplot2")
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/man/combine_ports.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/verbs.R
3 | \name{combine_ports}
4 | \alias{combine_ports}
5 | \title{Combine ports function}
6 | \usage{
7 | combine_ports(...)
8 | }
9 | \arguments{
10 | \item{...}{key/value pairs of ports}
11 | }
12 | \description{
13 | This function combines ports into a reactive list (reactiveValues)
14 | }
15 | \examples{
16 | \dontrun{
17 | # Somewhere in the app...
18 | MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
19 | MyModule$new("Mod1")
20 | MyModule$new("Mod2")
21 | MyModule$new("Mod3")
22 |
23 | # Must be in the server code and after calling the modules!
24 | callModules()
25 | observe({
26 | combine_ports(
27 | input_1 = mod(1)$getOutput(1),
28 | input_2 = mod(2)$getOutput(1)
29 | ) \%>1\% mod(3)
30 | })
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/man/defineEdges.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{defineEdges}
4 | \alias{defineEdges}
5 | \title{Function wrapper for ports connection expression.}
6 | \usage{
7 | defineEdges(x)
8 | }
9 | \arguments{
10 | \item{x}{expression}
11 | }
12 | \description{
13 | Used in server functions to define how modules are connected to each other.
14 | }
15 |
--------------------------------------------------------------------------------
/man/getCacheOption.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{getCacheOption}
4 | \alias{getCacheOption}
5 | \title{Retrieve cache option from the environment}
6 | \usage{
7 | getCacheOption()
8 | }
9 | \description{
10 | The cache option \code{tm_disable_cache} is a global options that enable or disable the use of existing modules from the current session.
11 | This option is \code{FALSE} by default and should be used in concordance with the \code{tm_session_type} global option. See \code{\link{session_type}} for a list of possible session type.
12 | }
13 |
--------------------------------------------------------------------------------
/man/getMod.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{getMod}
4 | \alias{getMod}
5 | \title{Retrieve module from ModStore}
6 | \usage{
7 | getMod(id = 1, group = NULL)
8 | }
9 | \arguments{
10 | \item{id}{Name or Id of the module}
11 |
12 | \item{group}{Group name}
13 | }
14 | \description{
15 | This utility function retrieve tidymodules from the central ModStore
16 | using module namespace/id and/or group
17 | }
18 | \examples{
19 |
20 | MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
21 | MyModule$new("MyFirst")
22 | MyModule$new("MySecond")
23 | MyModule$new("MyThird", group = "B")
24 |
25 | # MyFirst
26 | getMod(1)
27 | getMod("MyFirst")
28 |
29 | # MySecond
30 | getMod(2)
31 |
32 | # MyThird
33 | getMod(2)
34 | getMod("B-MyThird")
35 | getMod(1, group = "B")
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/man/getSessionId.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{getSessionId}
4 | \alias{getSessionId}
5 | \title{Function that generates session Id}
6 | \usage{
7 | getSessionId(session = getDefaultReactiveDomain())
8 | }
9 | \arguments{
10 | \item{session}{A shiny session as provide by the shiny server function.}
11 | }
12 | \value{
13 | A session ID
14 | }
15 | \description{
16 | tidymodules offers the ability to manage application sessions.
17 | This function is the main function used by tidymodules to find the current session Id.
18 | It takes an optional ShinySession object as argument. If null, default to the global_session.
19 | }
20 |
--------------------------------------------------------------------------------
/man/get_R6CG_list.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{get_R6CG_list}
4 | \alias{get_R6CG_list}
5 | \title{Recursive function for retrieving R6ClassGenerator inheritance}
6 | \usage{
7 | get_R6CG_list(r6cg)
8 | }
9 | \arguments{
10 | \item{r6cg}{A R6ClassGenerator object.}
11 | }
12 | \value{
13 | vector of class names
14 | }
15 | \description{
16 | This function is used to retrieve a list of class name that a R6ClassGenerator object inherit from.
17 | }
18 | \keyword{internal}
19 |
--------------------------------------------------------------------------------
/man/global_options.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{global_options}
4 | \alias{global_options}
5 | \title{tidymodules options}
6 | \description{
7 | List of global options used to adjust tidymodules configuration.
8 |
9 | \itemize{
10 | \item{\strong{tm_session_type}}{ : Define the type of the session, See available session types in \code{\link{session_type}} }
11 | \item{\strong{tm_session_custom}}{ : Used to set a custom function for generating the session Id. Used in concordance with the \code{CUSTOM} session type.}
12 | \item{\strong{tm_disable_cache}}{ : Disable caching of modules. This option is set to FALSE by default but is only relevant when user's session is managed properly. See also \code{\link{getCacheOption}}}
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/man/grapes-colon-c-greater-than-colon-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%:c>:\%}
4 | \alias{\%:c>:\%}
5 | \title{Multi-port mapping function}
6 | \usage{
7 | l \%:c>:\% r
8 | }
9 | \arguments{
10 | \item{l}{left module.}
11 |
12 | \item{r}{right module.}
13 | }
14 | \value{
15 | The right module
16 | }
17 | \description{
18 | This pipe copy all the left output ports to the right input ports.
19 | This is a copy not a mapping. The right module input ports will be created.
20 | }
21 |
--------------------------------------------------------------------------------
/man/grapes-colon-greater-than-colon-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%:>:\%}
4 | \alias{\%:>:\%}
5 | \title{Multi-port mapping function}
6 | \usage{
7 | l \%:>:\% r
8 | }
9 | \arguments{
10 | \item{l}{left module.}
11 |
12 | \item{r}{right module.}
13 | }
14 | \value{
15 | The right module
16 | }
17 | \description{
18 | This pipe maps all the left output ports to the right input ports.
19 | The right module input ports should match the number of the left output ports.
20 | }
21 |
--------------------------------------------------------------------------------
/man/grapes-colon-greater-than-greater-than-colon-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%:>>:\%}
4 | \alias{\%:>>:\%}
5 | \title{Multi-port mapping function}
6 | \usage{
7 | l \%:>>:\% r
8 | }
9 | \arguments{
10 | \item{l}{left module.}
11 |
12 | \item{r}{right module.}
13 | }
14 | \value{
15 | The left module.
16 | }
17 | \description{
18 | This pipe maps all the left output ports to the right input ports.
19 | }
20 |
--------------------------------------------------------------------------------
/man/grapes-colon-i-colon-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%:i:\%}
4 | \alias{\%:i:\%}
5 | \title{Multi-port mapping function}
6 | \usage{
7 | l \%:i:\% r
8 | }
9 | \arguments{
10 | \item{l}{left module.}
11 |
12 | \item{r}{right module.}
13 | }
14 | \value{
15 | The right module.
16 | }
17 | \description{
18 | This pipe copy all the left input ports to the right input ports.
19 | }
20 |
--------------------------------------------------------------------------------
/man/grapes-colon-pi-colon-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%:pi:\%}
4 | \alias{\%:pi:\%}
5 | \title{Multi-port mapping function}
6 | \usage{
7 | l \%:pi:\% r
8 | }
9 | \arguments{
10 | \item{l}{parent module.}
11 |
12 | \item{r}{child module.}
13 | }
14 | \value{
15 | The child module.
16 | }
17 | \description{
18 | This pipe copy all the left input ports to the right input ports in a parent to child mode.
19 | }
20 |
--------------------------------------------------------------------------------
/man/grapes-greater-than-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%->\%}
4 | \alias{\%->\%}
5 | \title{Port mapping function (port level)}
6 | \usage{
7 | lp \%->\% rp
8 | }
9 | \arguments{
10 | \item{lp}{Left port.}
11 |
12 | \item{rp}{Right port.}
13 | }
14 | \value{
15 | The right port
16 | }
17 | \description{
18 | This pipe works at the port level where left and right object are ports not modules.
19 | Take the left port and maps it to the right port.
20 | }
21 |
--------------------------------------------------------------------------------
/man/grapes-greater-than-greater-than-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%->>\%}
4 | \alias{\%->>\%}
5 | \title{Port mapping function (port level) - forward version}
6 | \usage{
7 | lp \%->>\% rp
8 | }
9 | \arguments{
10 | \item{lp}{Left port.}
11 |
12 | \item{rp}{Right port.}
13 | }
14 | \value{
15 | The left port
16 | }
17 | \description{
18 | This pipe works at the port level where left and right object are ports not modules.
19 | Take the left port and maps it to the right port. This is a forward version, i.e. return the left item.
20 | }
21 |
--------------------------------------------------------------------------------
/man/grapes-greater-than-greater-than-y-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%>>y\%}
4 | \alias{\%>>y\%}
5 | \alias{\%>>1\%}
6 | \alias{\%>>2\%}
7 | \alias{\%>>3\%}
8 | \alias{\%>>4\%}
9 | \alias{\%>>5\%}
10 | \alias{\%>>6\%}
11 | \alias{\%>>7\%}
12 | \alias{\%>>8\%}
13 | \alias{\%>>9\%}
14 | \alias{\%>>10\%}
15 | \title{Input port mapping function}
16 | \description{
17 | This pipe maps the left object (must be a reactive function
18 | or a reactivevalues object) to the right module's input port defined by
19 | the number in the operator (y).
20 | }
21 |
--------------------------------------------------------------------------------
/man/grapes-greater-than-y-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%>y\%}
4 | \alias{\%>y\%}
5 | \alias{\%>1\%}
6 | \alias{\%>2\%}
7 | \alias{\%>3\%}
8 | \alias{\%>4\%}
9 | \alias{\%>5\%}
10 | \alias{\%>6\%}
11 | \alias{\%>7\%}
12 | \alias{\%>8\%}
13 | \alias{\%>9\%}
14 | \alias{\%>10\%}
15 | \title{Input port mapping function}
16 | \description{
17 | This pipe maps the left object (must be a reactive function
18 | or a reactivevalues object) to the right module's input port
19 | defined by the number in the operator (y).
20 | }
21 |
--------------------------------------------------------------------------------
/man/grapes-x-greater-than-greater-than-y-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%x>>y\%}
4 | \alias{\%x>>y\%}
5 | \alias{\%1>>1\%}
6 | \alias{\%2>>1\%}
7 | \alias{\%3>>1\%}
8 | \alias{\%4>>1\%}
9 | \alias{\%5>>1\%}
10 | \alias{\%6>>1\%}
11 | \alias{\%7>>1\%}
12 | \alias{\%8>>1\%}
13 | \alias{\%9>>1\%}
14 | \alias{\%10>>1\%}
15 | \alias{\%1>>2\%}
16 | \alias{\%2>>2\%}
17 | \alias{\%3>>2\%}
18 | \alias{\%4>>2\%}
19 | \alias{\%5>>2\%}
20 | \alias{\%6>>2\%}
21 | \alias{\%7>>2\%}
22 | \alias{\%8>>2\%}
23 | \alias{\%9>>2\%}
24 | \alias{\%10>>2\%}
25 | \alias{\%1>>3\%}
26 | \alias{\%2>>3\%}
27 | \alias{\%3>>3\%}
28 | \alias{\%4>>3\%}
29 | \alias{\%5>>3\%}
30 | \alias{\%6>>3\%}
31 | \alias{\%7>>3\%}
32 | \alias{\%8>>3\%}
33 | \alias{\%9>>3\%}
34 | \alias{\%10>>3\%}
35 | \alias{\%1>>4\%}
36 | \alias{\%2>>4\%}
37 | \alias{\%3>>4\%}
38 | \alias{\%4>>4\%}
39 | \alias{\%5>>4\%}
40 | \alias{\%6>>4\%}
41 | \alias{\%7>>4\%}
42 | \alias{\%8>>4\%}
43 | \alias{\%9>>4\%}
44 | \alias{\%10>>4\%}
45 | \alias{\%1>>5\%}
46 | \alias{\%2>>5\%}
47 | \alias{\%3>>5\%}
48 | \alias{\%4>>5\%}
49 | \alias{\%5>>5\%}
50 | \alias{\%6>>5\%}
51 | \alias{\%7>>5\%}
52 | \alias{\%8>>5\%}
53 | \alias{\%9>>5\%}
54 | \alias{\%10>>5\%}
55 | \alias{\%1>>6\%}
56 | \alias{\%2>>6\%}
57 | \alias{\%3>>6\%}
58 | \alias{\%4>>6\%}
59 | \alias{\%5>>6\%}
60 | \alias{\%6>>6\%}
61 | \alias{\%7>>6\%}
62 | \alias{\%8>>6\%}
63 | \alias{\%9>>6\%}
64 | \alias{\%10>>6\%}
65 | \alias{\%1>>7\%}
66 | \alias{\%2>>7\%}
67 | \alias{\%3>>7\%}
68 | \alias{\%4>>7\%}
69 | \alias{\%5>>7\%}
70 | \alias{\%6>>7\%}
71 | \alias{\%7>>7\%}
72 | \alias{\%8>>7\%}
73 | \alias{\%9>>7\%}
74 | \alias{\%10>>7\%}
75 | \alias{\%1>>8\%}
76 | \alias{\%2>>8\%}
77 | \alias{\%3>>8\%}
78 | \alias{\%4>>8\%}
79 | \alias{\%5>>8\%}
80 | \alias{\%6>>8\%}
81 | \alias{\%7>>8\%}
82 | \alias{\%8>>8\%}
83 | \alias{\%9>>8\%}
84 | \alias{\%10>>8\%}
85 | \alias{\%1>>9\%}
86 | \alias{\%2>>9\%}
87 | \alias{\%3>>9\%}
88 | \alias{\%4>>9\%}
89 | \alias{\%5>>9\%}
90 | \alias{\%6>>9\%}
91 | \alias{\%7>>9\%}
92 | \alias{\%8>>9\%}
93 | \alias{\%9>>9\%}
94 | \alias{\%10>>9\%}
95 | \alias{\%1>>10\%}
96 | \alias{\%2>>10\%}
97 | \alias{\%3>>10\%}
98 | \alias{\%4>>10\%}
99 | \alias{\%5>>10\%}
100 | \alias{\%6>>10\%}
101 | \alias{\%7>>10\%}
102 | \alias{\%8>>10\%}
103 | \alias{\%9>>10\%}
104 | \alias{\%10>>10\%}
105 | \title{Single-port mapping function}
106 | \description{
107 | This pipe works at the module level.
108 | It maps the left module's output port defined by the left number (x)
109 | of the pipe operator to the right module's input port defined by the
110 | right number (y).
111 | }
112 |
--------------------------------------------------------------------------------
/man/grapes-x-greater-than-y-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%x>y\%}
4 | \alias{\%x>y\%}
5 | \alias{\%1>1\%}
6 | \alias{\%2>1\%}
7 | \alias{\%3>1\%}
8 | \alias{\%4>1\%}
9 | \alias{\%5>1\%}
10 | \alias{\%6>1\%}
11 | \alias{\%7>1\%}
12 | \alias{\%8>1\%}
13 | \alias{\%9>1\%}
14 | \alias{\%10>1\%}
15 | \alias{\%1>2\%}
16 | \alias{\%2>2\%}
17 | \alias{\%3>2\%}
18 | \alias{\%4>2\%}
19 | \alias{\%5>2\%}
20 | \alias{\%6>2\%}
21 | \alias{\%7>2\%}
22 | \alias{\%8>2\%}
23 | \alias{\%9>2\%}
24 | \alias{\%10>2\%}
25 | \alias{\%1>3\%}
26 | \alias{\%2>3\%}
27 | \alias{\%3>3\%}
28 | \alias{\%4>3\%}
29 | \alias{\%5>3\%}
30 | \alias{\%6>3\%}
31 | \alias{\%7>3\%}
32 | \alias{\%8>3\%}
33 | \alias{\%9>3\%}
34 | \alias{\%10>3\%}
35 | \alias{\%1>4\%}
36 | \alias{\%2>4\%}
37 | \alias{\%3>4\%}
38 | \alias{\%4>4\%}
39 | \alias{\%5>4\%}
40 | \alias{\%6>4\%}
41 | \alias{\%7>4\%}
42 | \alias{\%8>4\%}
43 | \alias{\%9>4\%}
44 | \alias{\%10>4\%}
45 | \alias{\%1>5\%}
46 | \alias{\%2>5\%}
47 | \alias{\%3>5\%}
48 | \alias{\%4>5\%}
49 | \alias{\%5>5\%}
50 | \alias{\%6>5\%}
51 | \alias{\%7>5\%}
52 | \alias{\%8>5\%}
53 | \alias{\%9>5\%}
54 | \alias{\%10>5\%}
55 | \alias{\%1>6\%}
56 | \alias{\%2>6\%}
57 | \alias{\%3>6\%}
58 | \alias{\%4>6\%}
59 | \alias{\%5>6\%}
60 | \alias{\%6>6\%}
61 | \alias{\%7>6\%}
62 | \alias{\%8>6\%}
63 | \alias{\%9>6\%}
64 | \alias{\%10>6\%}
65 | \alias{\%1>7\%}
66 | \alias{\%2>7\%}
67 | \alias{\%3>7\%}
68 | \alias{\%4>7\%}
69 | \alias{\%5>7\%}
70 | \alias{\%6>7\%}
71 | \alias{\%7>7\%}
72 | \alias{\%8>7\%}
73 | \alias{\%9>7\%}
74 | \alias{\%10>7\%}
75 | \alias{\%1>8\%}
76 | \alias{\%2>8\%}
77 | \alias{\%3>8\%}
78 | \alias{\%4>8\%}
79 | \alias{\%5>8\%}
80 | \alias{\%6>8\%}
81 | \alias{\%7>8\%}
82 | \alias{\%8>8\%}
83 | \alias{\%9>8\%}
84 | \alias{\%10>8\%}
85 | \alias{\%1>9\%}
86 | \alias{\%2>9\%}
87 | \alias{\%3>9\%}
88 | \alias{\%4>9\%}
89 | \alias{\%5>9\%}
90 | \alias{\%6>9\%}
91 | \alias{\%7>9\%}
92 | \alias{\%8>9\%}
93 | \alias{\%9>9\%}
94 | \alias{\%10>9\%}
95 | \alias{\%1>10\%}
96 | \alias{\%2>10\%}
97 | \alias{\%3>10\%}
98 | \alias{\%4>10\%}
99 | \alias{\%5>10\%}
100 | \alias{\%6>10\%}
101 | \alias{\%7>10\%}
102 | \alias{\%8>10\%}
103 | \alias{\%9>10\%}
104 | \alias{\%10>10\%}
105 | \title{Single-port mapping function}
106 | \description{
107 | This pipe works at the module level.
108 | It maps the left module's output port defined by the left
109 | number (x) of the pipe operator to the right module's
110 | input port defined by the right number (y).
111 | }
112 |
--------------------------------------------------------------------------------
/man/grapes-x-less-than-less-than-y-grapes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{\%x<>' or '<<' signs to return the item at the opposite of the arrow.}
15 |
16 | \item{rev}{Reverse operation. Boolean that indicates if this is a reverse operation, i.e. '<' or '<<'.}
17 | }
18 | \description{
19 | Create a pipe function for mapping a module output to a module input
20 | }
21 | \keyword{internal}
22 |
--------------------------------------------------------------------------------
/man/make_single_pipe.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{make_single_pipe}
4 | \alias{make_single_pipe}
5 | \title{make_single_pipe: Pipe function generator}
6 | \usage{
7 | make_single_pipe(p = NULL, f = FALSE, rev = FALSE)
8 | }
9 | \arguments{
10 | \item{f}{fast boolean. Used with the '>>' or '<<' signs to return the item at the opposite of the arrow.}
11 |
12 | \item{rev}{Reverse operation. Boolean that indicates if this is a reverse operation, i.e. '<' or '<<'.}
13 | }
14 | \description{
15 | Create a pipe function for mapping a reactive expression/value to a module input
16 | }
17 | \keyword{internal}
18 |
--------------------------------------------------------------------------------
/man/map_ports.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/verbs.R
3 | \name{map_ports}
4 | \alias{map_ports}
5 | \title{Ports mapping function}
6 | \usage{
7 | map_ports(
8 | leftModule = NULL,
9 | leftPort = 1,
10 | rightModule = NULL,
11 | rightPort = 1,
12 | reverse = FALSE
13 | )
14 | }
15 | \arguments{
16 | \item{leftModule}{The left module object}
17 |
18 | \item{leftPort}{Port name or Id of the left module's output port}
19 |
20 | \item{rightModule}{The right module object}
21 |
22 | \item{rightPort}{Port name or Id of the right module's input port}
23 |
24 | \item{reverse}{ligical value indicating which module to return. Default to FALSE, the right module}
25 | }
26 | \description{
27 | This function maps a module's outpout port to another module's input port.
28 | }
29 | \details{
30 | connect ports from two different modules
31 | }
32 |
--------------------------------------------------------------------------------
/man/mod.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{mod}
4 | \alias{mod}
5 | \title{Alias to getMod}
6 | \usage{
7 | mod(id = 1, group = NULL)
8 | }
9 | \arguments{
10 | \item{id}{Name or Id of the module}
11 |
12 | \item{group}{Group name}
13 | }
14 | \description{
15 | See \code{\link{getMod}}
16 | }
17 |
--------------------------------------------------------------------------------
/man/multi_port_map.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{multi_port_map}
4 | \alias{multi_port_map}
5 | \title{multi_port_map}
6 | \usage{
7 | multi_port_map(
8 | l_mod,
9 | r_mod,
10 | f = FALSE,
11 | t = NULL,
12 | is_parent = FALSE,
13 | is_copy = FALSE
14 | )
15 | }
16 | \arguments{
17 | \item{l_mod}{Left module.}
18 |
19 | \item{r_mod}{Right module.}
20 |
21 | \item{f}{fast boolean. If TRUE return the right module.}
22 |
23 | \item{t}{mapping type. Default to NULL. Could also be 'input' for mapping input to input.}
24 |
25 | \item{is_parent}{Is the left module a parent module? Default to FALSE.}
26 |
27 | \item{is_copy}{boolean. Default FALSE. Force the copy of the output ports.}
28 | }
29 | \description{
30 | Pipe function for sequentially mapping/copying left module ports to
31 | right module inputs
32 | }
33 | \keyword{internal}
34 |
--------------------------------------------------------------------------------
/man/oport.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{oport}
4 | \alias{oport}
5 | \title{Retrieve output module's port}
6 | \usage{
7 | oport(id = 1, p = 1, g = NULL)
8 | }
9 | \arguments{
10 | \item{id}{Name or Id of the module}
11 |
12 | \item{p}{Port Id or name}
13 |
14 | \item{g}{Module group name}
15 | }
16 | \description{
17 | This utility function retrieve the tidymodules output port specified in the arguments.
18 | }
19 |
--------------------------------------------------------------------------------
/man/port.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \name{port}
4 | \alias{port}
5 | \title{Retrieve module's port}
6 | \usage{
7 | port(id = 1, p = 1, t = "in", g = NULL)
8 | }
9 | \arguments{
10 | \item{id}{Name or Id of the module}
11 |
12 | \item{p}{Port Id or name}
13 |
14 | \item{t}{Port type, in or out}
15 |
16 | \item{g}{Module group name}
17 | }
18 | \description{
19 | This utility function retrieve the tidymodules port specified in the arguments.
20 | }
21 |
--------------------------------------------------------------------------------
/man/port_map.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/pipes.R
3 | \name{port_map}
4 | \alias{port_map}
5 | \title{port_map}
6 | \usage{
7 | port_map(lp, rp, f = FALSE)
8 | }
9 | \arguments{
10 | \item{lp}{Left port}
11 |
12 | \item{rp}{Right port}
13 |
14 | \item{f}{Forward operartion. Boolean.}
15 | }
16 | \description{
17 | port mapping function.
18 | }
19 | \keyword{internal}
20 |
--------------------------------------------------------------------------------
/man/race_ports.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/verbs.R
3 | \name{race_ports}
4 | \alias{race_ports}
5 | \title{Race ports function}
6 | \usage{
7 | race_ports(...)
8 | }
9 | \arguments{
10 | \item{...}{List of racing ports}
11 | }
12 | \description{
13 | This function collapse ports into a single port and make them race (i.e. always return the last one updated)
14 | }
15 | \examples{
16 | \dontrun{
17 | # Somewhere in the app...
18 | MyModule <- R6::R6Class("MyModule", inherit = tidymodules::TidyModule)
19 | MyModule$new("Mod1")
20 | MyModule$new("Mod2")
21 | MyModule$new("Mod3")
22 |
23 | # Must be in the server code and after calling the modules!
24 | callModules()
25 | observe({
26 | race_ports(
27 | mod(1)$getOutput(1),
28 | mod(2)$getOutput(1)
29 | ) \%>1\% mod(3)
30 | })
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/man/session_type.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utility.R
3 | \docType{data}
4 | \name{session_type}
5 | \alias{session_type}
6 | \title{List of possible session types}
7 | \format{
8 | An object of class \code{list} of length 3.
9 | }
10 | \usage{
11 | session_type
12 | }
13 | \description{
14 | tidymodules offers the ability to manage application sessions.
15 | At the moment the three options below are available.
16 |
17 | \itemize{
18 |
19 | \item{SHINY}{ : The default behaviour of shiny application and the default for tidymodules. Every time you access an application
20 | you get a new token Id that defines your application user session.}
21 |
22 | \item{USER}{ : This method defines a session based on the information available in the request object of shiny output.
23 | It is a concatenation of the variables REMOTE_ADDR, HTTP_HOST and PATH_INFO like below.
24 |
25 | \code{sid <- paste0(r$REMOTE_ADDR,"@",r$HTTP_HOST,r$PATH_INFO))}
26 |
27 | Note that the method is actually not working properly for now as the information available via the request object
28 | are not reflecting the actual user. We are working on a better method to uniquely identify a remote user.}
29 |
30 | \item{CUSTOM}{ : This method allow the developper to provide a custom function for generating the session Id.
31 | It relies on the global options \code{tm_session_custom} being set and pointing to a function taking a shiny output as argument.}
32 |
33 | }
34 | }
35 | \keyword{datasets}
36 |
--------------------------------------------------------------------------------
/man/showExamples.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/examples.R
3 | \name{showExamples}
4 | \alias{showExamples}
5 | \title{Launcher for the tidymodules examples}
6 | \usage{
7 | showExamples(id = NULL, server = F, options = NULL)
8 | }
9 | \arguments{
10 | \item{id}{Example ID. If null display list of examples with ID.}
11 |
12 | \item{server}{boolean. Is this a server call?}
13 |
14 | \item{options}{list of options to be passed to shinyApps or shinyDir}
15 | }
16 | \description{
17 | Helper function to launch the tidymodules examples.
18 | }
19 | \examples{
20 |
21 | if (interactive()) {
22 | showExamples(1)
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/man/tidymodules-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidymodules.R
3 | \docType{package}
4 | \name{tidymodules-package}
5 | \alias{tidymodules}
6 | \alias{tidymodules-package}
7 | \title{tidymodules: A robust framework for developing shiny modules}
8 | \description{
9 | tidymodules offers a robust framework for developing shiny modules based on R6 classes which should facilitates inter-modules communication.
10 | }
11 | \seealso{
12 | Useful links:
13 | \itemize{
14 | \item \url{https://github.com/Novartis/tidymodules}
15 | \item \url{http://opensource.nibr.com/tidymodules/}
16 | \item Report bugs at \url{https://github.com/Novartis/tidymodules/issues}
17 | }
18 |
19 | }
20 | \author{
21 | \strong{Maintainer}: Mustapha Larbaoui \email{mustapha.larbaoui@novartis.com}
22 |
23 | Other contributors:
24 | \itemize{
25 | \item Douglas Robinson \email{douglas.robinson@novartis.com} [contributor]
26 | \item Xiao Ni \email{xiao.ni@novartis.com} [contributor]
27 | \item David Granjon \email{david.granjon@novartis.com} [contributor]
28 | \item Stephen Eng \email{stefaneng13@gmail.com} [contributor]
29 | \item Marzieh Eslami Rasekh \email{marzie.rasekh@gmail.com} [contributor]
30 | \item Renan Sauteraud \email{rxs575@psu.edu} [contributor]
31 | }
32 |
33 | }
34 | \keyword{internal}
35 |
--------------------------------------------------------------------------------
/pkgdown/_pkgdown.yml:
--------------------------------------------------------------------------------
1 |
2 | url: https://opensource.nibr.com/tidymodules/
3 | home:
4 | links:
5 | - text: Github
6 | href: https://github.com/Novartis/tidymodules.git
7 | - text: Site
8 | href: https://opensource.nibr.com/tidymodules/
9 |
10 | toc:
11 | depth: 2
12 |
13 | navbar:
14 | components:
15 | articles:
16 | text: Articles
17 | menu:
18 | - text: 'Writing R6 classes and tidymodules'
19 | href: articles/intro.html
20 | - text: 'Namespace management'
21 | href: articles/namespace.html
22 | - text: 'Cross module communication'
23 | href: articles/communication.html
24 | - text: 'Inheritance'
25 | href: articles/inheritance.html
26 | - text: 'Session management'
27 | href: articles/session.html
28 |
29 | reference:
30 | - title: R6 Modules
31 | desc: List of R6 Classes provided by tidymodules
32 | contents:
33 | - '`ModStore`'
34 | - '`Store`'
35 | - '`TidyModule`'
36 | - title: Utility functions
37 | desc: List of utility functions
38 | contents:
39 | - '`showExamples`'
40 | - '`getSessionId`'
41 | - '`callModules`'
42 | - '`listModules`'
43 | - '`add_module`'
44 | - '`add_tm_snippets`'
45 | - '`check_and_load`'
46 | - '`getMod`'
47 | - '`mod`'
48 | - '`port`'
49 | - '`oport`'
50 | - '`iport`'
51 | - title: Configuration
52 | desc: List of available configurations
53 | contents:
54 | - '`global_options`'
55 | - '`session_type`'
56 | - '`getCacheOption`'
57 | - title: Port functions and operators
58 | desc: List of functions and pipe operators used to manage modules communication
59 | contents:
60 | - '`defineEdges`'
61 | - '`map_ports`'
62 | - '`combine_ports`'
63 | - '`race_ports`'
64 | - '`%>y%`'
65 | - '`%>>y%`'
66 | - '`%->%`'
67 | - '`%->>%`'
68 | - '`%x>y%`'
69 | - '`%x>>y%`'
70 | - '`%x:%`'
73 | - '`%:>>:%`'
74 | - '`%:c>:%`'
75 | - '`%:i:%`'
76 | - '`%:pi:%`'
77 | - title: internal
78 | contents:
79 | - '`UtilityModule`'
80 |
81 |
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/pkgdown/favicon/favicon.ico
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | library(testthat)
2 | library(tidymodules)
3 |
4 | test_check("tidymodules")
5 |
--------------------------------------------------------------------------------
/tests/testthat/test_dummy.R:
--------------------------------------------------------------------------------
1 | context("tests")
2 |
3 |
4 | # Configure this test to fit your need
5 | test_that(
6 | "my test",
7 | {
8 | TRUE
9 | }
10 | )
11 |
--------------------------------------------------------------------------------
/tidymodules.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: Default
4 | SaveWorkspace: Default
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 2
10 | Encoding: UTF-8
11 |
12 | RnwWeave: Sweave
13 | LaTeX: pdfLaTeX
14 |
15 | BuildType: Package
16 | PackageUseDevtools: Yes
17 | PackageInstallArgs: --no-multiarch --with-keep.source
18 | PackageRoxygenize: rd,collate,namespace
19 |
--------------------------------------------------------------------------------
/vignettes/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 | *.R
3 |
--------------------------------------------------------------------------------
/vignettes/communication.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | pagetitle: "Cross-module communication"
3 | title: "Cross-module communication"
4 | subtitle: "
"
5 | author: "Mustapha Larbaoui"
6 | date: "`r Sys.Date()`"
7 | output: rmarkdown::html_vignette
8 | vignette: >
9 | %\VignetteIndexEntry{Cross-module communication}
10 | %\VignetteEngine{knitr::rmarkdown}
11 | %\VignetteEncoding{UTF-8}
12 | ---
13 |
14 | ```{r setup, include = FALSE}
15 | knitr::opts_chunk$set(
16 | collapse = TRUE,
17 | comment = "#>"
18 | )
19 | ```
20 |
21 | Coming soon...
22 |
--------------------------------------------------------------------------------
/vignettes/figures/ERD.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/vignettes/figures/ERD.png
--------------------------------------------------------------------------------
/vignettes/figures/mod_network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/vignettes/figures/mod_network.png
--------------------------------------------------------------------------------
/vignettes/figures/ports_intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Novartis/tidymodules/daa948f31910686171476865051dcee9e6f5b10f/vignettes/figures/ports_intro.png
--------------------------------------------------------------------------------
/vignettes/inheritance.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | pagetitle: "Inheritance"
3 | title: "Inheritance"
4 | subtitle: "
"
5 | author: "Mustapha Larbaoui"
6 | date: "`r Sys.Date()`"
7 | output: rmarkdown::html_vignette
8 | vignette: >
9 | %\VignetteIndexEntry{Inheritance}
10 | %\VignetteEngine{knitr::rmarkdown}
11 | %\VignetteEncoding{UTF-8}
12 | ---
13 |
14 | ```{r setup, include = FALSE}
15 | knitr::opts_chunk$set(
16 | collapse = TRUE,
17 | comment = "#>"
18 | )
19 | ```
20 |
21 | Coming soon...
22 |
--------------------------------------------------------------------------------
/vignettes/intro.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | pagetitle: "Getting started with writing `{tidymodules}`"
3 | title: "Getting started with writing `{tidymodules}`"
4 | subtitle: "
"
5 | author: "Xiao Ni, Mustapha Larbaoui"
6 | date: "`r Sys.Date()`"
7 | output: rmarkdown::html_vignette
8 | vignette: >
9 | %\VignetteIndexEntry{Getting started with writing `{tidymodules}`}
10 | %\VignetteEngine{knitr::rmarkdown}
11 | %\VignetteEncoding{UTF-8}
12 | ---
13 |
14 | ```{r setup, include = FALSE}
15 | knitr::opts_chunk$set(
16 | collapse = TRUE,
17 | eval = TRUE,
18 | comment = "#>"
19 | )
20 | ```
21 |
22 | ## A quick introduction to R6 and Object Oriented Programming (OOP)
23 |
24 | `{tidymodules}` or `{tm}` in short, is based on R6 ([https://r6.r-lib.org/](https://r6.r-lib.org/)), which is an implementation of encapsulated object-oriented programming for R. Therefore knowledge of R6 is a prerequisite to develop `{tm}` modules.
25 |
26 | R6 provides a framework for OOP in R. Unlike the functional programming style, R6 encapsulates methods and fields in classes that instantiate into objects. R6 classes are similar to R's reference classes, but are more efficient and do not depend on S4 classes and the methods package.
27 |
28 | This vignette provides a brief overview of R6 for developers new to R6. For more information, developers are recommended to review the R6 packagedown site ([https://r6.r-lib.org/](https://r6.r-lib.org/)), as well as Chapter 14 of the Advanced R book ([https://adv-r.hadley.nz/r6.html](https://adv-r.hadley.nz/r6.html))
29 |
30 | ### R6 classes and methods
31 |
32 | `{tm}` depends on the R6 package which you can install from CRAN and load:
33 | ```{r , eval=FALSE}
34 | library(R6)
35 | ```
36 |
37 | R6 classes are created using the [`R6::R6Class()` function](https://r6.r-lib.org/reference/R6Class.html), which is the only function from the R6 package that is typically used. The following is a simple example of defining an R6 class:
38 | ```{r, eval=TRUE}
39 | Calculator <- R6::R6Class(
40 | classname = "Calculator",
41 | public = list(
42 | value = NA,
43 | initialize = function(value) {
44 | self$value <- value
45 | },
46 | add = function(x = 1) {
47 | self$value <- self$value + x
48 | invisible(self)
49 | }
50 | )
51 | )
52 | ```
53 | The first argument `classname` by convention uses `UpperCamelCase`. The second argument `public` encapsulates a list of methods (functions) and fields (objects) that make up the public interface of the object. By convention methods and fields use `snake_case`. Methods can access the methods and fields of the current object using `self$`. One should always assign the result of `R6Class()` into a variable with the same names as the `classname` because `R6Class()` returns an R6 object that defines the class.
54 |
55 |
56 | You can print the class definition:
57 | ```{r}
58 | Calculator
59 | ```
60 |
61 |
62 | To create a new instance of `Calculator`, use the `$new()` method. The `$initialize()` is an important method, which overrides the default behavior of `$new()`. In the above example, the `$initialize()` method initializes the `calculator1` object with `value = 0`.
63 | ```{r}
64 | calculator1 <- Calculator$new(0)
65 | calculator1
66 | ```
67 |
68 |
69 | You can then call the methods and access fields using `$`:
70 | ```{r}
71 | calculator1$add(10)
72 | calculator1$value
73 | ```
74 |
75 | You can also add methods after class creation as illustrated below for the existing `Calculator` R6 class, although new methods and fields are only available to new objects.
76 | ```{r}
77 | Calculator$set("public", "subtract", function(x = 1) {
78 | self$value <- self$value - x
79 | invisible(self)
80 | })
81 | Calculator
82 | ```
83 |
84 |
85 | Below are some key features of R6.
86 |
87 | - **Reference semantics**: objects are not copied when modified. R6 provides a `$clone()` method for making copy of an object. For more details, refer to https://r6.r-lib.org/reference/R6Class.html#cloning-objects.
88 | - **Public vs. private members**: `R6Class()` has a `private` argument for you to define private methods and fileds that can only be accessed from within the class, not from the outside.
89 | - **Inheritance**: as in classical OOP, one R6 class can inherit from another R6 class. Superclass methods can be accessed with `super$`.
90 |
91 | ## `tidymodules::TidyModule` class
92 |
93 | The `tidymodules::TidyModule` class is a R6 class and the parent of all `{tm}` modules.
94 |
95 | Below is partial code of the `TidyModule` class for illustration purpose. The `TidyModule` class includes many public methods. There are utility functions such as `callModules()`, `definePorts()`, `assignPort()` as well as functions that need to be overwritten such as `ui()`, `server()`, etc.
96 |
97 | Unlike conventional Shiny modules in funtional programming style, `{tm}` encapsulates functions such as ui() and server() as methods in a TidyModule class object. Module namespace ID is seamlessly managed within the module class for the ui and server. For complete technical documentation that includes other methods and fields, see `?TidyModule`.
98 | ```{r, eval=FALSE}
99 | TidyModule <- R6::R6Class(
100 | "TidyModule",
101 | public = list(
102 | id = NULL,
103 | module_ns = NULL,
104 | parent_ns = NULL,
105 | parent_mod = NULL,
106 | parent_ports = NULL,
107 | group = NULL,
108 | created = NULL,
109 | o = NULL,
110 | i = NULL,
111 | initialize = function(id = NULL, inherit = TRUE, group = NULL) {
112 | # details omitted
113 | },
114 | # Other methods such
115 | ui = function() {
116 | return(shiny::tagList())
117 | },
118 | server = function(input,
119 | output,
120 | session) {
121 | # Need to isolate this block to avoid unecessary triggers
122 | shiny::isolate({
123 | private$shiny_session <- session
124 | private$shiny_input <- input
125 | private$shiny_output <- output
126 | })
127 | },
128 | definePort = function(x) {
129 | shiny::isolate(x)
130 | },
131 | assignPort = function(x) {
132 | shiny::observe({
133 | shiny::isolate(x)
134 | })
135 | },
136 | # Other public methods omitted
137 | ),
138 | private = list(
139 | # Details omitted
140 | )
141 | )
142 | ```
143 |
144 |
145 |
146 | ## Writing your first `{tm}` module
147 |
148 | You can develop new `{tm}` modules by inheriting and extending the `tidymodules::TidyModule` class.
149 |
150 | Below is a minimal example, `RandomNumberGenerator`, defined with one input port and one output port. The input port is a random number seed that feeds into a random number generator, whose result serves as the module output.
151 |
152 | ```{r}
153 | # Module definition
154 | RandomNumMod <- R6::R6Class(
155 | "RandomNumGenerator",
156 | inherit = tidymodules::TidyModule,
157 | public = list(
158 | initialize = function(id = NULL) {
159 | super$initialize(id)
160 |
161 | self$definePort({
162 | self$addInputPort(
163 | name = "seed",
164 | description = "random number seed",
165 | sample = 123
166 | )
167 |
168 | self$addOutputPort(
169 | name = "number",
170 | description = "Random number",
171 | sample = 123
172 | )
173 | })
174 | },
175 | ui = function() {
176 | tagList(
177 | verbatimTextOutput(self$ns("text"))
178 | )
179 | },
180 | server = function(input, output, session) {
181 | super$server(input, output, session)
182 |
183 | result <- reactive({
184 | s <- self$getInput("seed")
185 | set.seed(s())
186 | floor(runif(1) * 1e5)
187 | })
188 |
189 | output$text <- renderPrint({
190 | s <- self$getInput("seed")
191 | print(paste0("seed = ", s()))
192 | print(paste0("number = ", result()))
193 | })
194 |
195 | self$assignPort({
196 | self$updateOutputPort(
197 | id = "number",
198 | output = result
199 | )
200 | })
201 | return(result)
202 | }
203 | )
204 | )
205 | ```
206 |
207 | Cross-communication between two `{tm}` modules is established using several flavours of the pipe `%>%` operator, as illustrated in the following code. The first module's output is fed as the random number seed for the second module.
208 | ```{r, eval=FALSE}
209 | ## Calling app
210 | randomNumMod1 <- RandomNumMod$new()
211 | randomNumMod2 <- RandomNumMod$new()
212 |
213 | ui <- tagList(
214 | fluidPage(
215 | randomNumMod1$ui(),
216 | randomNumMod2$ui()
217 | )
218 | )
219 | server <- function(input, output, session) {
220 | randomNumMod1$callModule()
221 | randomNumMod2$callModule()
222 |
223 | seed_1 <- reactive(123)
224 |
225 | observe({
226 | seed_1 %>1% randomNumMod1 %1>1% randomNumMod2
227 | })
228 | }
229 |
230 | shinyApp(ui = ui, server = server)
231 | ```
232 |
233 | ## Next steps
234 |
235 | To learn more about writing `{tm}` modules, read the examples.
236 |
237 |
--------------------------------------------------------------------------------
/vignettes/namespace.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | pagetitle: "Namespace management"
3 | title: "Namespace management"
4 | subtitle: "
"
5 | author: "Mustapha Larbaoui"
6 | date: "`r Sys.Date()`"
7 | output: rmarkdown::html_vignette
8 | vignette: >
9 | %\VignetteIndexEntry{Namespace management}
10 | %\VignetteEngine{knitr::rmarkdown}
11 | %\VignetteEncoding{UTF-8}
12 | ---
13 |
14 | ```{r setup, include = FALSE}
15 | knitr::opts_chunk$set(
16 | collapse = TRUE,
17 | comment = "#>"
18 | )
19 | ```
20 |
21 |
22 | As a reminder, the namespace is an important aspect of shiny module that prevents collision of input and output IDs in your application. The namespace identify each unique instance of the modules and need to be provided by the caller.
23 |
24 | `{tidymodules}` can be considered as an object-oriented organisational layer on top of shiny that does conventional module in the background.
25 | It does use namespace but make it optional to the users. In other words, you don't need to remember it!
26 |
27 | ## Optional namespace Id
28 |
29 | In the example below a `{tm}` module `MyMod` is defined and called with the `new()` function and with no arguments supplied.
30 | Printing the instance `m` to the console shows the structure of the module.
31 |
32 | ```{r, eval=TRUE}
33 | library(tidymodules)
34 |
35 | MyMod <- R6::R6Class(
36 | "MyMod",
37 | inherit = TidyModule
38 | )
39 |
40 | m <- MyMod$new()
41 |
42 | m
43 | ```
44 |
45 | The namespace Id for module `m` above is `MyMod-1`. It is a unique Id generated by `{tidymodules}` and composed of the class name and the initialisation order of the module.
46 |
47 |
48 | ## User defined namespace Id
49 |
50 | The user can also provide a more informative name when initialising a module.
51 |
52 |
53 | ```{r, eval=TRUE}
54 |
55 | m <- MyMod$new("Tom")
56 |
57 | m
58 | ```
59 |
60 | `Tom` end up being the namespace Id of `m`.
61 |
62 | The provided name must begin with a letter `[A-Za-z]` and may be followed by any number of letters, digits `[0-9]`, hyphens `-`, underscores `_`.
63 | You should not use the following characters as they have a special meaning on the UI ( CSS / jQuery ).
64 |
65 | > ~ ! @ $ % ^ & * ( ) + = , . / ' ; : " ? > < [ ] \ { } | ` #
66 |
67 |
68 | ## Namespace and nested modules
69 |
70 | The code below illustrates a very simple example of nested module and the corresponding namespace Ids. Here are few things to note about this example:
71 |
72 | * `ModA` and `ModB` are both `{tm}` classes as they both inherit from `tidymodules::TidyModule`
73 | * `ModB` is used as a nested class of `ModA`
74 | * `ModA` has a public field named `nested_mod`
75 | * The namespace of a nested module is always prefixed with its parent
76 |
77 |
78 | ```{r, eval=TRUE}
79 | ModB <- R6::R6Class(
80 | "ModB",
81 | inherit = TidyModule
82 | )
83 |
84 | ModA <- R6::R6Class(
85 | "ModA",
86 | inherit = TidyModule,
87 | public = list(
88 | nested_mod = NULL,
89 | initialize = function(...) {
90 | super$initialize(...)
91 | self$nested_mod <- ModB$new()
92 | }
93 | )
94 | )
95 |
96 | m <- ModA$new()
97 | n <- m$nested_mod
98 |
99 | m
100 | n
101 | ```
102 |
103 | ## Grouping modules
104 |
105 | This option allows the grouping of `{tm}` modules together to facilitate their retrieval from the `ModStore` and to better visualize them later in a network diagram.
106 |
107 |
108 | ```{r, eval=TRUE}
109 |
110 | ta <- MyMod$new("Tom", group = "A")
111 | tb <- MyMod$new("Tom", group = "B")
112 |
113 | ta
114 | tb
115 | ```
116 |
117 | As you can see above the group argument is used to define the final namespace Id of the modules.
118 | Those modules share the same name but have different namespace IDs.
119 |
120 | ## module's IDs and UI function
121 |
122 | A `{tm}` module namespace is a concatenation of many fields.
123 |
124 | * Some are optional fields: `name` and `group`
125 | * And some are managed by `{tidymodules}`: `id`, `parent_ns` and `module_ns`
126 |
127 | the module's namespace is built like this
128 |
129 | ```
130 | id = _
131 | module_ns = _
132 | ```
133 |
134 | ```{r,eval=TRUE}
135 |
136 | #' @field name Name of the module, either generated for or provided by the user.
137 | n$name
138 | ta$name
139 |
140 | #' @field group Group name of the module.
141 | n$group
142 | ta$group
143 |
144 | #' @field id ID of the module.
145 | n$id
146 | ta$id
147 |
148 | #' @field parent_ns Parent module namespace in case of nested modules.
149 | n$parent_ns
150 | ta$parent_ns
151 |
152 | #' @field module_ns Module namespace, unique identifier for the module.
153 | n$module_ns
154 | ta$module_ns
155 | ```
156 |
157 | Like in conventional module, `{tm}` module also requires wrapping UI elements with the namespace.
158 | The function named `ns()` available in all `{tm}` modules should be used to namespace the inputs.
159 | As you can see in the example below, you simply need to call the function using the `self` R6 keyword.
160 | There is no need to remember the modules's namespace anymore.
161 |
162 |
163 | ```{r,eval=TRUE}
164 |
165 | MyMod <- R6::R6Class(
166 | "MyMod",
167 | inherit = TidyModule,
168 | public = list(
169 | ui = function() {
170 | shiny::tagList(
171 | shiny::numericInput(self$ns("inputId"), NULL, 0)
172 | )
173 | }
174 | )
175 | )
176 |
177 | m <- MyMod$new()
178 | as.character(m$ui())
179 | ```
180 |
181 | ## `ModStore` and module lookup
182 |
183 | The `ModStore` is an internal repository for all `{tm}` modules and connections (see [communication article](communication.html) for learning how to connect modules). It is a shared environment created by `{tidymodules}` that orginizes the objects (modules and edges) by applications and sessions.
184 | This allows to track and easily retrieve the modules anywhere in the application.
185 |
186 | All the examples above show the creation of modules with the `new()` R6 function and the assignment to variables (pointers to the R6 objects).
187 | However `{tidymodules}` also offers the choice to not save module references and instead use the `getMod()` or `mod()` utility functions to retrieve existing module. Note that `mod()` is just an alias of `getMod()`.
188 |
189 | ```{r,eval=TRUE}
190 |
191 | MyMod$new("SaveMeInStore")
192 |
193 | # look-up by namespace ID
194 | mod("SaveMeInStore")
195 |
196 | # look-up by index
197 | mod(2)
198 |
199 | # look-up by index within a group
200 | mod(1, group = "A")
201 | ```
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
--------------------------------------------------------------------------------
/vignettes/session.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | pagetitle: "Session management"
3 | title: "Session management"
4 | subtitle: "
"
5 | author: "Mustapha Larbaoui"
6 | date: "`r Sys.Date()`"
7 | output: rmarkdown::html_vignette
8 | vignette: >
9 | %\VignetteIndexEntry{Session management}
10 | %\VignetteEngine{knitr::rmarkdown}
11 | %\VignetteEncoding{UTF-8}
12 | ---
13 |
14 | ```{r setup, include = FALSE}
15 | knitr::opts_chunk$set(
16 | collapse = TRUE,
17 | comment = "#>"
18 | )
19 | ```
20 |
21 | Coming soon...
22 |
--------------------------------------------------------------------------------
/vignettes/tidymodules.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | pagetitle: "Introduction to tidymodules"
3 | title: "Introduction to tidymodules"
4 | author: "Xiao Ni, Mustapha Larbaoui"
5 | date: "`r Sys.Date()`"
6 | output: rmarkdown::html_vignette
7 | vignette: >
8 | %\VignetteIndexEntry{Introduction to tidymodules}
9 | %\VignetteEngine{knitr::rmarkdown}
10 | %\VignetteEncoding{UTF-8}
11 | ---
12 |
13 | ```{r setup, include = FALSE}
14 | knitr::opts_chunk$set(
15 | collapse = TRUE,
16 | comment = "#>"
17 | )
18 | ```
19 |
20 | ## Overview
21 |
22 | This vignette aims to provide a high level introduction to tidymodules. We recommend reading this article for anyone who is new to tidymodules, especially those tidymodules module "consumers", who use existing module classes as a "black-box" in their Shiny app development. This article includes the following topics:
23 |
24 | - TidyModule ports: key infrastructure for managing cross-module communication
25 | - How to use tidymodules modules as a Shiny app developer and tidymodules consumer
26 | - Existing examples
27 |
28 | If you would like to develop new tidymodules modules, please refer to the vignettes under "Articles".
29 |
30 | ## TidyModule structure: ports
31 |
32 | ### Introducing ports
33 |
34 | In conventional Shiny modules, communication between modules is realized through the following
35 |
36 | - **Input:** passing reactives as functional parameters into the Shiny `callModule()` function
37 | - **Output:** returning reactives in the module `server()` function.
38 |
39 | It can be challenging to keep track of the arbitrary number of input and output reactives for complex apps with many interacting modules.
40 |
41 | To address this challenge, we introduced input/output ports as fields of the TidyModule class object in tidymodules.
42 | The concept is illustrated in the following diagram. In this example, Module1 and Module2 each have input and output ports that hold reactives.
43 | The ports defined in each modules provides a data structure that allow TidyModule to establish a communication between them.
44 |
45 | 
46 |
47 | The two modules are connected via a tidymodules pipe operator `%x>y%` where x and y could be any numbers from 1 to 10. For example,`%1>1%` means the left module's first output port (x = 1) is mapped to the first input port (y = 1) of the right module. Multiple connected tidymodules modules with such directed edges form a directed graph network, which will be further discussed later in this article.
48 |
49 | ### Finding out ports of a tidymodules module
50 |
51 | To find out the port structure, simply print the module object on the console. The following example shows that the `Addition` module has one input port named "left" and one output port named "total" that are both empty, i.e. not being assigned an input reactive (input ports) or injected into the server code (output ports).
52 | ```{r}
53 | library(shiny)
54 | library(tidymodules)
55 | source(system.file(package = "tidymodules", "shiny/examples/1_simple_addition/Addition.R"))
56 | Addition$new()
57 | ```
58 |
59 |
60 | ## Using tidymodules modules
61 |
62 | The basic workflow of using tidymodules modules in a Shiny app is the following
63 |
64 | - Load tidymodules module class definition
65 | - Identify module structure such as input/output ports, ui(), and server() functionalities
66 | - Instantiate new module objects from tidymodules classes
67 | - Construct app `ui()` using module `ui()` methods
68 | - In app `server()` inject tidymodules module `server()` logic using `callModules()` or the `callModule()` function of the module object, like `myMod$callModule()`.
69 | - Set up module communication/relationship via tidymodules pipe operators and functions.
70 |
71 | The workflow is illustrated in the following example, which is available at
72 |
73 | Example 1: Simple addition [](https://tidymodules.shinyapps.io/1_simple_addition/)
74 |
75 | ### Load module definition
76 |
77 | ```{r, eval=FALSE}
78 | library(tidymodules)
79 | # source tidymodules Addition module definition
80 | source(system.file(package = "tidymodules", "shiny/examples/1_simple_addition/Addition.R"))
81 | ```
82 |
83 | ### Instantiate module objects
84 |
85 | Notice that the namespace argument in `$new()` is optional and `tidymodules` will automatically generate a namespace ID if not provided.
86 | ```{r, eval=T}
87 | # Instantiate two Addition module objects
88 | Addition$new()
89 | Addition$new()
90 | ```
91 | Also notice that it is not necessary to give a name to the `Addition$new()` object. `tidymodules` provides `mod()` or `getMod()` function to help users conveniently retrieve module objects via their numerical ID or namespace ID.
92 |
93 | ### Adding `ui()`
94 |
95 | In the app `ui()`, we call the `ui()` method of each module object.
96 | ```{r, eval=FALSE}
97 | ui <- fixedPage(
98 | h2("tidymodules : Addition example"),
99 | shiny::fluidRow(
100 | sliderInput("first_number", label = "Enter your first number", min = 1, max = 100, value = 1), br(),
101 |
102 | # Calling module ui() methods
103 | mod(1)$ui(), br(),
104 | mod(2)$ui(), br(),
105 | "Total: ", textOutput("total_result")
106 | )
107 | )
108 | ```
109 |
110 | ### Add module `server()` logic using `callModules()`
111 | Here we use the `callModules()` function to call the server() methods for the two modules that we created.
112 | ```{r, eval=FALSE}
113 | server <- function(input, output, session) {
114 | # call the server() functions for all existing tidymodules modules in the global environment
115 | callModules()
116 | }
117 | ```
118 |
119 | ### Establish cross-module communication via 'tidy' pipe operators
120 |
121 | The module communication is established through the pipe operators: `first %>1% mod(1) %1>1% mod(2)`. Note that in `first` must be a Shiny reactive value or endpoint in order to server as an input to other tidymodules modules.
122 | ```{r, eval=FALSE}
123 | server <- function(input, output, session) {
124 | # call the server() functions for all existing tidymodules modules in the global environment
125 | callModules()
126 |
127 | first <- reactive({
128 | req(input$first_number)
129 | })
130 |
131 | # Setting up module commmunication
132 | observe({
133 | first %>1% mod(1) %1>1% mod(2)
134 | })
135 |
136 | output$total_result <- renderText({
137 | result <- mod(2)$getOutput(1)
138 | result()
139 | })
140 | }
141 |
142 | shinyApp(ui, server)
143 | ```
144 |
145 | We also provide utility functions to help identify and connect ports using the port names. For more information about the pipe operators, refer to the functional documentation under "Reference" tab.
146 |
147 | ### Module relational network
148 |
149 | Tidymodules module objects are mananged by `ModStore` in tidymodules. For example, in the
150 | example 2 - Linkled scatter [](https://tidymodules.shinyapps.io/2_linked_scatter/) you can find the sessions, module objects, edges and port mapping diagram in the "Help | ModStore" tab.
151 |
152 | Below is the module relationship network digram generated by `tidymodules` using the `visNetwork` package.
153 |
154 |
155 |
156 | ### Inheritance and Entity Relational Diagram
157 |
158 | For more details about class and ports inheritance, see article [inheritance](inheritance.html).
159 | The diagram below illustrates the relation between the classes defined in the example 4 of tidymodules [](https://tidymodules.shinyapps.io/4_communication/).
160 |
161 | 
162 |
163 | ## Other examples
164 |
165 | You can list all Shiny examples that come with the `tidymodules` package by the `showExamples()` function. We recommend going through these examples to help you understand the use patterns.
166 | ```{r}
167 | showExamples()
168 | ```
169 |
170 | We have already used the first example to illustrate the basic usage of tidymodules, below we briefly describe the other examples.
171 |
172 | ### Example 2: linked scatter plot
173 |
174 | This example illustrates the tidymodules implementation of the classical Shiny module example of [two linked scatter plots](https://shiny.rstudio.com/gallery/module-example.html).
175 |
176 | ### Example 3: nested modules
177 |
178 | This example [](https://tidymodules.shinyapps.io/3_nested_modules/) illustrates constructing and using nested modules in the `tidymodules` framework, as well as dynamically creating tidymodules modules.
179 |
180 | ### Example 4: module communication
181 |
182 | This is a comprehensive example [](https://tidymodules.shinyapps.io/4_communication/) to illustrate mutiple advanced features such as
183 |
184 | - Inheritance
185 | - Port operations: `combine_ports()`
186 | - Enable/disable module communication
187 |
--------------------------------------------------------------------------------