impressionistでcounter_cacheがうまくupdateされない

PVの測定にimpressionistというgemを使っているのですが、このimpressionistにはRails3くらいに実装されたcounter_cacheの機能を簡単に盛り込めるようにしてくれるオプションをis_impressionableメソッドにつけてくれてます。

ですがこいつがうまくupdateされなくて困りました。RailsRubyのversionは以下です。

そして色々原因を探っていて以下のissueにたどり着きました。

https://github.com/charlotte-ruby/impressionist/issues/158

これをもとに

is_impressionable counter_cache: true, unique: :all

としたところ、無事動きました...!!!

これだけのためにかなり時間を使ってしまった、つらい。

HTMLのタグを除去して文字数だけをカウントしたい

Rubyで例えばブログみたいなシステムを想定して「DBの中身はHTMLなんだけど、タグ部分は除いて純粋な本文の文字数のみをカウントしたい」ってとき。想像以上に簡単にできちゃうのでメモ。

Nokogiriを使えば以下のようにすればok。

Nokogiri::HTML(source).xpath("//text()").to_s.length

まとめ

Nokogiriありがとう :pray:

Reactでタブ機能を実装

以下を参考にしてReactでタブを実装します。

https://toddmotto.com/creating-a-tabs-component-with-react/

Componentのデザイン

以下のようなComponentを作成することを目指します。

<Tabs>
  <Pane label="Tab 1">
    <div>This is my tab 1 contents!</div>
  </Pane>
  <Pane label="Tab 2">
    <div>This is my tab 2 contents!</div>
  </Pane>
  <Pane label="Tab 3">
    <div>This is my tab 3 contents!</div>
  </Pane>
</Tabs>

実装

タブ

タブの部分のComponentを作成します。

const Tabs = React.createClass({
  displayName: "Tabs",
  getDefaultProps() {
    return {
      selected: 0
    };
  },
  getInitialState() {
    return {
      selected: this.props.selected
    };
  },
  handleClick(index, event) {
    event.preventDefault();
    this.setState({
      selected: index
    });
  },
  _renderContent() {
    return (
      <div className="tabs__content">
        {this.props.children[this.state.selected]}
      </div>
    );
  },
  _renderTitles() {
    function labels(child, index) {
      let activeClass = (this.state.selected == index ? 'active' : '');
      return (
        <li key={index}>
          <a href="#"
            onClick={this.handleClick.bind(this, index)}
            className={activeClass}
            onClick={this.handleClick.bind(this, index)}>
            {child.props.label}
          </a>
        </li>
      );
    }
    return (
      <ul className="tabs__labels">
        {this.props.children.map(labels.bind(this))}
      </ul>
    );
  },
  render() {
    return (
      <div className="tabs">
        {this._renderTitles()}
        {this._renderContent()}
      </div>
    );
  }
});

Pane

続いてPaneの実装。

const Pane = React.createClass({
  displayName: "Pane",
  propTypes: {
    label: React.PropTypes.string.isRequired,
    children: React.PropTypes.element.isRequired
  },
  render() {
    return (
      <div>
        {this.props.children}
      </div>
    );
  }
})

本体

最後に、これらを組み合わせる本体を実装します。

const app = React.createClass({
  render() {
    return (
      <div className="app">
        <Tabs selected={0}>
          <Pane label="Tab 1">
            <div>This is my tab 1 contents!</div>
          </Pane>
          <Pane label="Tab 2">
            <div>This is my tab 2 contents!</div>
          </Pane>
          <Pane label="Tab 3">
            <div>This is my tab 3 contents!</div>
          </Pane>
        </Tabs>
      </div>
    );
  }
});

おわり

これでタブの実装は完了です。わーい