redmine_extensions 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e9dafb1894abe51656840de62914a350b3c9d78c
4
- data.tar.gz: 6dcbff3717bfac6323606d4dffd6bb959ae5bf42
3
+ metadata.gz: 23ca96f77329db75217df08f43cdf52bbd7ed2e8
4
+ data.tar.gz: 16757b5cddacb84ec7d1bbe0637d034bfa8aeaaf
5
5
  SHA512:
6
- metadata.gz: 491d56cfee43c1daed176f2f94551daf40d02fb4718d215fb37743e634a8b439dd53f6db1c18f0be6a654c584ca0b6ebea8027018669b80a4077bd57b1e15d69
7
- data.tar.gz: 0f2b44330b8a107a674b06652acd5bc82f91354c60f58065ffec60e76d1a30903ae0d1552695b1f634f37cf379bef2e859d32517870f890b66423051cd11db37
6
+ metadata.gz: 1fcd410c0f9f7d0a095aff4547322f1681f7a940f25ccc0c46303f670c895386141a09c99d76198880c1fc00b02922b4d944eb25b7ce888520bf9798aeed466c
7
+ data.tar.gz: 4d0a6f90ea945c090d2be5a823814e402525ad1a4850b401f4bf365049bf48a0ccc988a1bd4ede7ac1dea47c9f380bb16da452689c3d589052ead86d18250733
data/Rakefile CHANGED
@@ -37,7 +37,7 @@ task :generate_test_plugin do
37
37
  require RedmineExtensions::Engine.root.join('spec', 'support', 'plugin_generator').to_s
38
38
  PluginGenerator.generate_test_plugin!
39
39
  end
40
- RSpec::Core::RakeTask.new(spec: :generate_test_plugin) do |spec|
40
+ RSpec::Core::RakeTask.new(spec: [:generate_test_plugin, 'app:redmine:plugins:migrate']) do |spec|
41
41
  spec.pattern = 'spec/**/*_spec.rb'
42
42
  spec.exclude_pattern = 'spec/redmine/**/*_spec.rb'
43
43
  end
@@ -205,7 +205,27 @@ module RedmineExtensions
205
205
 
206
206
  end
207
207
 
208
-
208
+ # Returns a multiselect autocomplete input tag tailored for selecting 1..N values from defined source by +jsonpath_or_array+.
209
+ # Preselected will be values in +selected_values+ parameter, or if those are empty, and +select_first_value+ option is set,
210
+ # it will select first value from source. See warning from this parameter!
211
+ # Additional options on the input tag can be passed as a hash with +options+.
212
+ # These options will be passed to the handling javascript.
213
+ # Available format for +selected_values+:
214
+ # * Array of values. It will search for the values in available_values for values names assigned.
215
+ # * Array of objects in format: {id: <value send in form>, value: <label - user shown value>}
216
+ # Available options are:
217
+ # * +multiple+ - tells if more than one value can be selected.
218
+ # * +preload+ - tells if values should be preloaded all at once - in one request - if jsonpathh is a source, this parameter expect it to return all available values.
219
+ # * +load_immediately+ - tells if values should be loaded immediatelly after page loaded, or wait for first use of the field.
220
+ # Warning! if this option is false, selected values passed in first format will be ignored till it is loaded.
221
+ # Please use second format for proper functionality.
222
+ # * +show_toggle_button+ - only valid with <tt>multiple: true, preload: true</tt> options set. Shows toggle button to expand select to the multiselect tag.
223
+ # * +select_first_value+ - if selectd_values are empty, with this option first available value will be selected.
224
+ # Available only with <tt>preload: true</tt> option.
225
+ # With <tt>load_immediately: false</tt> it will appear kinda weird for user because it will select the value after user starts to interact with input.
226
+ # please consider if this is what you want.
227
+ # * +rootElement+ - Has sence only if jsonpath is used for available values. It tells if the json response has values wrapped under root element.
228
+ # For response like <tt>{projects: [[<name>, <id>], [<name2>, <id2>]]}</tt> user option <tt>rootElement: 'projects'</tt>
209
229
  def autocomplete_field_tag(name, jsonpath_or_array, selected_values, options = {})
