coprl 3.0.0.beta.1 → 3.0.0.beta.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +9 -15
  3. data/CHANGELOG.md +105 -13
  4. data/Gemfile +12 -1
  5. data/Gemfile.lock +105 -32
  6. data/README.md +10 -5
  7. data/app/demo/components/dialogs.pom +1 -1
  8. data/app/demo/components/nav/menu.pom +0 -1
  9. data/app/demo/components/snackbar.pom +9 -3
  10. data/app/demo/events/content_as_form.pom +3 -3
  11. data/app/demo/events/halted.pom +23 -0
  12. data/app/demo/events/nav/drawer.pom +1 -1
  13. data/app/demo/events/tagged_input.pom +2 -2
  14. data/app/demo/patterns/search_select.pom +1 -1
  15. data/app/demo/plugins/animate.pom +144 -0
  16. data/app/demo/plugins/cacheable.pom +64 -0
  17. data/app/demo/plugins/clipboard.pom +21 -0
  18. data/app/demo/plugins/color_picker.pom +17 -0
  19. data/app/demo/plugins/google_maps.pom +24 -0
  20. data/app/demo/plugins/iframe.pom +14 -0
  21. data/app/demo/plugins/image_crop.pom +1 -1
  22. data/app/demo/plugins/markup.pom +14 -0
  23. data/app/demo/plugins/nav/drawer.pom +1 -1
  24. data/app/demo/plugins/script.pom +17 -0
  25. data/app/demo/plugins/scroll_to.pom +22 -0
  26. data/app/demo/plugins/timer.pom +24 -0
  27. data/app/demo/shared/context_list.pom +1 -1
  28. data/config.ru +15 -1
  29. data/coprl.gemspec +2 -2
  30. data/lib/coprl/presenters/cli.rb +10 -0
  31. data/lib/coprl/presenters/dsl/components/actions/base.rb +5 -1
  32. data/lib/coprl/presenters/dsl/components/base.rb +6 -3
  33. data/lib/coprl/presenters/dsl/components/event.rb +6 -1
  34. data/lib/coprl/presenters/dsl/components/multi_select.rb +3 -3
  35. data/lib/coprl/presenters/dsl/components/table.rb +2 -2
  36. data/lib/coprl/presenters/generators/plugin.rb +21 -6
  37. data/lib/coprl/presenters/generators/templates/plugin/.github/workflows/semantic-release.yml +41 -0
  38. data/lib/coprl/presenters/generators/templates/plugin/.releaserc +15 -0
  39. data/lib/coprl/presenters/generators/templates/plugin/.ruby-version +1 -0
  40. data/lib/coprl/presenters/generators/templates/plugin/README.md.tt +34 -0
  41. data/lib/coprl/presenters/generators/templates/plugin/demo/plugin.pom.tt +15 -0
  42. data/lib/coprl/presenters/generators/templates/plugin/lib/coprl/presenters/plugins/components/actions/dsl.rb.tt +1 -1
  43. data/lib/coprl/presenters/generators/templates/plugin/lib/coprl/presenters/plugins/version.rb.tt +3 -0
  44. data/lib/coprl/presenters/generators/templates/plugin/presenter_plugin.gemspec.tt +8 -7
  45. data/lib/coprl/presenters/generators/templates/plugin/views/components/application/component.erb.tt +1 -1
  46. data/lib/coprl/presenters/settings.rb +1 -1
  47. data/lib/coprl/presenters/version.rb +1 -1
  48. data/lib/coprl/presenters/web_client/helpers/headers.rb +8 -6
  49. data/lib/coprl/presenters/web_client/helpers/rails.rb +1 -0
  50. data/lib/coprl/presenters/web_client/helpers/rails/template_helper.rb +10 -0
  51. data/lib/coprl/presenters/web_client/helpers/sinatra.rb +1 -0
  52. data/lib/coprl/presenters/web_client/helpers/sinatra/template_helper.rb +20 -0
  53. data/public/bundle.js +10 -4
  54. data/public/wc.js +10 -4
  55. data/rails-engine/app/controllers/coprl_controller.rb +20 -1
  56. data/rails-engine/app/views/layouts/coprl.html.erb +4 -4
  57. data/rails-engine/config/initializers/session.rb +8 -0
  58. data/rails-engine/config/routes.rb +2 -1
  59. data/views/mdc/assets/js/components/events/posts.js +10 -6
  60. data/views/mdc/body/{_preamble.erb → _wrapper.erb} +16 -5
  61. data/views/mdc/components/_card.erb +2 -2
  62. data/views/mdc/components/_checkbox.erb +1 -1
  63. data/views/mdc/components/_chip.erb +2 -2
  64. data/views/mdc/components/_content.erb +2 -2
  65. data/views/mdc/components/_datetime.erb +1 -1
  66. data/views/mdc/components/_dialog.erb +2 -2
  67. data/views/mdc/components/_form.erb +2 -2
  68. data/views/mdc/components/_grid.erb +2 -2
  69. data/views/mdc/components/_hidden_field.erb +1 -1
  70. data/views/mdc/components/_multi_select.erb +1 -1
  71. data/views/mdc/components/_number_field.erb +1 -1
  72. data/views/mdc/components/_radio_button.erb +1 -1
  73. data/views/mdc/components/_rich_text_area.erb +1 -1
  74. data/views/mdc/components/_select.erb +1 -1
  75. data/views/mdc/components/_slider.erb +2 -2
  76. data/views/mdc/components/_stepper.erb +4 -4
  77. data/views/mdc/components/_switch.erb +1 -1
  78. data/views/mdc/components/_text_area.erb +1 -1
  79. data/views/mdc/components/_text_field.erb +1 -1
  80. data/views/mdc/components/buttons/_image.erb +1 -1
  81. data/views/mdc/components/unordered_list/_list_item.erb +3 -3
  82. data/views/mdc/layout.erb +4 -4
  83. metadata +31 -17
  84. data/app/demo/components/google_maps.pom +0 -22
  85. data/app/demo/components/snackbar_attached.pom +0 -6
  86. data/lib/coprl/presenters/generators/templates/plugin/README.md +0 -253
  87. data/lib/coprl/presenters/plugins/google_maps.rb +0 -24
  88. data/lib/coprl/presenters/plugins/google_maps/google_map.erb +0 -10
  89. data/lib/coprl/presenters/plugins/google_maps/google_map.rb +0 -41
  90. data/views/mdc/body/_postamble.erb +0 -17
