From 24a176d56c0a04c758bf1b66d52da9248fa8e2b0 Mon Sep 17 00:00:00 2001 From: Steve Ross Date: Tue, 24 Feb 2015 15:31:28 -0800 Subject: [PATCH 1/8] Updated to RM 3.6 --- .travis.yml | 2 +- Rakefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1432492..0435949 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ before_install: - (ruby --version) - sudo chown -R travis ~/Library/RubyMotion - mkdir -p ~/Library/RubyMotion/build - - sudo motion update --cache-version=3.3 + - sudo motion update --cache-version=3.6 gemfile: - Gemfile script: diff --git a/Rakefile b/Rakefile index e732d99..9608aea 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -RM_VERSION = "3.3" # Update .travis.yml too +RM_VERSION = "3.6" # Update .travis.yml too unless File.exist?("/Library/RubyMotion#{RM_VERSION}/lib") abort "Couldn't find RubyMotion #{RM_VERSION}. Run `sudo motion update --cache-version=#{RM_VERSION}`." end From e56e80b3a4a09c899ad389ac00d43ee62cc80937 Mon Sep 17 00:00:00 2001 From: Steve Ross Date: Tue, 24 Feb 2015 15:32:19 -0800 Subject: [PATCH 2/8] Added custom search methods --- app/test_screens/table_screen_searchable.rb | 44 ++++++++++++++++++++ lib/ProMotion/table/data/table_data.rb | 27 ++++++++++-- lib/ProMotion/table/extensions/searchable.rb | 2 +- lib/ProMotion/table/table.rb | 4 +- spec/unit/searchable_table_spec.rb | 11 +++++ 5 files changed, 81 insertions(+), 7 deletions(-) diff --git a/app/test_screens/table_screen_searchable.rb b/app/test_screens/table_screen_searchable.rb index 8650886..c7d8046 100644 --- a/app/test_screens/table_screen_searchable.rb +++ b/app/test_screens/table_screen_searchable.rb @@ -94,3 +94,47 @@ class TableScreenSearchable < TestTableScreen end end + +class TableScreenStabbySearchable < TableScreenSearchable + searchable with: -> (cell, search_string) { + result = true + search_string.split(/\s+/).each {|term| + result &&= cell[:properties][:searched_title].downcase.strip.include?(term.downcase.strip) + } + return result + } + + def build_cell(title) + { + title: title, + subtitle: @subtitle.to_s, + action: :update_subtitle, + properties: { + searched_title: "#{title} - stabby" + } + } + end +end + +class TableScreenSymbolSearchable < TableScreenSearchable + searchable with: :custom_search + + def custom_search(cell, search_string) + result = true + search_string.split(/\s+/).each {|term| + result &&= cell[:properties][:searched_title].downcase.strip.include?(term.downcase.strip) + } + return result + end + + def build_cell(title) + { + title: title, + subtitle: @subtitle.to_s, + action: :update_subtitle, + properties: { + searched_title: "#{title} - symbol" + } + } + end +end diff --git a/lib/ProMotion/table/data/table_data.rb b/lib/ProMotion/table/data/table_data.rb index ede4717..d558cf1 100644 --- a/lib/ProMotion/table/data/table_data.rb +++ b/lib/ProMotion/table/data/table_data.rb @@ -2,9 +2,10 @@ module ProMotion class TableData include ProMotion::Table::Utils - attr_accessor :data, :filtered_data, :search_string, :original_search_string, :filtered, :table_view + attr_accessor :data, :filtered_data, :search_string, :original_search_string, :filtered, :table_view, :search_params - def initialize(data, table_view) + def initialize(data, table_view, controller = nil) + @controller = controller self.data = data self.table_view = WeakRef.new(table_view) end @@ -39,14 +40,32 @@ module ProMotion section(to.section)[:cells].insert(to.row, section(from.section)[:cells].delete_at(from.row)) end - def search(search_string) + def default_search(cell, search_string) + cell[:searchable] != false && "#{cell[:title]}\n#{cell[:search_text]}".downcase.strip.include?(search_string.downcase.strip) + end + + def custom_search?(params) + return params[:with] || + params[:find_by] || + params[:search_by] || + params[:filter_by] + end + + def search(search_string, params = {}) start_searching(search_string) self.data.compact.each do |section| new_section = {} new_section[:cells] = section[:cells].map do |cell| - cell[:searchable] != false && "#{cell[:title]}\n#{cell[:search_text]}".downcase.strip.include?(self.search_string) ? cell : nil + if search_method = custom_search?(params) + case search_method + when Proc then search_method.call(cell, search_string) + when Symbol then @controller.send(search_method, cell, search_string) + end + else + self.default_search(cell, search_string) + end ? cell : nil end.compact if new_section[:cells] && new_section[:cells].length > 0 diff --git a/lib/ProMotion/table/extensions/searchable.rb b/lib/ProMotion/table/extensions/searchable.rb index 784a6c3..c7b24fb 100644 --- a/lib/ProMotion/table/extensions/searchable.rb +++ b/lib/ProMotion/table/extensions/searchable.rb @@ -42,7 +42,7 @@ module ProMotion ######### iOS methods, headless camel case ####### def searchDisplayController(controller, shouldReloadTableForSearchString:search_string) - self.promotion_table_data.search(search_string) + self.promotion_table_data.search(search_string, self.class.get_searchable_params) true end diff --git a/lib/ProMotion/table/table.rb b/lib/ProMotion/table/table.rb index 67ca434..f1ad6c7 100644 --- a/lib/ProMotion/table/table.rb +++ b/lib/ProMotion/table/table.rb @@ -27,7 +27,7 @@ module ProMotion end def promotion_table_data - @promotion_table_data ||= TableData.new(table_data, table_view) + @promotion_table_data ||= TableData.new(table_data, table_view, self) end def set_up_header_view @@ -148,7 +148,7 @@ module ProMotion args = { index_paths: args } unless args.is_a?(Hash) self.update_table_view_data(self.table_data, args) - self.promotion_table_data.search(search_string) if searching? + self.promotion_table_data.search(search_string, self.class.get_searchable_params) if searching? end def toggle_edit_mode(animated = true) diff --git a/spec/unit/searchable_table_spec.rb b/spec/unit/searchable_table_spec.rb index 8ee6183..ae2f8c0 100644 --- a/spec/unit/searchable_table_spec.rb +++ b/spec/unit/searchable_table_spec.rb @@ -58,4 +58,15 @@ describe "Searchable table spec" do controller.searchDisplayController(controller, didLoadSearchResultsTableView: tableView) end + it "should allow searching for all the 'New' states using a custom search proc" do + controller = TableScreenStabbySearchable.new + controller.searchDisplayController(controller, shouldReloadTableForSearchString:"New Stabby") + controller.tableView(controller.tableView, numberOfRowsInSection:0).should == 4 + end + + it "should allow searching for all the 'New' states using a symbol as a search proc" do + controller = TableScreenSymbolSearchable.new + controller.searchDisplayController(controller, shouldReloadTableForSearchString:"New Symbol") + controller.tableView(controller.tableView, numberOfRowsInSection:0).should == 4 + end end From 2b7e8d6617836b13337b6586e933588fb8df1006 Mon Sep 17 00:00:00 2001 From: Steve Ross Date: Thu, 26 Feb 2015 10:52:36 -0800 Subject: [PATCH 3/8] Changes custom table view search to pass action to table data. - Instead of passing the controller into the table data, pass a proc bound in the context of the controller. Thanks @jasonholmgren. - Remove params as second argument to search -- the params will have been resolved when the table_data is allocated. - Which one wins: If the user passes more than one proc or symbol using the params hash, the behavior is undefined, but the last one evaluated is the one that becomes the custom action. Because hashes are not necessarily ordered sets, order is meaningless in this context. --- app/test_screens/table_screen_searchable.rb | 5 ++--- lib/ProMotion/table/data/table_data.rb | 20 +++++--------------- lib/ProMotion/table/extensions/searchable.rb | 2 +- lib/ProMotion/table/table.rb | 18 ++++++++++++++++-- 4 files changed, 24 insertions(+), 21 deletions(-) diff --git a/app/test_screens/table_screen_searchable.rb b/app/test_screens/table_screen_searchable.rb index c7d8046..80d2b87 100644 --- a/app/test_screens/table_screen_searchable.rb +++ b/app/test_screens/table_screen_searchable.rb @@ -121,10 +121,9 @@ class TableScreenSymbolSearchable < TableScreenSearchable def custom_search(cell, search_string) result = true - search_string.split(/\s+/).each {|term| - result &&= cell[:properties][:searched_title].downcase.strip.include?(term.downcase.strip) + search_string.split(/\s+/).all? {|term| + cell[:properties][:searched_title].downcase.strip.include?(term.downcase.strip) } - return result end def build_cell(title) diff --git a/lib/ProMotion/table/data/table_data.rb b/lib/ProMotion/table/data/table_data.rb index d558cf1..578a4da 100644 --- a/lib/ProMotion/table/data/table_data.rb +++ b/lib/ProMotion/table/data/table_data.rb @@ -4,8 +4,8 @@ module ProMotion attr_accessor :data, :filtered_data, :search_string, :original_search_string, :filtered, :table_view, :search_params - def initialize(data, table_view, controller = nil) - @controller = controller + def initialize(data, table_view, search_action = nil) + @search_action ||= search_action self.data = data self.table_view = WeakRef.new(table_view) end @@ -44,25 +44,15 @@ module ProMotion cell[:searchable] != false && "#{cell[:title]}\n#{cell[:search_text]}".downcase.strip.include?(search_string.downcase.strip) end - def custom_search?(params) - return params[:with] || - params[:find_by] || - params[:search_by] || - params[:filter_by] - end - - def search(search_string, params = {}) + def search(search_string) start_searching(search_string) self.data.compact.each do |section| new_section = {} new_section[:cells] = section[:cells].map do |cell| - if search_method = custom_search?(params) - case search_method - when Proc then search_method.call(cell, search_string) - when Symbol then @controller.send(search_method, cell, search_string) - end + if @search_action + @search_action.call(cell, search_string) else self.default_search(cell, search_string) end ? cell : nil diff --git a/lib/ProMotion/table/extensions/searchable.rb b/lib/ProMotion/table/extensions/searchable.rb index c7b24fb..784a6c3 100644 --- a/lib/ProMotion/table/extensions/searchable.rb +++ b/lib/ProMotion/table/extensions/searchable.rb @@ -42,7 +42,7 @@ module ProMotion ######### iOS methods, headless camel case ####### def searchDisplayController(controller, shouldReloadTableForSearchString:search_string) - self.promotion_table_data.search(search_string, self.class.get_searchable_params) + self.promotion_table_data.search(search_string) true end diff --git a/lib/ProMotion/table/table.rb b/lib/ProMotion/table/table.rb index f1ad6c7..eff1f30 100644 --- a/lib/ProMotion/table/table.rb +++ b/lib/ProMotion/table/table.rb @@ -27,7 +27,7 @@ module ProMotion end def promotion_table_data - @promotion_table_data ||= TableData.new(table_data, table_view, self) + @promotion_table_data ||= TableData.new(table_data, table_view, setup_search_method) end def set_up_header_view @@ -47,6 +47,20 @@ module ProMotion end end + def setup_search_method + params = self.class.get_searchable_params + if params.nil? + return nil + else + @search_method || begin + params = self.class.get_searchable_params + @search_action = params[:with] || params[:find_by] || params[:search_by] || params[:filter_by] + @search_action = method(@search_action) if @search_action.is_a?(Symbol) || @search_action.is_a?(String) + @search_action + end + end + end + def set_up_refreshable if self.class.respond_to?(:get_refreshable) && self.class.get_refreshable if defined?(UIRefreshControl) @@ -148,7 +162,7 @@ module ProMotion args = { index_paths: args } unless args.is_a?(Hash) self.update_table_view_data(self.table_data, args) - self.promotion_table_data.search(search_string, self.class.get_searchable_params) if searching? + self.promotion_table_data.search(search_string) if searching? end def toggle_edit_mode(animated = true) From 66b4bee667b095481477900359d6d790e5c08c9a Mon Sep 17 00:00:00 2001 From: Steve Ross Date: Sat, 28 Feb 2015 12:03:42 -0800 Subject: [PATCH 4/8] Added specs for actual data returned from search --- spec/unit/searchable_table_spec.rb | 52 ++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/spec/unit/searchable_table_spec.rb b/spec/unit/searchable_table_spec.rb index ae2f8c0..491d4e8 100644 --- a/spec/unit/searchable_table_spec.rb +++ b/spec/unit/searchable_table_spec.rb @@ -58,15 +58,49 @@ describe "Searchable table spec" do controller.searchDisplayController(controller, didLoadSearchResultsTableView: tableView) end - it "should allow searching for all the 'New' states using a custom search proc" do - controller = TableScreenStabbySearchable.new - controller.searchDisplayController(controller, shouldReloadTableForSearchString:"New Stabby") - controller.tableView(controller.tableView, numberOfRowsInSection:0).should == 4 - end + describe "custom search" do + before do + @stabby_controller = TableScreenStabbySearchable.new + @proc_controller = TableScreenSymbolSearchable.new + end + + after do + @stabby_controller = nil + @proc_controller = nil + end + + it "should allow searching for all the 'New' states using a custom search proc" do + @stabby_controller.searchDisplayController(@stabby_controller, shouldReloadTableForSearchString:"New Stabby") + @stabby_controller.tableView(@stabby_controller.tableView, numberOfRowsInSection:0).should == 4 + rows = @stabby_controller.promotion_table_data.search("New stabby") + rows.first[:cells].length.should == 4 + rows.first[:cells].each do |row| + # Starts with "New" and ends with "stabby" + row[:properties][:searched_title].should.match(/^New(.+)?stabby$/) + end + end + + it "should allow searching for all the 'New' states using a symbol as a search proc" do + @proc_controller.searchDisplayController(@proc_controller, shouldReloadTableForSearchString:"New Symbol") + cell_count = @proc_controller.tableView(@proc_controller.tableView, numberOfRowsInSection:0) + cell_count.should == 4 + rows = @proc_controller.promotion_table_data.search("New Symbol") + rows.first[:cells].length.should == 4 + rows.first[:cells].each do |row| + # Starts with "New" and ends with "symbol" + row[:properties][:searched_title].should.match(/^New(.+)?symbol$/) + end + end + + it "custom searches empty with stabby proc if there is no match" do + @stabby_controller.searchDisplayController(@stabby_controller, shouldReloadTableForSearchString:"Totally Bogus") + @stabby_controller.tableView(@stabby_controller.tableView, numberOfRowsInSection:0).should == 0 + end + + it "custom searches empty with symbol for proc if there is no match" do + @proc_controller.searchDisplayController(@proc_controller, shouldReloadTableForSearchString:"Totally Bogus") + @proc_controller.tableView(@proc_controller.tableView, numberOfRowsInSection:0).should == 0 + end - it "should allow searching for all the 'New' states using a symbol as a search proc" do - controller = TableScreenSymbolSearchable.new - controller.searchDisplayController(controller, shouldReloadTableForSearchString:"New Symbol") - controller.tableView(controller.tableView, numberOfRowsInSection:0).should == 4 end end From 9be0526524a93185d4fe2cca44ae40f9410b63a1 Mon Sep 17 00:00:00 2001 From: Steve Ross Date: Tue, 3 Mar 2015 19:38:45 -0800 Subject: [PATCH 5/8] Updated doc -- added custom search method --- docs/API Reference - ProMotion TableScreen.md | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/API Reference - ProMotion TableScreen.md b/docs/API Reference - ProMotion TableScreen.md index dbfdad8..21bf478 100644 --- a/docs/API Reference - ProMotion TableScreen.md +++ b/docs/API Reference - ProMotion TableScreen.md @@ -278,7 +278,7 @@ This is useful for information that needs to only be at the very bottom of a tab ### Class Methods -#### searchable(placeholder: "placeholder text") +#### searchable(placeholder: "placeholder text", with: -> (cell, search_string){}) Class method to make the current table searchable. @@ -288,6 +288,31 @@ class MyTableScreen < PM::TableScreen end ``` +Without a `with:` specifier, search is performed on the `title` attribute, and +the `search_text` attribute, if present. If you want to create a custom search +method, specify it as the value of the `with` key (`find_by`, `search_by` and `filter_by` +are aliases). E.g.: + +```ruby +class MyTableScreen < PM::TableScreen + searchable placeholder: "Search This Table", with: -> (cell, search_string){ + cell[:properties][:some_obscure_attribute].include? search_string + } +end +``` + +or if you want to create a version that is less resistant to refactoring: + +```ruby +class MyTableScreen < PM::TableScreen + searchable placeholder: "Search This Table", with: :custom_search_method + + def custom_search_method(cell, search_string) + cell[:properties][:some_obscure_attribute].include? search_string + end +end +``` + ![Searchable Image](http://clrsight.co/jh/Screen_Shot_2014-06-21_at_9.01.09_PM.png) To initially hide the search bar behind the nav bar until the user scrolls it into view, use `hide_initially`. From f374ff1d9be33742475b2f46ceb19d829d1126ab Mon Sep 17 00:00:00 2001 From: Steve Ross Date: Tue, 3 Mar 2015 19:40:38 -0800 Subject: [PATCH 6/8] Updated doc -- tidied up search method code --- docs/API Reference - ProMotion TableScreen.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/API Reference - ProMotion TableScreen.md b/docs/API Reference - ProMotion TableScreen.md index 21bf478..724f6e1 100644 --- a/docs/API Reference - ProMotion TableScreen.md +++ b/docs/API Reference - ProMotion TableScreen.md @@ -296,7 +296,7 @@ are aliases). E.g.: ```ruby class MyTableScreen < PM::TableScreen searchable placeholder: "Search This Table", with: -> (cell, search_string){ - cell[:properties][:some_obscure_attribute].include? search_string + cell[:properties][:some_obscure_attribute].strip.downcase.include? search_string.strip.downcase } end ``` @@ -308,7 +308,7 @@ class MyTableScreen < PM::TableScreen searchable placeholder: "Search This Table", with: :custom_search_method def custom_search_method(cell, search_string) - cell[:properties][:some_obscure_attribute].include? search_string + cell[:properties][:some_obscure_attribute].strip.downcase.include? search_string.strip.downcase end end ``` From cde4b5e54961a9074de06282588ff7b680f7ba90 Mon Sep 17 00:00:00 2001 From: Jamon Holmgren Date: Thu, 2 Apr 2015 16:34:29 -0700 Subject: [PATCH 7/8] Add back in on_load for table cells --- lib/ProMotion/table/table.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ProMotion/table/table.rb b/lib/ProMotion/table/table.rb index b7230a6..e56f18b 100644 --- a/lib/ProMotion/table/table.rb +++ b/lib/ProMotion/table/table.rb @@ -141,9 +141,9 @@ module ProMotion new_cell.extend(PM::TableViewCellModule) unless new_cell.is_a?(PM::TableViewCellModule) new_cell.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleLeftMargin|UIViewAutoresizingFlexibleRightMargin new_cell.clipsToBounds = true # fix for changed default in 7.1 + new_cell.send(:on_load) if new_cell.respond_to?(:on_load) new_cell end - table_cell.setup(data_cell, self) if table_cell.respond_to?(:setup) table_cell.send(:on_reuse) if !new_cell && table_cell.respond_to?(:on_reuse) table_cell From 8797ec31f7dc00d349309adf5d3101cc7b6855a9 Mon Sep 17 00:00:00 2001 From: Jamon Holmgren Date: Thu, 2 Apr 2015 16:46:44 -0700 Subject: [PATCH 8/8] Fix issue #651 --- lib/ProMotion/cocoatouch/tab_bar_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ProMotion/cocoatouch/tab_bar_controller.rb b/lib/ProMotion/cocoatouch/tab_bar_controller.rb index b0733f9..42e9c41 100644 --- a/lib/ProMotion/cocoatouch/tab_bar_controller.rb +++ b/lib/ProMotion/cocoatouch/tab_bar_controller.rb @@ -63,7 +63,7 @@ module ProMotion private def on_tab_selected_try(vc) - if pm_tab_delegate && pm_tab_delegate.respond_to?("on_tab_selected:") + if pm_tab_delegate && pm_tab_delegate.respond_to?(:weakref_alive?) && pm_tab_delegate.weakref_alive? && pm_tab_delegate.respond_to?("on_tab_selected:") pm_tab_delegate.send(:on_tab_selected, vc) end end