210
230
  options.reverse_merge!({select_first_value: false, show_toggle_button: false, load_immediately: false, preload: true, multiple: true})
211
231
  options[:id] ||= sanitize_to_id(name)
@@ -224,5 +244,29 @@ module RedmineExtensions
224
244
  end
225
245
  end
226
246
 
247
+ # Returns a multiselect autocomplete input tag tailored for accessing a specified attribute (identified by +method+) on an object
248
+ # assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
249
+ # hash with +options+. These options will be passed to the handling javascript as in the example shown.
250
+ # Available values for select are passed as +choices+ attribute. It can be Array of values, or json path for later loading or autocomplete.
251
+ # Please see autocomplete_field_tag documentation for more information about options and available values format combinations.
252
+ # HTML options can be passed as a hash with +html_options+. These options will be passed to the input element.
253
+ #
254
+ # ==== Examples
255
+ # autocomplete_field(:issue, :tag_ids, Tag.all.pluck(:name, :id), multiple: true, show_toggle_button: true)
256
+ # # => <span class="easy-multiselect-tag-container"> \
257
+ # <input type="text" id="issue_tags" /> \
258
+ # <button type="button" tabindex="-1" class="..." role="button" ...>
259
+ # <span class="ui-button-icon-primary ui-icon ui-icon-triangle-1-s"></span><span class="ui-button-text">&nbsp;</span>
260
+ # </button>
261
+ # <a class="icon icon-add clear-link"></a> # toggle button to the multiselect tag from show_toggle_button option
262
+ # ...(wraping service tags)
263
+ # <input type="hidden" name="issue[tag_ids][]" value="#{@issue.tag_ids.first}" />
264
+ # <input type="hidden" name="issue[tag_ids][]" value="#{@issue.tag_ids.second}" />
265
+ # ...(wraping service tags end)
266
+ # </span>
267
+ def autocomplete_field(object_name, method, choices, options={}, html_options={})
268
+ Tags::AutocompleteField.new(object_name, method, self, choices, options, html_options).render
269
+ end
270
+
227
271
  end
228
272
  end
@@ -1,6 +1,6 @@
1
1
  Description:
2
- The plugin generator creates stubs for a new Redmine plugin.
3
- Plugin is prepared for use in Redmine and Easy Redmine as well.
2
+ The entity generator creates new entity for Redmine plugin.
3
+ New entity is prepared for use in Redmine and Easy Redmine as well.
4
4
 
5
5
  Example:
6
6
  rails g redmine_extensions:entity NameOfNewPlugin Post
@@ -49,6 +49,9 @@ module RedmineExtensions
49
49
  template 'edit.html.erb.erb', "#{plugin_path}/app/views/#{model_name_pluralize_underscored}/edit.html.erb"
50
50
  template 'edit.js.erb.erb', "#{plugin_path}/app/views/#{model_name_pluralize_underscored}/edit.js.erb"
51
51
 
52
+ template 'controller_spec.rb.erb', "#{plugin_path}/test/spec/controllers/#{model_name_pluralize_underscored}_controller_spec.rb"
53
+ template 'factories.rb.erb', "#{plugin_path}/test/factories/#{model_name_underscored}.rb"
54
+
52
55
  if File.exists?("#{plugin_path}/config/locales/en.yml")
53
56
  original_langfile = YAML.load_file("#{plugin_path}/config/locales/en.yml")
54
57
 
@@ -15,7 +15,7 @@
15
15
  </ul>
16
16
  </li>
17
17
  <%% end %>
18
- <li><%%= context_menu_link l(:button_delete), <%= model_name_pluralize_underscored %>_path(ids: @<%= model_name_underscored %>_ids, back_url: @back), method: :delete, data: {confirm: l(:text_are_you_sure)}, class: 'icon icon-del', disabled: !@can[:delete] %></li>
18
+ <li><%%= context_menu_link l(:button_delete), <%= model_name_underscored %>_path(id: @<%= model_name_underscored %>_ids, back_url: @back), method: :delete, data: {confirm: l(:text_are_you_sure)}, class: 'icon icon-del', disabled: !@can[:delete] %></li>
19
19
 