@@ -0,0 +1,14 @@
1
+ Coprl::Presenters.define(:iframe, namespace: :plugins) do
2
+ helpers Demo::Helpers::IndentedGrid
3
+ attach :top_nav
4
+ attach :plugin_drawer
5
+ plugin :iframe
6
+ page_title 'Iframe'
7
+
8
+
9
+ indented_grid do
10
+ subheading 'Embed an iFrame'
11
+ iframe 'https://en.wikipedia.org/wiki/Special:Random', height: "500px",scrolling: :yes # | :no (default)
12
+ attach :code, file: __FILE__
13
+ end
14
+ end
@@ -20,7 +20,7 @@ Coprl::Presenters.define(:image_crop, namespace: :plugins) do
20
20
  grid do
21
21
  column 12 do
22
22
  card do
23
- text 'Drop it here'
23
+ text 'Drop an image or click'
24
24
  button icon: :cloud_upload
25
25
  end
26
26
  end
@@ -0,0 +1,14 @@
1
+ Coprl::Presenters.define(:markup, namespace: :plugins) do
2
+ helpers Demo::Helpers::IndentedGrid
3
+ attach :top_nav
4
+ attach :plugin_drawer
5
+ plugin :markup
6
+ page_title 'Markup'
7
+
8
+
9
+ indented_grid do
10
+ markup "<h1>Hello Cruel World</h1>"
11
+
12
+ attach :code, file: __FILE__
13
+ end
14
+ end
@@ -7,7 +7,7 @@ Coprl::Presenters.define(:plugin_drawer, namespace: :plugins) do
7
7
  loads :index
8
8
  end
9
9
  end
10
- %i(image_crop chart).sort.each do |comp|
10
+ %i(image_crop chart google_maps cacheable script scroll_to clipboard markup iframe color_picker timer animate).sort.each do |comp|
11
11
  item titleize(comp) do
12
12
  event :click do
13
13
  loads comp
@@ -0,0 +1,17 @@
1
+ Coprl::Presenters.define(:script, namespace: :plugins) do
2
+ helpers Demo::Helpers::IndentedGrid
3
+ attach :top_nav
4
+ attach :plugin_drawer
5
+ plugin :script
6
+ page_title 'Script'
7
+
8
+ script inline: <<~JAVASCRIPT
9
+ alert( 'Hello, world!' );
10
+ JAVASCRIPT
11
+ indented_grid do
12
+ subheading 'You can write arbitrary javascript using the script component. For example, the alert you just dismissed.'
13
+
14
+ attach :code, file: __FILE__
15
+ end
16
+
17
+ end
@@ -0,0 +1,22 @@
1
+ Coprl::Presenters.define(:scroll_to, namespace: :plugins) do
2
+ helpers Demo::Helpers::IndentedGrid
3
+ attach :top_nav
4
+ attach :plugin_drawer
5
+ plugin :scroll_to
6
+ page_title 'Scroll to'
7
+
8
+ indented_grid do
9
+ subheading 'Sometimes you need to scroll to find content after some action. This is what the scroll to plugin is for.'
10
+ button :scroll do
11
+ event :click do
12
+ scroll_to :scroll_target #, smooth: true, offset: 100
13
+ end
14
+ end
15
+ (1..100).each do
16
+ body '.'
17
+ end
18
+ title 'Thanks for scrolling here', id: :scroll_target
19
+
20
+ attach :code, file: __FILE__
21
+ end
22
+ end
@@ -0,0 +1,24 @@
1
+ Coprl::Presenters.define(:timer, namespace: :plugins) do
2
+ helpers Demo::Helpers::IndentedGrid
3
+ attach :top_nav
4
+ attach :plugin_drawer
5
+ plugin :timer
6
+ page_title 'Timer'
7
+
8
+ indented_grid do
9
+ subheading 'Using an expiration timer'
10
+
11
+ count_down_timer DateTime.now + + Rational(130, 60 * 60 * 24), # add 2 minutes and 10 seconds
12
+ expired_message: 'Something important (shoping cart for example) has Expired',
13
+ safe_color: 'green',
14
+ warn_color: 'red',
15
+ delete_url: 'TODO', # TODO URL to delete something on expiration
16
+ delete_params: {}, # parmeters to pass on expiration
17
+ redirect_url: 'TODO', # Where to redirect after delete?
18
+ redirect_params: {} do
19
+ icon 'fas fa-stopwatch', color: :white
20
+ end
21
+
22
+ attach :code, file: __FILE__
23
+ end
24
+ end
@@ -9,7 +9,7 @@ Coprl::Presenters.define(:context_list) do
9
9
  end
10
10
 
11
11
  def scrubbed_context
12
- scrubbed_keys = context.fetch(:exclude) {%w{title file hide_time _presenter_ _namespace1_ session}}
12
+ scrubbed_keys = context.fetch(:exclude) {%w{title file hide_time _presenter_ _namespace1_ session request controller action presenter}}
13
13
  context.select {|k, _| !scrubbed_keys.include?(k.to_s)}
14
14
  end
15
15
  end
data/config.ru CHANGED
@@ -9,7 +9,7 @@ if ENV['VOOM_ENV'] == 'integration_testing'
9
9
  end
10
10
 
11
11
  ENV['VOOM_ROOT'] = File.expand_path(__dir__)
12
- ENV['GOOGLE_API_KEY'] = 'AIzaSyDhSgj9XSBLY5E9Rx5pP2ILQ7IXnD4uX2Q'
12
+ require 'dotenv/load'
13
13
  require 'coprl'
14
14
 
15
15
  require 'rack/cors'
@@ -27,6 +27,20 @@ Coprl::Presenters::Settings.configure do |config|
27
27
  }
28
28
  end
29
29
 