20
20
  <%%= call_hook(:view_<%= model_name_pluralize_underscored %>_context_menu_end, {<%= model_name_underscored %>: @<%= model_name_underscored %>, <%= model_name_pluralize_underscored %>: @<%= model_name_pluralize_underscored %>, can: @can, back: @back, project: @project}) %>
21
21
  </ul>
@@ -0,0 +1,78 @@
1
+ require Rails.root.join('plugins', 'easyproject', 'easy_plugins', 'easy_extensions/test/spec/spec_helper')
2
+
3
+ describe <%= controller_class %>Controller, logged: true do
4
+ let(:<%= model_name_underscored %>) { FactoryGirl.create(:<%= model_name_underscored %>) }
5
+ let(:<%= model_name_pluralize_underscored %>) { FactoryGirl.create_list(:<%= model_name_underscored %>, 5) }
6
+
7
+ before(:each) do
8
+ role = Role.non_member
9
+ role.add_permission! :view_<%= model_name_pluralize_underscored %>
10
+ role.add_permission! :manage_<%= model_name_pluralize_underscored %>
11
+ end
12
+
13
+ render_views
14
+
15
+ it 'index' do
16
+ <%= model_name_pluralize_underscored %>
17
+
18
+ get :index
19
+ expect(response).to have_http_status(:success)
20
+ expect(response).to render_template('<%= model_name_pluralize_underscored %>/index')
21
+ end
22
+
23
+ it 'show' do
24
+ get :index, {id: <%= model_name_underscored %>}
25
+ expect(response).to have_http_status(:success)
26
+ expect(response).to render_template('<%= model_name_pluralize_underscored %>/show')
27
+ end
28
+
29
+ it 'new' do
30
+ get :new
31
+ expect(response).to have_http_status(:success)
32
+ expect(response).to render_template('<%= model_name_pluralize_underscored %>/new')
33
+ end
34
+
35
+ it 'create with invalid' do
36
+ post :create, {<%= model_name_underscored %>: {}}
37
+ expect(response).to have_http_status(:success)
38
+ expect(assigns[:<%= model_name_underscored %>]).to be_a_new(<%= model_name %>)
39
+ expect(response).to render_template('<%= model_name_pluralize_underscored %>/new')
40
+ end
41
+
42
+ it 'create with valid' do
43
+ post :create, {<%= model_name_underscored %>: FactoryGirl.attributes_for(:<%= model_name_underscored %>)}
44
+ expect(response).to have_http_status(:success)
45
+ expect(assigns[:<%= model_name_underscored %>]).not_to be_a_new(<%= model_name %>)
46
+ expect(response).to redirect_to(<%= model_name_underscored %>_path(assigns[:<%= model_name_underscored %>]))
47
+ end
48
+
49
+ it 'edit' do
50
+ get :edit, {id: <%= model_name_underscored %>}
51
+ expect(response).to have_http_status(:success)
52
+ expect(response).to render_template('<%= model_name_pluralize_underscored %>/edit')
53
+ end
54
+
55
+ it 'update with invalid' do
56
+ put :update, {<%= model_name_underscored %>: {url: ''}}
57
+ expect(response).to have_http_status(:success)
58
+ expect(assigns[:<%= model_name_underscored %>].valid?).to be false
59
+ expect(response).to render_template('<%= model_name_pluralize_underscored %>/edit')
60
+ end
61
+
62
+ it 'update with valid' do
63
+ put :update, {<%= model_name_underscored %>: {url: 'http://tlapmazon.easy:123'}}
64
+ expect(response).to have_http_status(:success)
65
+ expect(assigns[:<%= model_name_underscored %>].valid?).to be true
66
+ expect(response).to redirect_to(<%= model_name_underscored %>_path(assigns[:<%= model_name_underscored %>]))
67
+ end
68
+
69
+ it 'destroy' do
70
+ <%= model_name_underscored %>
71
+ <%= model_name_pluralize_underscored %>
72
+
73
+ expect(<%= model_name %>.count).to eq(6)
74
+ expect {delete :destroy, {id: <%= model_name_underscored %>}}.to change(<%= model_name %>, :count).by(-1)
75
+ expect(response).to redirect_to(<%= model_name_pluralize_underscored %>_path)
76
+ expect(response).to redirect_to(<%= model_name_pluralize_underscored %>_path)
77
+ end
78
+ end
@@ -0,0 +1,12 @@
1
+ FactoryGirl.define do
2
+
3
+ factory :<%= model_name_underscored %> do
4
+ <%- string_columns.each_key do |c| -%>
5
+ sequence(:<%= c %>) { | n | "<%= c %>-#{n}"}
6
+ <%- end -%>
7
+ <%= 'association :author, factory: :user' if author? %>
8
+ <%= 'project' if project? %>
9
+
10
+ end
11
+
12
+ end
@@ -3,5 +3,7 @@
3
3
  <%%= context_menu context_menu_<%= model_name_pluralize_underscored %>_path(:project_id => @project) %>