30
+ require 'pry'
31
+ require 'coprl/presenters/plugins/cacheable'
32
+ cache_store = Concurrent::Hash.new
33
+ # Quick and dirty demo always growing memory cache -- DONT DO THIS IN PRODUCTION!
34
+ def cache_store.fetch(key, options=nil, &block)
35
+ result = super(key, &block)
36
+ store(key, result) unless has_key?(key)
37
+ result
38
+ end
39
+
40
+ Coprl::Presenters::Plugins::Cacheable::Settings.configure do |config|
41
+ config.cache=cache_store
42
+ end
43
+
30
44
  use Coprl::Presenters::Demo::Search
31
45
  use Coprl::Presenters::Demo::Echo
32
46
  use Coprl::Presenters::Demo::Slow
data/coprl.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Russell Edens']
10
10
  spec.email = ["rx@russelledens.net\n"]
11
11
 
12
- spec.summary = 'COPRL Presenters Gem'
12
+ spec.summary = 'COmmon PResenter Language - A Ruby gem for joyfully writing modern user interfaces in Rack and Rails.'
13
13
  spec.homepage = 'http://github.com/rx/presenters'
14
14
  spec.license = 'MIT'
15
15
 
@@ -38,6 +38,6 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency 'bundler', '>= 1.13'
39
39
  spec.add_development_dependency 'rake', '~> 13.0'
40
40
  spec.add_development_dependency 'rspec', '~> 3.0'
41
- spec.add_development_dependency 'gem-release', '~> 2.0'
42
41
  spec.add_development_dependency 'shotgun', '~> 0.9'
42
+ spec.add_development_dependency 'timecop', '~> 0.9.4 '
43
43
  end
@@ -1,5 +1,6 @@
1
1
  require 'thor'
2
2
  require_relative 'generators/plugin'
3
+ require_relative 'version'
3
4
 
4
5
  module Coprl
5
6
  module Presenters
@@ -20,6 +21,15 @@ module Coprl
20
21
  end
21
22
  end
22
23
  class Cli < Thor
24
+ def self.exit_on_failure?
25
+ true
26
+ end
27
+ map %w[--version -v] => :__print_version
28
+
29
+ desc "--version, -v", "print the version"
30
+ def __print_version
31
+ puts Coprl::Presenters::Version::VERSION
32
+ end
23
33
  desc "generate", "generate a plugin"
24
34
  subcommand "generate", Generate
25
35
  end
@@ -25,10 +25,14 @@ module Coprl
25
25
  private
26
26
 
27
27
  def extract_options!
28
- %i(path presenter target input_tag headers).each do |option|
28
+ %i(path presenter target headers).each do |option|
29
29
  option_value = attribs.delete(option){:not_found}
30
30
  @options.merge!({option => option_value}) unless option_value==:not_found
31
31
  end
32
+ # Special case ... the input tag is defined at the component base level for all components
33
+ # By the time we get here we have put it into its accessor via the super call
34
+ # So we manually move it to the options for all actions
35
+ @options.merge!(input_tag: self.input_tag) if self.input_tag
32
36
  end
33
37
 
34
38
  def extract_dynamic_params(hash)
@@ -15,16 +15,19 @@ module Coprl
15
15
  include Mixins::YieldTo
16
16
  extend Pluggable
17
17
 
18
- attr_reader :type, :id, :tag, :attributes, :draggable, :drop_zone, :css_class
18
+ attr_reader :type, :id, :input_tag, :attributes, :draggable, :drop_zone, :css_class
19
19
 
20
20
  alias attribs attributes
21
21
 
22
- def initialize(type:, parent:, id: nil, tag: nil, **attributes, &block)
22
+ def initialize(type:, parent:, id: nil, tag: nil, input_tag: nil, **attributes, &block)
23
23
  @draggable = attributes.delete(:draggable) {nil}
24
24
  @drop_zone = attributes.delete(:drop_zone) {nil}
25
25
  @css_class = Array(attributes.delete(:class) {nil})
26
26
  @id = id || generate_id
27
- @tag = tag
27
+ @input_tag = input_tag || tag
28
+ logger.warn(
29
+ 'The `tag` attribute is deprecated. ' \
30
+ 'Please use `input_tag` instead. This will change in a future feature release.') unless tag.nil?
28
31
  @type = type
29
32
  @parent = parent
30
33
  @attributes = attributes
@@ -27,6 +27,7 @@ module Coprl
27
27
  wait_for_download: wait_for_download,
28
28
  params: params, &block)
29
29
  end
30
+ alias load loads
30
31
 
31
32
  def replaces(target, presenter, input_tag: nil, ignore_input_values: [], **params, &block)
32
33
  self << Actions::Replaces.new(parent: self,
@@ -36,6 +37,7 @@ module Coprl
36
37
  ignore_input_values: Array(ignore_input_values),
37
38
  params: params, &block)
38
39
  end
40
+ alias replace replaces
39
41
 
40
42
  # Method can be one of :post, :put, :delete or :patch
41
43
  def posts(path, input_tag: nil, headers: nil, **params, &block)
@@ -45,8 +47,10 @@ module Coprl
45
47
  headers: headers,
46
48
  params: params, &block)
47
49
  end
48
-
50
+ alias post posts
49
51
  alias creates posts
52
+ alias create posts
53
+
50
54
 
51
55
  def updates(path, input_tag: nil, headers: nil, **params, &block)
52
56
  self << Actions::Updates.new(parent: self,
@@ -63,6 +67,7 @@ module Coprl
63
67
  headers: headers,
64
68
  params: params, &block)
65
69
  end
70
+ alias delete deletes
66
71
 
67
72
  def dialog(dialog_id, **params, &block)
68
73
  self << Actions::Dialog.new(parent: self,
@@ -16,8 +16,8 @@ module Coprl
16
16
  def check_option(**attribs, &block)
17
17
  @options << CheckOption.new(parent: self,
18
18
  name: @name,
19
- tag: @tag,
20
- **attribs.delete_if{ |k,v| [:tag, :name].include?(k) }, &block)
19
+ input_tag: @input_tag,
20
+ **attribs.delete_if{ |k,v| [:tag, :name, :input_tag].include?(k) }, &block)
21
21
  end
22
22
 
23
23
  class CheckOption < EventBase
@@ -33,7 +33,7 @@ module Coprl
33
33
  self.checkbox(name: "#{attribs[:name]}[]",
34
34
  value: @value,
35
35
  text: @text,
36
- tag: tag,
36
+ input_tag: self.input_tag,
37
37
  checked: @selected,
38
38
  disabled: @disabled,
39
39
  &block)
@@ -58,10 +58,10 @@ module Coprl
58
58
  def checkbox(**attributes, &block)
59
59
  return @checkbox if locked?
60
60
  field_name = @type == :header ? 'all' : "#{attributes.delete(:name)}[]"
61
- tag = @type == :header ? '' : @parent.tag
61
+ tag = @type == :header ? '' : @parent.input_tag
62
62
  @checkbox = Components::Checkbox.new(parent: self,
63
63
  name: field_name,
64
- tag: tag,
64
+ input_tag: tag,
65
65
  **attributes,
66
66
  &block)
67
67
  end
@@ -32,13 +32,13 @@ module Coprl
32
32
 
33
33
  def template_file(template, target_filename=template, source_path=nil, target_path=source_path)
34
34
  source = join_path(TEMPLATES_ROOT, source_path, "#{template}.tt")
35
- destination = join_path(plusin_name, target_path, target_filename)
35
+ destination = join_path(plugin_name, target_path, target_filename)
36
36
  template source, destination
37
37
  end
38
38
 
39
39
  def file(file, target_filename=file, source_path=nil, target_path=nil)
40
40
  source = join_path(TEMPLATES_ROOT, source_path, file)
41
- destination = join_path(plusin_name, target_path, target_filename)
41
+ destination = join_path(plugin_name, target_path, target_filename)
42
42
 
43
43
  copy_file source, destination
44
44
  end
@@ -59,17 +59,28 @@ module Coprl
59
59
  underscore(name)
60
60
  end
61
61
 