4
4
  <%% content_for :sidebar do %>
5
5
  <%%= render :partial => '<%= model_name_pluralize_underscored %>/sidebar' %>
6
+ <% if Redmine::Plugin.installed?(:easy_extensions) %>
6
7
  <%%= render :partial => 'sidebar/saved_easyqueries_by_type', :locals => {:query_class => @query.class, :project => @project} %>
8
+ <% end %>
7
9
  <%% end %>
@@ -1,5 +1,5 @@
1
1
  Description:
2
- The plugin generator creates stubs for a new Redmine plugin.
2
+ The plugin generator creates stubs for new Redmine plugin.
3
3
  Plugin is prepared for use in Redmine and Easy Redmine as well.
4
4
  You can use --customer flag to generate a special plugin if you can make just a few changes to code. Backup this plugin before Redmine or Easy Redmine upgrade and move back for keep you changes.
5
5
 
@@ -46,7 +46,7 @@ module RedmineExtensions
46
46
  template 'Gemfile.erb', "#{plugin_path}/Gemfile"
47
47
  template 'init.rb.erb', "#{plugin_path}/init.rb"
48
48
  template 'javascript.js', "#{plugin_path}/assets/javascripts/#{plugin_name_underscored}.js"
49
- template 'stylesheet.js', "#{plugin_path}/assets/stylesheets/#{plugin_name_underscored}.css"
49
+ template 'stylesheet.css', "#{plugin_path}/assets/stylesheets/#{plugin_name_underscored}.css"
50
50
  template 'en.yml.erb', "#{plugin_path}/config/locales/en.yml"
51
51
  template 'routes.rb.erb', "#{plugin_path}/config/routes.rb"
52
52
  template 'hooks.rb.erb', "#{plugin_path}/lib/#{plugin_name_underscored}/hooks.rb"
@@ -57,6 +57,9 @@ module RedmineExtensions
57
57
  ActiveSupport.on_load(:active_record) do
58
58
  include RedmineExtensions::RailsPatches::ActiveRecord
59
59
  end
60
+ ActiveSupport.on_load(:action_view) do
61
+ default_form_builder.send(:include, RedmineExtensions::RailsPatches::FormBuilderPatch)
62
+ end
60
63
  end
61
64
 
62
65
  initializer 'redmine_extensions.initialize_easy_plugins', after: :load_config_initializers do