62
- def plusin_name
62
+ def plugin_name
63
63
  "#{underscored_name}_presenter_plugin"
64
64
  end
65
65
  end
66
66
 
67
+ def create_version
68
+ template_file('version.rb', "version.rb", LIB_ROOT, "lib/#{underscored_name}_presenter_plugin")
69
+ end
70
+
67
71
  def create_root_files
68
72
  file('.gitignore')
69
73
  template_file('Gemfile')
70
74
  template_file('LICENSE.txt')
71
75
  template_file('presenter_plugin.gemspec', "#{underscored_name}_presenter_plugin.gemspec")
72
- file 'README.md'
76
+ template_file 'README.md'
77
+ end
78
+
79
+ def create_semantic_release_github_action
80
+ file('.releaserc')
81
+ file('.ruby-version')
82
+ file('semantic-release.yml', 'semantic-release.yml',
83
+ dir('.github', 'workflows'), dir('.github', 'workflows'))
73
84
  end
74
85
 
75
86
  def create_plugin
@@ -90,9 +101,9 @@ module Coprl
90
101
  dir('views', 'assets', 'css', 'components'))
91
102
  template_file('component.js', "#{underscored_name}.js",
92
103
  dir('views', 'assets', 'js', 'components'))
93
- template_file('component.erb', "#{underscored_name}.erb",
104
+ template_file('component.erb', "_#{underscored_name}.erb",
94
105
  dir('views', 'components', 'application'))
95
- template_file('component_header.erb', "#{underscored_name}_header.erb",
106
+ template_file('component_header.erb', "_#{underscored_name}_header.erb",
96
107
  dir('views', 'components', 'application'))
97
108
  end
98
109
 
@@ -115,6 +126,10 @@ module Coprl
115
126
  lib_dir('helpers'),
116
127
  named_dir('helpers'))
117
128
  end
129
+
130
+ def create_demo_pom
131
+ template_file('plugin.pom', "#{underscored_name}.pom", dir('demo'))
132
+ end
118
133
  end
119
134
  end
120
135
  end
@@ -0,0 +1,41 @@
1
+ name: CI
2
+
3
+ # Controls when the action will run.
4
+ on:
5
+ # Triggers the workflow on push or pull request events but only for the master branch
6
+ push:
7
+ branches: [ master ]
8
+ pull_request:
9
+ branches: [ master ]
10
+
11
+ # Allows you to run this workflow manually from the Actions tab
12
+ workflow_dispatch:
13
+
14
+ # A workflow run is made up of one or more jobs that can run sequentially or in parallel
15
+ jobs:
16
+ # This workflow contains a single job called "build"
17
+ build:
18
+ # The type of runner that the job will run on
19
+ runs-on: ubuntu-latest
20
+
21
+ # Steps represent a sequence of tasks that will be executed as part of the job
22
+ steps:
23
+ - name: Checkout
24
+ uses: actions/checkout@v2
25
+ - name: Setup Ruby
26
+ uses: ruby/setup-ruby@v1
27
+ with:
28
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
29
+ - name: Semantic Release
30
+ uses: cycjimmy/semantic-release-action@v2
31
+ with:
32
+ extra_plugins: |
33
+ @semantic-release/git
34
+ @semantic-release/changelog
35
+ @semantic-release/github
36
+ semantic-release-rubygem
37
+ dry_run: false
38
+ env:
39
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
+ GEM_HOST_API_KEY: ${{ secrets.GEM_HOST_API_KEY }}
41
+
@@ -0,0 +1,15 @@
1
+ {
2
+ "branches": ['+([0-9])?(.{+([0-9]),x}).x', 'master', 'next', 'next-major', {name: 'beta', prerelease: true}, {name: 'alpha', prerelease: true}],
3
+ "plugins": [
4
+ "@semantic-release/commit-analyzer",
5
+ "@semantic-release/release-notes-generator",
6
+ "@semantic-release/changelog",
7
+ "@semantic-release/github",
8
+ ["semantic-release-rubygem",
9
+ {
10
+ "gemPublish": false
11
+ }
12
+ ],
13
+ "@semantic-release/git"
14
+ ]
15
+ }