@@ -0,0 +1,12 @@
1
+ module RedmineExtensions
2
+ module RailsPatches
3
+ module FormBuilderPatch
4
+
5
+ # See documentation for autocomplete_field in helper
6
+ def autocomplete_field(method, choices, options={}, html_options={})
7
+ @template.autocomplete_field(@object_name, method, choices, objectify_options(options), html_options)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,28 @@
1
+ require 'action_view/helpers/tags/placeholderable'
2
+
3
+ module RedmineExtensions
4
+ module Tags
5
+ class AutocompleteField < ActionView::Helpers::Tags::Base
6
+ include ActionView::Helpers::Tags::Placeholderable
7
+
8
+ def initialize(object_name, method_name, template_object, choices, options, html_options)
9
+ @choices = block_given? ? template_object.capture { yield || "" } : choices
10
+ @choices = @choices.to_a if @choices.is_a?(Range)
11
+
12
+ options[:multiple] = true unless options.key?(:multiple)
13
+
14
+ @html_options = html_options
15
+
16
+ super(object_name, method_name, template_object, html_options.merge(options))
17
+ end
18
+
19
+ def render
20
+ options = @options.stringify_keys
21
+ options["value"] = options.fetch("value") { value_before_type_cast(object) }
22
+ add_default_name_and_id(options)
23
+ @template_object.autocomplete_field_tag(options.delete('name'), @choices, options.delete('value'), options.symbolize_keys)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module RedmineExtensions
2
- VERSION = '0.1.16'
2
+ VERSION = '0.1.17'
3
3
  end
@@ -8,6 +8,13 @@ RSpec.describe 'autocomplete', type: :feature, js: true do
8
8
  expect(page).to have_css('input#default[type="text"]')
9
9
  expect(page).to have_css('input[type="hidden"][name="default"][value="value1"]', visible: false)
10
10
  end
11
+
12
+ it 'generate autocomplete_field in form_for' do
13
+ visit '/dummy_autocompletes'
14
+ binding.pry
15
+ expect(page).to have_css('input#dummy_entities_autocomplete[type="text"]')
16
+ expect(page).to have_css('input[type="hidden"][name="dummy_entity[array_of_dummies][]"][value="value1"]', visible: false)
17
+ end
11
18
  end
12
19
 
13
20
  end
@@ -15,11 +15,60 @@ module PluginGenerator
15
15
  end
16
16
 
17
17
  def self.generate_autocomplete!
18
+ generate_dummy_entity!
18
19
  generate_autocomplete_controller!
19
20
  generate_autocomplete_routes!
20
21
  generate_autocomplete_view!
21
22
  end
22
23
 
24
+ def self.generate_dummy_entity!
25
+ File.open( Rails.root.join('plugins', 'dummy_plugin', 'db', 'migrate', '20162010160230_create_dummy_entities.rb'), 'w' ) do |file|
26
+ file.write( <<-END_RUBY )
27
+ class CreateDummyEntities < ActiveRecord::Migration
28
+ def change
29
+ create_table :dummy_entities do |t|
30
+ t.string :name
31
+ t.integer :value
32
+ t.references :project, index: true
33
+ t.text :array_of_dummies
34
+ end
35
+ end
36
+ end
37
+ END_RUBY
38
+ end
39
+
40
+ File.open( Rails.root.join('plugins', 'dummy_plugin', 'app', 'models', 'dummy_entity.rb'), 'w' ) do |file|
41
+ file.write( <<-END_RUBY )
42
+ class DummyEntity < ActiveRecord::Base
43
+ include Redmine::SafeAttributes
44
+ safe_attributes 'name',
45
+ 'value',
46
+ 'project_id',
47
+ 'array_of_dummies'
48
+
49
+ attr_protected :id
50
+
51
+ serialize :array_of_dummies, Array
52
+ end
53
+ END_RUBY
54
+ end
55
+
56
+ File.open(Rails.root.join('plugins', 'dummy_plugin', 'app', 'controllers', 'dummy_entities_controller.rb'), 'w') do |file|
57
+ file.write( <<-END_RUBY )
58
+ class DummyEntitiesController < ApplicationController
59
+ def index
60
+ end
61
+
62
+ def create
63
+ @entity = DummyEntity.new
64
+ @entity.safe_attributes = params[:dummy_entity]
65
+ @entity.save
66
+ end
67
+ end
68
+ END_RUBY
69
+ end
70
+ end
71
+
23
72
  def self.generate_autocomplete_controller!
24
73
  File.open(Rails.root.join('plugins', 'dummy_plugin', 'app', 'controllers', 'dummy_autocompletes_controller.rb'), 'w') do |file|
25
74
  file.write( <<-END_RUBY )
@@ -33,7 +82,10 @@ module PluginGenerator
33
82
 
34
83
  def self.generate_autocomplete_routes!
35
84
  File.open(Rails.root.join('plugins', 'dummy_plugin', 'config', 'routes.rb'), 'w') do |file|
36
- file.write("resources :dummy_autocompletes")
85
+ file.write( <<-END_ROUTES )
86
+ resources :dummy_autocompletes
87
+ resources :dummy_entities
88
+ END_ROUTES
37
89
  end
38
90
  end
39
91
 
@@ -41,11 +93,15 @@ module PluginGenerator
41
93
  dir = Rails.root.join('plugins', 'dummy_plugin', 'app', 'views', 'dummy_autocompletes')
42
94
  Dir.mkdir dir
43
95
  File.open(dir.join('index.html.erb'), 'w') do |file|
44
- file.write( <<-END_RUBY )
96
+ file.write( <<-END_ERB )
45
97
  <%= form_tag('/dummy_autocompletes', id: 'autocompletes_form') do %>
46
98
  <%= autocomplete_field_tag('default', ['value1', 'value2'], ['value1']) %>
47
99
  <% end %>
48
- END_RUBY
100
+
101
+ <%= form_for(DummyEntity.new(array_of_dummies: ['value1'])) do |f| %>
102
+ <%= f.autocomplete_field(:array_of_dummies, ['value1', 'value2'], {}, id: 'dummy_entities_autocomplete') %>
103
+ <% end %>
104
+ END_ERB
49
105
  end
50
106
  end
51
107
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redmine_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.16
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Easy Software Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-15 00:00:00.000000000 Z
11
+ date: 2016-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -168,10 +168,12 @@ files:
168
168
  - lib/generators/redmine_extensions/entity/templates/_sidebar.html.erb.erb
169
169
  - lib/generators/redmine_extensions/entity/templates/context_menu.html.erb.erb
170
170
  - lib/generators/redmine_extensions/entity/templates/controller.rb.erb
171
+ - lib/generators/redmine_extensions/entity/templates/controller_spec.rb.erb
171
172
  - lib/generators/redmine_extensions/entity/templates/custom_field.rb.erb
172
173
  - lib/generators/redmine_extensions/entity/templates/edit.html.erb.erb
173
174
  - lib/generators/redmine_extensions/entity/templates/edit.js.erb.erb
174
175
  - lib/generators/redmine_extensions/entity/templates/en.yml.erb
176
+ - lib/generators/redmine_extensions/entity/templates/factories.rb.erb
175
177
  - lib/generators/redmine_extensions/entity/templates/helper.rb.erb
176
178
  - lib/generators/redmine_extensions/entity/templates/hooks.rb.erb
177
179
  - lib/generators/redmine_extensions/entity/templates/index.api.rsb.erb
@@ -228,9 +230,11 @@ files:
228
230
  - lib/redmine_extensions/query_outputs/list_output.rb
229
231
  - lib/redmine_extensions/query_outputs/tiles_output.rb
230
232
  - lib/redmine_extensions/rails_patches/active_record.rb
233
+ - lib/redmine_extensions/rails_patches/form_builder_patch.rb
231
234
  - lib/redmine_extensions/rails_patches/route_set_generator_patch.rb
232
235
  - lib/redmine_extensions/redmine_patches/controllers/application_controller_patch.rb
233
236
  - lib/redmine_extensions/redmine_patches/patches.rb
237
+ - lib/redmine_extensions/tags/autocomplete_field.rb
234
238
  - lib/redmine_extensions/version.rb
235
239
  - lib/tasks/redmine_extensions_tasks.rake
236
240
  - spec/factories/easy_queries.rb